src/video/x11/SDL_x11video.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 07 Dec 2012 23:26:28 -0500
changeset 6724 6c5ed0c4cc6d
parent 6638 3d221da309d3
child 6774 ad8522052ce6
permissions -rw-r--r--
X11 msgbox: try to protect the existing setlocale() state.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.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_ES || SDL_VIDEO_OPENGL_ES2
    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 = XOpenDisplay(NULL);
    97         if (display != NULL) {
    98             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         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     /* if we trigger an error in our error handler, don't try again. */
   126     if (!safety_net_triggered) {
   127         safety_net_triggered = SDL_TRUE;
   128         SDL_VideoDevice *device = SDL_GetVideoDevice();
   129         if (device != NULL) {
   130             int i;
   131             for (i = 0; i < device->num_displays; i++) {
   132                 SDL_VideoDisplay *display = &device->displays[i];
   133                 if (SDL_memcmp(&display->current_mode, &display->desktop_mode,
   134                                sizeof (SDL_DisplayMode)) != 0) {
   135                     X11_SetDisplayMode(device, display, &display->desktop_mode);
   136                 }
   137             }
   138         }
   139     }
   140 
   141     if (orig_x11_errhandler != NULL) {
   142         return orig_x11_errhandler(d, e);  /* probably terminate. */
   143     }
   144 
   145     return 0;
   146 }
   147 
   148 static SDL_VideoDevice *
   149 X11_CreateDevice(int devindex)
   150 {
   151     SDL_VideoDevice *device;
   152     SDL_VideoData *data;
   153     const char *display = NULL; /* Use the DISPLAY environment variable */
   154 
   155     if (!SDL_X11_LoadSymbols()) {
   156         return NULL;
   157     }
   158 
   159     // Need for threading gl calls. This is also required for the proprietary nVidia
   160 	//  driver to be threaded.
   161     XInitThreads();
   162 
   163     /* Initialize all variables that we clean on shutdown */
   164     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   165     if (!device) {
   166         SDL_OutOfMemory();
   167         return NULL;
   168     }
   169     data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   170     if (!data) {
   171         SDL_OutOfMemory();
   172         SDL_free(device);
   173         return NULL;
   174     }
   175     device->driverdata = data;
   176 
   177     /* FIXME: Do we need this?
   178        if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
   179        (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
   180        local_X11 = 1;
   181        } else {
   182        local_X11 = 0;
   183        }
   184      */
   185     data->display = XOpenDisplay(display);
   186 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
   187     /* On Tru64 if linking without -lX11, it fails and you get following message.
   188      * Xlib: connection to ":0.0" refused by server
   189      * Xlib: XDM authorization key matches an existing client!
   190      *
   191      * It succeeds if retrying 1 second later
   192      * or if running xhost +localhost on shell.
   193      */
   194     if (data->display == NULL) {
   195         SDL_Delay(1000);
   196         data->display = XOpenDisplay(display);
   197     }
   198 #endif
   199     if (data->display == NULL) {
   200         SDL_free(device->driverdata);
   201         SDL_free(device);
   202         SDL_SetError("Couldn't open X11 display");
   203         return NULL;
   204     }
   205 #ifdef X11_DEBUG
   206     XSynchronize(data->display, True);
   207 #endif
   208 
   209     /* Hook up an X11 error handler to recover the desktop resolution. */
   210     safety_net_triggered = SDL_FALSE;
   211     orig_x11_errhandler = XSetErrorHandler(X11_SafetyNetErrHandler);
   212 
   213     /* Set the function pointers */
   214     device->VideoInit = X11_VideoInit;
   215     device->VideoQuit = X11_VideoQuit;
   216     device->GetDisplayModes = X11_GetDisplayModes;
   217     device->GetDisplayBounds = X11_GetDisplayBounds;
   218     device->SetDisplayMode = X11_SetDisplayMode;
   219     device->SuspendScreenSaver = X11_SuspendScreenSaver;
   220     device->PumpEvents = X11_PumpEvents;
   221 
   222     device->CreateWindow = X11_CreateWindow;
   223     device->CreateWindowFrom = X11_CreateWindowFrom;
   224     device->SetWindowTitle = X11_SetWindowTitle;
   225     device->SetWindowIcon = X11_SetWindowIcon;
   226     device->SetWindowPosition = X11_SetWindowPosition;
   227     device->SetWindowSize = X11_SetWindowSize;
   228     device->ShowWindow = X11_ShowWindow;
   229     device->HideWindow = X11_HideWindow;
   230     device->RaiseWindow = X11_RaiseWindow;
   231     device->MaximizeWindow = X11_MaximizeWindow;
   232     device->MinimizeWindow = X11_MinimizeWindow;
   233     device->RestoreWindow = X11_RestoreWindow;
   234     device->SetWindowBordered = X11_SetWindowBordered;
   235     device->SetWindowFullscreen = X11_SetWindowFullscreen;
   236     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
   237     device->SetWindowGrab = X11_SetWindowGrab;
   238     device->DestroyWindow = X11_DestroyWindow;
   239     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
   240     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
   241     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
   242     device->GetWindowWMInfo = X11_GetWindowWMInfo;
   243 
   244     device->shape_driver.CreateShaper = X11_CreateShaper;
   245     device->shape_driver.SetWindowShape = X11_SetWindowShape;
   246     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
   247 
   248 #if SDL_VIDEO_OPENGL_GLX
   249     device->GL_LoadLibrary = X11_GL_LoadLibrary;
   250     device->GL_GetProcAddress = X11_GL_GetProcAddress;
   251     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
   252     device->GL_CreateContext = X11_GL_CreateContext;
   253     device->GL_MakeCurrent = X11_GL_MakeCurrent;
   254     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
   255     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
   256     device->GL_SwapWindow = X11_GL_SwapWindow;
   257     device->GL_DeleteContext = X11_GL_DeleteContext;
   258 #elif SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
   259     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
   260     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
   261     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
   262     device->GL_CreateContext = X11_GLES_CreateContext;
   263     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
   264     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
   265     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
   266     device->GL_SwapWindow = X11_GLES_SwapWindow;
   267     device->GL_DeleteContext = X11_GLES_DeleteContext;
   268 #endif
   269 
   270     device->SetClipboardText = X11_SetClipboardText;
   271     device->GetClipboardText = X11_GetClipboardText;
   272     device->HasClipboardText = X11_HasClipboardText;
   273 
   274     device->free = X11_DeleteDevice;
   275 
   276     return device;
   277 }
   278 
   279 VideoBootStrap X11_bootstrap = {
   280     "x11", "SDL X11 video driver",
   281     X11_Available, X11_CreateDevice
   282 };
   283 
   284 static int (*handler) (Display *, XErrorEvent *) = NULL;
   285 static int
   286 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
   287 {
   288     if (e->error_code == BadWindow) {
   289         return (0);
   290     } else {
   291         return (handler(d, e));
   292     }
   293 }
   294 
   295 static void
   296 X11_CheckWindowManager(_THIS)
   297 {
   298     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   299     Display *display = data->display;
   300     Atom _NET_SUPPORTING_WM_CHECK;
   301     int status, real_format;
   302     Atom real_type;
   303     unsigned long items_read, items_left;
   304     unsigned char *propdata;
   305     Window wm_window = 0;
   306 #ifdef DEBUG_WINDOW_MANAGER
   307     char *wm_name;
   308 #endif
   309 
   310     /* Set up a handler to gracefully catch errors */
   311     XSync(display, False);
   312     handler = XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
   313 
   314     _NET_SUPPORTING_WM_CHECK = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
   315     status = XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   316     if (status == Success && items_read) {
   317         wm_window = ((Window*)propdata)[0];
   318     }
   319     if (propdata) {
   320         XFree(propdata);
   321     }
   322 
   323     if (wm_window) {
   324         status = XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   325         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
   326             wm_window = None;
   327         }
   328         if (propdata) {
   329             XFree(propdata);
   330         }
   331     }
   332 
   333     /* Reset the error handler, we're done checking */
   334     XSync(display, False);
   335     XSetErrorHandler(handler);
   336 
   337     if (!wm_window) {
   338 #ifdef DEBUG_WINDOW_MANAGER
   339         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
   340 #endif
   341         return;
   342     }
   343     data->net_wm = SDL_TRUE;
   344 
   345 #ifdef DEBUG_WINDOW_MANAGER
   346     wm_name = X11_GetWindowTitle(_this, wm_window);
   347     printf("Window manager: %s\n", wm_name);
   348     SDL_free(wm_name);
   349 #endif
   350 }
   351 
   352 int
   353 X11_VideoInit(_THIS)
   354 {
   355     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   356 
   357     /* Get the window class name, usually the name of the application */
   358     data->classname = get_classname();
   359 
   360     /* Get the process PID to be associated to the window */
   361     data->pid = getpid();
   362 
   363     /* Open a connection to the X input manager */
   364 #ifdef X_HAVE_UTF8_STRING
   365     if (SDL_X11_HAVE_UTF8) {
   366         data->im =
   367             XOpenIM(data->display, NULL, data->classname, data->classname);
   368     }
   369 #endif
   370 
   371     /* Look up some useful Atoms */
   372 #define GET_ATOM(X) data->X = XInternAtom(data->display, #X, False)
   373     GET_ATOM(WM_PROTOCOLS);
   374     GET_ATOM(WM_DELETE_WINDOW);
   375     GET_ATOM(_NET_WM_STATE);
   376     GET_ATOM(_NET_WM_STATE_HIDDEN);
   377     GET_ATOM(_NET_WM_STATE_FOCUSED);
   378     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
   379     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
   380     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
   381     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
   382     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
   383     GET_ATOM(_NET_WM_NAME);
   384     GET_ATOM(_NET_WM_ICON_NAME);
   385     GET_ATOM(_NET_WM_ICON);
   386     GET_ATOM(_NET_WM_PING);
   387     GET_ATOM(UTF8_STRING);
   388 
   389     /* Detect the window manager */
   390     X11_CheckWindowManager(_this);
   391 
   392     if (X11_InitModes(_this) < 0) {
   393         return -1;
   394     }
   395 
   396     X11_InitXinput2(_this);
   397 
   398     if (X11_InitKeyboard(_this) != 0) {
   399         return -1;
   400     }
   401     X11_InitMouse(_this);
   402 
   403     X11_InitTouch(_this);
   404     return 0;
   405 }
   406 
   407 void
   408 X11_VideoQuit(_THIS)
   409 {
   410     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   411 
   412     if (data->classname) {
   413         SDL_free(data->classname);
   414     }
   415 #ifdef X_HAVE_UTF8_STRING
   416     if (data->im) {
   417         XCloseIM(data->im);
   418     }
   419 #endif
   420 
   421     X11_QuitModes(_this);
   422     X11_QuitKeyboard(_this);
   423     X11_QuitMouse(_this);
   424     X11_QuitTouch(_this);
   425 }
   426 
   427 SDL_bool
   428 X11_UseDirectColorVisuals(void)
   429 {
   430     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
   431 }
   432 
   433 #endif /* SDL_VIDEO_DRIVER_X11 */
   434 
   435 /* vim: set ts=4 sw=4 expandtab: */