src/video/x11/SDL_x11video.c
author Alex Baines <alex@abaines.me.uk>
Fri, 28 Oct 2016 01:28:58 +0100
changeset 10562 b48d8a98e261
parent 10518 fe8023cff95a
child 10737 3406a0f8b041
permissions -rw-r--r--
Fix double events / no repeat flag on key events when built withoutibus/fcitx

Uses XkbSetDetectableKeyRepeat, and falls back to forcing @im=none if it's not
supported.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include <unistd.h> /* For getpid() and readlink() */
    26 
    27 #include "SDL_video.h"
    28 #include "SDL_mouse.h"
    29 #include "../SDL_sysvideo.h"
    30 #include "../SDL_pixels_c.h"
    31 
    32 #include "SDL_x11video.h"
    33 #include "SDL_x11framebuffer.h"
    34 #include "SDL_x11shape.h"
    35 #include "SDL_x11touch.h"
    36 #include "SDL_x11xinput2.h"
    37 
    38 #if SDL_VIDEO_OPENGL_EGL
    39 #include "SDL_x11opengles.h"
    40 #endif
    41 
    42 /* Initialization/Query functions */
    43 static int X11_VideoInit(_THIS);
    44 static void X11_VideoQuit(_THIS);
    45 
    46 /* Find out what class name we should use */
    47 static char *
    48 get_classname()
    49 {
    50     char *spot;
    51 #if defined(__LINUX__) || defined(__FREEBSD__)
    52     char procfile[1024];
    53     char linkfile[1024];
    54     int linksize;
    55 #endif
    56 
    57     /* First allow environment variable override */
    58     spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
    59     if (spot) {
    60         return SDL_strdup(spot);
    61     }
    62 
    63     /* Next look at the application's executable name */
    64 #if defined(__LINUX__) || defined(__FREEBSD__)
    65 #if defined(__LINUX__)
    66     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
    67 #elif defined(__FREEBSD__)
    68     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
    69                  getpid());
    70 #else
    71 #error Where can we find the executable name?
    72 #endif
    73     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
    74     if (linksize > 0) {
    75         linkfile[linksize] = '\0';
    76         spot = SDL_strrchr(linkfile, '/');
    77         if (spot) {
    78             return SDL_strdup(spot + 1);
    79         } else {
    80             return SDL_strdup(linkfile);
    81         }
    82     }
    83 #endif /* __LINUX__ || __FREEBSD__ */
    84 
    85     /* Finally use the default we've used forever */
    86     return SDL_strdup("SDL_App");
    87 }
    88 
    89 /* X11 driver bootstrap functions */
    90 
    91 static int
    92 X11_Available(void)
    93 {
    94     Display *display = NULL;
    95     if (SDL_X11_LoadSymbols()) {
    96         display = X11_XOpenDisplay(NULL);
    97         if (display != NULL) {
    98             X11_XCloseDisplay(display);
    99         }
   100         SDL_X11_UnloadSymbols();
   101     }
   102     return (display != NULL);
   103 }
   104 
   105 static void
   106 X11_DeleteDevice(SDL_VideoDevice * device)
   107 {
   108     SDL_VideoData *data = (SDL_VideoData *) device->driverdata;
   109     if (data->display) {
   110         X11_XCloseDisplay(data->display);
   111     }
   112     SDL_free(data->windowlist);
   113     SDL_free(device->driverdata);
   114     SDL_free(device);
   115 
   116     SDL_X11_UnloadSymbols();
   117 }
   118 
   119 /* An error handler to reset the vidmode and then call the default handler. */
   120 static SDL_bool safety_net_triggered = SDL_FALSE;
   121 static int (*orig_x11_errhandler) (Display *, XErrorEvent *) = NULL;
   122 static int
   123 X11_SafetyNetErrHandler(Display * d, XErrorEvent * e)
   124 {
   125     SDL_VideoDevice *device = NULL;
   126     /* if we trigger an error in our error handler, don't try again. */
   127     if (!safety_net_triggered) {
   128         safety_net_triggered = SDL_TRUE;
   129         device = SDL_GetVideoDevice();
   130         if (device != NULL) {
   131             int i;
   132             for (i = 0; i < device->num_displays; i++) {
   133                 SDL_VideoDisplay *display = &device->displays[i];
   134                 if (SDL_memcmp(&display->current_mode, &display->desktop_mode,
   135                                sizeof (SDL_DisplayMode)) != 0) {
   136                     X11_SetDisplayMode(device, display, &display->desktop_mode);
   137                 }
   138             }
   139         }
   140     }
   141 
   142     if (orig_x11_errhandler != NULL) {
   143         return orig_x11_errhandler(d, e);  /* probably terminate. */
   144     }
   145 
   146     return 0;
   147 }
   148 
   149 static SDL_VideoDevice *
   150 X11_CreateDevice(int devindex)
   151 {
   152     SDL_VideoDevice *device;
   153     SDL_VideoData *data;
   154     const char *display = NULL; /* Use the DISPLAY environment variable */
   155 
   156     if (!SDL_X11_LoadSymbols()) {
   157         return NULL;
   158     }
   159 
   160     /* Need for threading gl calls. This is also required for the proprietary
   161         nVidia driver to be threaded. */
   162     X11_XInitThreads();
   163 
   164     /* Initialize all variables that we clean on shutdown */
   165     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   166     if (!device) {
   167         SDL_OutOfMemory();
   168         return NULL;
   169     }
   170     data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   171     if (!data) {
   172         SDL_free(device);
   173         SDL_OutOfMemory();
   174         return NULL;
   175     }
   176     device->driverdata = data;
   177 
   178     data->global_mouse_changed = SDL_TRUE;
   179 
   180     /* FIXME: Do we need this?
   181        if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) ||
   182        (SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) {
   183        local_X11 = 1;
   184        } else {
   185        local_X11 = 0;
   186        }
   187      */
   188     data->display = X11_XOpenDisplay(display);
   189 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
   190     /* On Tru64 if linking without -lX11, it fails and you get following message.
   191      * Xlib: connection to ":0.0" refused by server
   192      * Xlib: XDM authorization key matches an existing client!
   193      *
   194      * It succeeds if retrying 1 second later
   195      * or if running xhost +localhost on shell.
   196      */
   197     if (data->display == NULL) {
   198         SDL_Delay(1000);
   199         data->display = X11_XOpenDisplay(display);
   200     }
   201 #endif
   202     if (data->display == NULL) {
   203         SDL_free(device->driverdata);
   204         SDL_free(device);
   205         SDL_SetError("Couldn't open X11 display");
   206         return NULL;
   207     }
   208 #ifdef X11_DEBUG
   209     X11_XSynchronize(data->display, True);
   210 #endif
   211 
   212     /* Hook up an X11 error handler to recover the desktop resolution. */
   213     safety_net_triggered = SDL_FALSE;
   214     orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler);
   215 
   216     /* Set the function pointers */
   217     device->VideoInit = X11_VideoInit;
   218     device->VideoQuit = X11_VideoQuit;
   219     device->GetDisplayModes = X11_GetDisplayModes;
   220     device->GetDisplayBounds = X11_GetDisplayBounds;
   221     device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds;
   222     device->GetDisplayDPI = X11_GetDisplayDPI;
   223     device->SetDisplayMode = X11_SetDisplayMode;
   224     device->SuspendScreenSaver = X11_SuspendScreenSaver;
   225     device->PumpEvents = X11_PumpEvents;
   226 
   227     device->CreateWindow = X11_CreateWindow;
   228     device->CreateWindowFrom = X11_CreateWindowFrom;
   229     device->SetWindowTitle = X11_SetWindowTitle;
   230     device->SetWindowIcon = X11_SetWindowIcon;
   231     device->SetWindowPosition = X11_SetWindowPosition;
   232     device->SetWindowSize = X11_SetWindowSize;
   233     device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
   234     device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
   235     device->GetWindowBordersSize = X11_GetWindowBordersSize;
   236     device->SetWindowOpacity = X11_SetWindowOpacity;
   237     device->SetWindowModalFor = X11_SetWindowModalFor;
   238     device->SetWindowInputFocus = X11_SetWindowInputFocus;
   239     device->ShowWindow = X11_ShowWindow;
   240     device->HideWindow = X11_HideWindow;
   241     device->RaiseWindow = X11_RaiseWindow;
   242     device->MaximizeWindow = X11_MaximizeWindow;
   243     device->MinimizeWindow = X11_MinimizeWindow;
   244     device->RestoreWindow = X11_RestoreWindow;
   245     device->SetWindowBordered = X11_SetWindowBordered;
   246     device->SetWindowResizable = X11_SetWindowResizable;
   247     device->SetWindowFullscreen = X11_SetWindowFullscreen;
   248     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
   249     device->SetWindowGrab = X11_SetWindowGrab;
   250     device->DestroyWindow = X11_DestroyWindow;
   251     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
   252     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
   253     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
   254     device->GetWindowWMInfo = X11_GetWindowWMInfo;
   255     device->SetWindowHitTest = X11_SetWindowHitTest;
   256 
   257     device->shape_driver.CreateShaper = X11_CreateShaper;
   258     device->shape_driver.SetWindowShape = X11_SetWindowShape;
   259     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
   260 
   261 #if SDL_VIDEO_OPENGL_GLX
   262     device->GL_LoadLibrary = X11_GL_LoadLibrary;
   263     device->GL_GetProcAddress = X11_GL_GetProcAddress;
   264     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
   265     device->GL_CreateContext = X11_GL_CreateContext;
   266     device->GL_MakeCurrent = X11_GL_MakeCurrent;
   267     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
   268     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
   269     device->GL_SwapWindow = X11_GL_SwapWindow;
   270     device->GL_DeleteContext = X11_GL_DeleteContext;
   271 #elif SDL_VIDEO_OPENGL_EGL
   272     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
   273     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
   274     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
   275     device->GL_CreateContext = X11_GLES_CreateContext;
   276     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
   277     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
   278     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
   279     device->GL_SwapWindow = X11_GLES_SwapWindow;
   280     device->GL_DeleteContext = X11_GLES_DeleteContext;
   281 #endif
   282 
   283     device->SetClipboardText = X11_SetClipboardText;
   284     device->GetClipboardText = X11_GetClipboardText;
   285     device->HasClipboardText = X11_HasClipboardText;
   286     device->StartTextInput = X11_StartTextInput;
   287     device->StopTextInput = X11_StopTextInput;
   288     device->SetTextInputRect = X11_SetTextInputRect;
   289 
   290     device->free = X11_DeleteDevice;
   291 
   292     return device;
   293 }
   294 
   295 VideoBootStrap X11_bootstrap = {
   296     "x11", "SDL X11 video driver",
   297     X11_Available, X11_CreateDevice
   298 };
   299 
   300 static int (*handler) (Display *, XErrorEvent *) = NULL;
   301 static int
   302 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
   303 {
   304     if (e->error_code == BadWindow) {
   305         return (0);
   306     } else {
   307         return (handler(d, e));
   308     }
   309 }
   310 
   311 static void
   312 X11_CheckWindowManager(_THIS)
   313 {
   314     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   315     Display *display = data->display;
   316     Atom _NET_SUPPORTING_WM_CHECK;
   317     int status, real_format;
   318     Atom real_type;
   319     unsigned long items_read = 0, items_left = 0;
   320     unsigned char *propdata = NULL;
   321     Window wm_window = 0;
   322 #ifdef DEBUG_WINDOW_MANAGER
   323     char *wm_name;
   324 #endif
   325 
   326     /* Set up a handler to gracefully catch errors */
   327     X11_XSync(display, False);
   328     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
   329 
   330     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
   331     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   332     if (status == Success) {
   333         if (items_read) {
   334             wm_window = ((Window*)propdata)[0];
   335         }
   336         if (propdata) {
   337             X11_XFree(propdata);
   338             propdata = NULL;
   339         }
   340     }
   341 
   342     if (wm_window) {
   343         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   344         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
   345             wm_window = None;
   346         }
   347         if (status == Success && propdata) {
   348             X11_XFree(propdata);
   349             propdata = NULL;
   350         }
   351     }
   352 
   353     /* Reset the error handler, we're done checking */
   354     X11_XSync(display, False);
   355     X11_XSetErrorHandler(handler);
   356 
   357     if (!wm_window) {
   358 #ifdef DEBUG_WINDOW_MANAGER
   359         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
   360 #endif
   361         return;
   362     }
   363     data->net_wm = SDL_TRUE;
   364 
   365 #ifdef DEBUG_WINDOW_MANAGER
   366     wm_name = X11_GetWindowTitle(_this, wm_window);
   367     printf("Window manager: %s\n", wm_name);
   368     SDL_free(wm_name);
   369 #endif
   370 }
   371 
   372 
   373 int
   374 X11_VideoInit(_THIS)
   375 {
   376     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   377 
   378     /* Get the window class name, usually the name of the application */
   379     data->classname = get_classname();
   380 
   381     /* Get the process PID to be associated to the window */
   382     data->pid = getpid();
   383 
   384     /* I have no idea how random this actually is, or has to be. */
   385     data->window_group = (XID) (((size_t) data->pid) ^ ((size_t) _this));
   386 
   387     /* Look up some useful Atoms */
   388 #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False)
   389     GET_ATOM(WM_PROTOCOLS);
   390     GET_ATOM(WM_DELETE_WINDOW);
   391     GET_ATOM(WM_TAKE_FOCUS);
   392     GET_ATOM(_NET_WM_STATE);
   393     GET_ATOM(_NET_WM_STATE_HIDDEN);
   394     GET_ATOM(_NET_WM_STATE_FOCUSED);
   395     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
   396     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
   397     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
   398     GET_ATOM(_NET_WM_STATE_ABOVE);
   399     GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
   400     GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
   401     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
   402     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
   403     GET_ATOM(_NET_WM_NAME);
   404     GET_ATOM(_NET_WM_ICON_NAME);
   405     GET_ATOM(_NET_WM_ICON);
   406     GET_ATOM(_NET_WM_PING);
   407     GET_ATOM(_NET_WM_WINDOW_OPACITY);
   408     GET_ATOM(_NET_WM_USER_TIME);
   409     GET_ATOM(_NET_ACTIVE_WINDOW);
   410     GET_ATOM(_NET_FRAME_EXTENTS);
   411     GET_ATOM(UTF8_STRING);
   412     GET_ATOM(PRIMARY);
   413     GET_ATOM(XdndEnter);
   414     GET_ATOM(XdndPosition);
   415     GET_ATOM(XdndStatus);
   416     GET_ATOM(XdndTypeList);
   417     GET_ATOM(XdndActionCopy);
   418     GET_ATOM(XdndDrop);
   419     GET_ATOM(XdndFinished);
   420     GET_ATOM(XdndSelection);
   421     GET_ATOM(XKLAVIER_STATE);
   422 
   423     /* Detect the window manager */
   424     X11_CheckWindowManager(_this);
   425 
   426     if (X11_InitModes(_this) < 0) {
   427         return -1;
   428     }
   429 
   430     X11_InitXinput2(_this);
   431 
   432     if (X11_InitKeyboard(_this) != 0) {
   433         return -1;
   434     }
   435     X11_InitMouse(_this);
   436 
   437     X11_InitTouch(_this);
   438 
   439 #if SDL_USE_LIBDBUS
   440     SDL_DBus_Init();
   441 #endif
   442 
   443     return 0;
   444 }
   445 
   446 void
   447 X11_VideoQuit(_THIS)
   448 {
   449     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   450 
   451     SDL_free(data->classname);
   452 #ifdef X_HAVE_UTF8_STRING
   453     if (data->im) {
   454         X11_XCloseIM(data->im);
   455     }
   456 #endif
   457 
   458     X11_QuitModes(_this);
   459     X11_QuitKeyboard(_this);
   460     X11_QuitMouse(_this);
   461     X11_QuitTouch(_this);
   462 
   463 #if SDL_USE_LIBDBUS
   464     SDL_DBus_Quit();
   465 #endif
   466 }
   467 
   468 SDL_bool
   469 X11_UseDirectColorVisuals(void)
   470 {
   471     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
   472 }
   473 
   474 #endif /* SDL_VIDEO_DRIVER_X11 */
   475 
   476 /* vim: set ts=4 sw=4 expandtab: */