src/video/x11/SDL_x11video.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 26 May 2015 06:27:46 -0700
changeset 9619 b94b6d0bff0f
parent 8953 dc80dc0bd22e
child 9814 4df28b087060
permissions -rw-r--r--
Updated the copyright year to 2015
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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     /* FIXME: Do we need this?
   179        if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) ||
   180        (SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) {
   181        local_X11 = 1;
   182        } else {
   183        local_X11 = 0;
   184        }
   185      */
   186     data->display = X11_XOpenDisplay(display);
   187 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
   188     /* On Tru64 if linking without -lX11, it fails and you get following message.
   189      * Xlib: connection to ":0.0" refused by server
   190      * Xlib: XDM authorization key matches an existing client!
   191      *
   192      * It succeeds if retrying 1 second later
   193      * or if running xhost +localhost on shell.
   194      */
   195     if (data->display == NULL) {
   196         SDL_Delay(1000);
   197         data->display = X11_XOpenDisplay(display);
   198     }
   199 #endif
   200     if (data->display == NULL) {
   201         SDL_free(device->driverdata);
   202         SDL_free(device);
   203         SDL_SetError("Couldn't open X11 display");
   204         return NULL;
   205     }
   206 #ifdef X11_DEBUG
   207     X11_XSynchronize(data->display, True);
   208 #endif
   209 
   210     /* Hook up an X11 error handler to recover the desktop resolution. */
   211     safety_net_triggered = SDL_FALSE;
   212     orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler);
   213 
   214     /* Set the function pointers */
   215     device->VideoInit = X11_VideoInit;
   216     device->VideoQuit = X11_VideoQuit;
   217     device->GetDisplayModes = X11_GetDisplayModes;
   218     device->GetDisplayBounds = X11_GetDisplayBounds;
   219     device->SetDisplayMode = X11_SetDisplayMode;
   220     device->SuspendScreenSaver = X11_SuspendScreenSaver;
   221     device->PumpEvents = X11_PumpEvents;
   222 
   223     device->CreateWindow = X11_CreateWindow;
   224     device->CreateWindowFrom = X11_CreateWindowFrom;
   225     device->SetWindowTitle = X11_SetWindowTitle;
   226     device->SetWindowIcon = X11_SetWindowIcon;
   227     device->SetWindowPosition = X11_SetWindowPosition;
   228     device->SetWindowSize = X11_SetWindowSize;
   229     device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
   230     device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
   231     device->ShowWindow = X11_ShowWindow;
   232     device->HideWindow = X11_HideWindow;
   233     device->RaiseWindow = X11_RaiseWindow;
   234     device->MaximizeWindow = X11_MaximizeWindow;
   235     device->MinimizeWindow = X11_MinimizeWindow;
   236     device->RestoreWindow = X11_RestoreWindow;
   237     device->SetWindowBordered = X11_SetWindowBordered;
   238     device->SetWindowFullscreen = X11_SetWindowFullscreen;
   239     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
   240     device->SetWindowGrab = X11_SetWindowGrab;
   241     device->DestroyWindow = X11_DestroyWindow;
   242     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
   243     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
   244     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
   245     device->GetWindowWMInfo = X11_GetWindowWMInfo;
   246     device->SetWindowHitTest = X11_SetWindowHitTest;
   247 
   248     device->shape_driver.CreateShaper = X11_CreateShaper;
   249     device->shape_driver.SetWindowShape = X11_SetWindowShape;
   250     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
   251 
   252 #if SDL_VIDEO_OPENGL_GLX
   253     device->GL_LoadLibrary = X11_GL_LoadLibrary;
   254     device->GL_GetProcAddress = X11_GL_GetProcAddress;
   255     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
   256     device->GL_CreateContext = X11_GL_CreateContext;
   257     device->GL_MakeCurrent = X11_GL_MakeCurrent;
   258     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
   259     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
   260     device->GL_SwapWindow = X11_GL_SwapWindow;
   261     device->GL_DeleteContext = X11_GL_DeleteContext;
   262 #elif SDL_VIDEO_OPENGL_EGL
   263     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
   264     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
   265     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
   266     device->GL_CreateContext = X11_GLES_CreateContext;
   267     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
   268     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
   269     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
   270     device->GL_SwapWindow = X11_GLES_SwapWindow;
   271     device->GL_DeleteContext = X11_GLES_DeleteContext;
   272 #endif
   273 
   274     device->SetClipboardText = X11_SetClipboardText;
   275     device->GetClipboardText = X11_GetClipboardText;
   276     device->HasClipboardText = X11_HasClipboardText;
   277     device->StartTextInput = X11_StartTextInput;
   278     device->StopTextInput = X11_StopTextInput;
   279     device->SetTextInputRect = X11_SetTextInputRect;
   280     
   281     device->free = X11_DeleteDevice;
   282 
   283     return device;
   284 }
   285 
   286 VideoBootStrap X11_bootstrap = {
   287     "x11", "SDL X11 video driver",
   288     X11_Available, X11_CreateDevice
   289 };
   290 
   291 static int (*handler) (Display *, XErrorEvent *) = NULL;
   292 static int
   293 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
   294 {
   295     if (e->error_code == BadWindow) {
   296         return (0);
   297     } else {
   298         return (handler(d, e));
   299     }
   300 }
   301 
   302 static void
   303 X11_CheckWindowManager(_THIS)
   304 {
   305     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   306     Display *display = data->display;
   307     Atom _NET_SUPPORTING_WM_CHECK;
   308     int status, real_format;
   309     Atom real_type;
   310     unsigned long items_read = 0, items_left = 0;
   311     unsigned char *propdata = NULL;
   312     Window wm_window = 0;
   313 #ifdef DEBUG_WINDOW_MANAGER
   314     char *wm_name;
   315 #endif
   316 
   317     /* Set up a handler to gracefully catch errors */
   318     X11_XSync(display, False);
   319     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
   320 
   321     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
   322     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   323     if (status == Success) {
   324         if (items_read) {
   325             wm_window = ((Window*)propdata)[0];
   326         }
   327         if (propdata) {
   328             X11_XFree(propdata);
   329             propdata = NULL;
   330         }
   331     }
   332 
   333     if (wm_window) {
   334         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   335         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
   336             wm_window = None;
   337         }
   338         if (status == Success && propdata) {
   339             X11_XFree(propdata);
   340             propdata = NULL;
   341         }
   342     }
   343 
   344     /* Reset the error handler, we're done checking */
   345     X11_XSync(display, False);
   346     X11_XSetErrorHandler(handler);
   347 
   348     if (!wm_window) {
   349 #ifdef DEBUG_WINDOW_MANAGER
   350         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
   351 #endif
   352         return;
   353     }
   354     data->net_wm = SDL_TRUE;
   355 
   356 #ifdef DEBUG_WINDOW_MANAGER
   357     wm_name = X11_GetWindowTitle(_this, wm_window);
   358     printf("Window manager: %s\n", wm_name);
   359     SDL_free(wm_name);
   360 #endif
   361 }
   362 
   363 
   364 int
   365 X11_VideoInit(_THIS)
   366 {
   367     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   368 
   369     /* Get the window class name, usually the name of the application */
   370     data->classname = get_classname();
   371 
   372     /* Get the process PID to be associated to the window */
   373     data->pid = getpid();
   374 
   375     /* Open a connection to the X input manager */
   376 #ifdef X_HAVE_UTF8_STRING
   377     if (SDL_X11_HAVE_UTF8) {
   378         data->im =
   379             X11_XOpenIM(data->display, NULL, data->classname, data->classname);
   380     }
   381 #endif
   382 
   383     /* Look up some useful Atoms */
   384 #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False)
   385     GET_ATOM(WM_PROTOCOLS);
   386     GET_ATOM(WM_DELETE_WINDOW);
   387     GET_ATOM(_NET_WM_STATE);
   388     GET_ATOM(_NET_WM_STATE_HIDDEN);
   389     GET_ATOM(_NET_WM_STATE_FOCUSED);
   390     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
   391     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
   392     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
   393     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
   394     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
   395     GET_ATOM(_NET_WM_NAME);
   396     GET_ATOM(_NET_WM_ICON_NAME);
   397     GET_ATOM(_NET_WM_ICON);
   398     GET_ATOM(_NET_WM_PING);
   399     GET_ATOM(_NET_ACTIVE_WINDOW);
   400     GET_ATOM(UTF8_STRING);
   401     GET_ATOM(PRIMARY);
   402     GET_ATOM(XdndEnter);
   403     GET_ATOM(XdndPosition);
   404     GET_ATOM(XdndStatus);
   405     GET_ATOM(XdndTypeList);
   406     GET_ATOM(XdndActionCopy);
   407     GET_ATOM(XdndDrop);
   408     GET_ATOM(XdndFinished);
   409     GET_ATOM(XdndSelection);
   410 
   411     /* Detect the window manager */
   412     X11_CheckWindowManager(_this);
   413 
   414     if (X11_InitModes(_this) < 0) {
   415         return -1;
   416     }
   417 
   418     X11_InitXinput2(_this);
   419 
   420     if (X11_InitKeyboard(_this) != 0) {
   421         return -1;
   422     }
   423     X11_InitMouse(_this);
   424 
   425     X11_InitTouch(_this);
   426 
   427 #if SDL_USE_LIBDBUS
   428     SDL_DBus_Init();
   429 #endif
   430 
   431     return 0;
   432 }
   433 
   434 void
   435 X11_VideoQuit(_THIS)
   436 {
   437     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   438 
   439     SDL_free(data->classname);
   440 #ifdef X_HAVE_UTF8_STRING
   441     if (data->im) {
   442         X11_XCloseIM(data->im);
   443     }
   444 #endif
   445 
   446     X11_QuitModes(_this);
   447     X11_QuitKeyboard(_this);
   448     X11_QuitMouse(_this);
   449     X11_QuitTouch(_this);
   450 
   451 #if SDL_USE_LIBDBUS
   452     SDL_DBus_Quit();
   453 #endif
   454 }
   455 
   456 SDL_bool
   457 X11_UseDirectColorVisuals(void)
   458 {
   459     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
   460 }
   461 
   462 #endif /* SDL_VIDEO_DRIVER_X11 */
   463 
   464 /* vim: set ts=4 sw=4 expandtab: */