src/video/x11/SDL_x11video.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 04 Jan 2016 23:52:40 -0500
changeset 10019 36f7e8084508
parent 10018 3c1384edf9fa
child 10021 3beca914a2ad
permissions -rw-r--r--
Added SDL_GetDisplayUsableBounds().
     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->ShowWindow = X11_ShowWindow;
   236     device->HideWindow = X11_HideWindow;
   237     device->RaiseWindow = X11_RaiseWindow;
   238     device->MaximizeWindow = X11_MaximizeWindow;
   239     device->MinimizeWindow = X11_MinimizeWindow;
   240     device->RestoreWindow = X11_RestoreWindow;
   241     device->SetWindowBordered = X11_SetWindowBordered;
   242     device->SetWindowFullscreen = X11_SetWindowFullscreen;
   243     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
   244     device->SetWindowGrab = X11_SetWindowGrab;
   245     device->DestroyWindow = X11_DestroyWindow;
   246     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
   247     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
   248     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
   249     device->GetWindowWMInfo = X11_GetWindowWMInfo;
   250     device->SetWindowHitTest = X11_SetWindowHitTest;
   251 
   252     device->shape_driver.CreateShaper = X11_CreateShaper;
   253     device->shape_driver.SetWindowShape = X11_SetWindowShape;
   254     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
   255 
   256 #if SDL_VIDEO_OPENGL_GLX
   257     device->GL_LoadLibrary = X11_GL_LoadLibrary;
   258     device->GL_GetProcAddress = X11_GL_GetProcAddress;
   259     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
   260     device->GL_CreateContext = X11_GL_CreateContext;
   261     device->GL_MakeCurrent = X11_GL_MakeCurrent;
   262     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
   263     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
   264     device->GL_SwapWindow = X11_GL_SwapWindow;
   265     device->GL_DeleteContext = X11_GL_DeleteContext;
   266 #elif SDL_VIDEO_OPENGL_EGL
   267     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
   268     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
   269     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
   270     device->GL_CreateContext = X11_GLES_CreateContext;
   271     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
   272     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
   273     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
   274     device->GL_SwapWindow = X11_GLES_SwapWindow;
   275     device->GL_DeleteContext = X11_GLES_DeleteContext;
   276 #endif
   277 
   278     device->SetClipboardText = X11_SetClipboardText;
   279     device->GetClipboardText = X11_GetClipboardText;
   280     device->HasClipboardText = X11_HasClipboardText;
   281     device->StartTextInput = X11_StartTextInput;
   282     device->StopTextInput = X11_StopTextInput;
   283     device->SetTextInputRect = X11_SetTextInputRect;
   284     
   285     device->free = X11_DeleteDevice;
   286 
   287     return device;
   288 }
   289 
   290 VideoBootStrap X11_bootstrap = {
   291     "x11", "SDL X11 video driver",
   292     X11_Available, X11_CreateDevice
   293 };
   294 
   295 static int (*handler) (Display *, XErrorEvent *) = NULL;
   296 static int
   297 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
   298 {
   299     if (e->error_code == BadWindow) {
   300         return (0);
   301     } else {
   302         return (handler(d, e));
   303     }
   304 }
   305 
   306 static void
   307 X11_CheckWindowManager(_THIS)
   308 {
   309     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   310     Display *display = data->display;
   311     Atom _NET_SUPPORTING_WM_CHECK;
   312     int status, real_format;
   313     Atom real_type;
   314     unsigned long items_read = 0, items_left = 0;
   315     unsigned char *propdata = NULL;
   316     Window wm_window = 0;
   317 #ifdef DEBUG_WINDOW_MANAGER
   318     char *wm_name;
   319 #endif
   320 
   321     /* Set up a handler to gracefully catch errors */
   322     X11_XSync(display, False);
   323     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
   324 
   325     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
   326     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   327     if (status == Success) {
   328         if (items_read) {
   329             wm_window = ((Window*)propdata)[0];
   330         }
   331         if (propdata) {
   332             X11_XFree(propdata);
   333             propdata = NULL;
   334         }
   335     }
   336 
   337     if (wm_window) {
   338         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   339         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
   340             wm_window = None;
   341         }
   342         if (status == Success && propdata) {
   343             X11_XFree(propdata);
   344             propdata = NULL;
   345         }
   346     }
   347 
   348     /* Reset the error handler, we're done checking */
   349     X11_XSync(display, False);
   350     X11_XSetErrorHandler(handler);
   351 
   352     if (!wm_window) {
   353 #ifdef DEBUG_WINDOW_MANAGER
   354         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
   355 #endif
   356         return;
   357     }
   358     data->net_wm = SDL_TRUE;
   359 
   360 #ifdef DEBUG_WINDOW_MANAGER
   361     wm_name = X11_GetWindowTitle(_this, wm_window);
   362     printf("Window manager: %s\n", wm_name);
   363     SDL_free(wm_name);
   364 #endif
   365 }
   366 
   367 
   368 int
   369 X11_VideoInit(_THIS)
   370 {
   371     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   372 
   373     /* Get the window class name, usually the name of the application */
   374     data->classname = get_classname();
   375 
   376     /* Get the process PID to be associated to the window */
   377     data->pid = getpid();
   378 
   379     /* Open a connection to the X input manager */
   380 #ifdef X_HAVE_UTF8_STRING
   381     if (SDL_X11_HAVE_UTF8) {
   382         data->im =
   383             X11_XOpenIM(data->display, NULL, data->classname, data->classname);
   384     }
   385 #endif
   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(_NET_WM_STATE);
   392     GET_ATOM(_NET_WM_STATE_HIDDEN);
   393     GET_ATOM(_NET_WM_STATE_FOCUSED);
   394     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
   395     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
   396     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
   397     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
   398     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
   399     GET_ATOM(_NET_WM_NAME);
   400     GET_ATOM(_NET_WM_ICON_NAME);
   401     GET_ATOM(_NET_WM_ICON);
   402     GET_ATOM(_NET_WM_PING);
   403     GET_ATOM(_NET_WM_USER_TIME);
   404     GET_ATOM(_NET_ACTIVE_WINDOW);
   405     GET_ATOM(UTF8_STRING);
   406     GET_ATOM(PRIMARY);
   407     GET_ATOM(XdndEnter);
   408     GET_ATOM(XdndPosition);
   409     GET_ATOM(XdndStatus);
   410     GET_ATOM(XdndTypeList);
   411     GET_ATOM(XdndActionCopy);
   412     GET_ATOM(XdndDrop);
   413     GET_ATOM(XdndFinished);
   414     GET_ATOM(XdndSelection);
   415     GET_ATOM(XKLAVIER_STATE);
   416 
   417     /* Detect the window manager */
   418     X11_CheckWindowManager(_this);
   419 
   420     if (X11_InitModes(_this) < 0) {
   421         return -1;
   422     }
   423 
   424     X11_InitXinput2(_this);
   425 
   426     if (X11_InitKeyboard(_this) != 0) {
   427         return -1;
   428     }
   429     X11_InitMouse(_this);
   430 
   431     X11_InitTouch(_this);
   432 
   433 #if SDL_USE_LIBDBUS
   434     SDL_DBus_Init();
   435 #endif
   436 
   437     return 0;
   438 }
   439 
   440 void
   441 X11_VideoQuit(_THIS)
   442 {
   443     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   444 
   445     SDL_free(data->classname);
   446 #ifdef X_HAVE_UTF8_STRING
   447     if (data->im) {
   448         X11_XCloseIM(data->im);
   449     }
   450 #endif
   451 
   452     X11_QuitModes(_this);
   453     X11_QuitKeyboard(_this);
   454     X11_QuitMouse(_this);
   455     X11_QuitTouch(_this);
   456 
   457 #if SDL_USE_LIBDBUS
   458     SDL_DBus_Quit();
   459 #endif
   460 }
   461 
   462 SDL_bool
   463 X11_UseDirectColorVisuals(void)
   464 {
   465     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
   466 }
   467 
   468 #endif /* SDL_VIDEO_DRIVER_X11 */
   469 
   470 /* vim: set ts=4 sw=4 expandtab: */