src/video/x11/SDL_x11video.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 02 Mar 2013 20:44:16 -0800
changeset 6950 1ddb72193079
parent 6885 700f1b25f77f
child 6970 e0db39f7afb0
permissions -rw-r--r--
Added a mouse ID to the mouse events, which set to the special value SDL_TOUCH_MOUSEID for mouse events simulated by touch input.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 /* !!! FIXME: move dbus stuff to somewhere under src/core/linux ... */
    43 #if SDL_USE_LIBDBUS
    44 /* we never link directly to libdbus. */
    45 #include "SDL_loadso.h"
    46 static const char *dbus_library = "libdbus-1.so.3";
    47 static void *dbus_handle = NULL;
    48 
    49 /* !!! FIXME: this is kinda ugly. */
    50 static SDL_bool
    51 load_dbus_sym(const char *fn, void **addr)
    52 {
    53     *addr = SDL_LoadFunction(dbus_handle, fn);
    54     if (*addr == NULL) {
    55         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    56         return SDL_FALSE;
    57     }
    58 
    59     return SDL_TRUE;
    60 }
    61 
    62 /* libdbus entry points... */
    63 static DBusConnection *(*DBUS_dbus_bus_get_private)(DBusBusType, DBusError *) = NULL;
    64 static void (*DBUS_dbus_connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t) = NULL;
    65 static dbus_bool_t (*DBUS_dbus_connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *) = NULL;
    66 static void (*DBUS_dbus_connection_close)(DBusConnection *) = NULL;
    67 static void (*DBUS_dbus_connection_unref)(DBusConnection *) = NULL;
    68 static void (*DBUS_dbus_connection_flush)(DBusConnection *) = NULL;
    69 static DBusMessage *(*DBUS_dbus_message_new_method_call)(const char *, const char *, const char *, const char *) = NULL;
    70 static void (*DBUS_dbus_message_unref)(DBusMessage *) = NULL;
    71 static void (*DBUS_dbus_error_init)(DBusError *) = NULL;
    72 static dbus_bool_t (*DBUS_dbus_error_is_set)(const DBusError *) = NULL;
    73 static void (*DBUS_dbus_error_free)(DBusError *) = NULL;
    74 
    75 static int
    76 load_dbus_syms(void)
    77 {
    78     /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    79     #define SDL_DBUS_SYM(x) \
    80         if (!load_dbus_sym(#x, (void **) (char *) &DBUS_##x)) return -1
    81 
    82     SDL_DBUS_SYM(dbus_bus_get_private);
    83     SDL_DBUS_SYM(dbus_connection_set_exit_on_disconnect);
    84     SDL_DBUS_SYM(dbus_connection_send);
    85     SDL_DBUS_SYM(dbus_connection_close);
    86     SDL_DBUS_SYM(dbus_connection_unref);
    87     SDL_DBUS_SYM(dbus_connection_flush);
    88     SDL_DBUS_SYM(dbus_message_new_method_call);
    89     SDL_DBUS_SYM(dbus_message_unref);
    90     SDL_DBUS_SYM(dbus_error_init);
    91     SDL_DBUS_SYM(dbus_error_is_set);
    92     SDL_DBUS_SYM(dbus_error_free);
    93 
    94     #undef SDL_DBUS_SYM
    95 
    96     return 0;
    97 }
    98 
    99 static void
   100 UnloadDBUSLibrary(void)
   101 {
   102     if (dbus_handle != NULL) {
   103         SDL_UnloadObject(dbus_handle);
   104         dbus_handle = NULL;
   105     }
   106 }
   107 
   108 static int
   109 LoadDBUSLibrary(void)
   110 {
   111     int retval = 0;
   112     if (dbus_handle == NULL) {
   113         dbus_handle = SDL_LoadObject(dbus_library);
   114         if (dbus_handle == NULL) {
   115             retval = -1;
   116             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
   117         } else {
   118             retval = load_dbus_syms();
   119             if (retval < 0) {
   120                 UnloadDBUSLibrary();
   121             }
   122         }
   123     }
   124 
   125     return retval;
   126 }
   127 
   128 static void
   129 X11_InitDBus(_THIS)
   130 {
   131     if (LoadDBUSLibrary() != -1) {
   132         SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   133         DBusError err;
   134         DBUS_dbus_error_init(&err);
   135         data->dbus = DBUS_dbus_bus_get_private(DBUS_BUS_SESSION, &err);
   136         if (DBUS_dbus_error_is_set(&err)) {
   137             DBUS_dbus_error_free(&err);
   138             if (data->dbus) {
   139                 DBUS_dbus_connection_unref(data->dbus);
   140                 data->dbus = NULL;
   141             }
   142             return;  /* oh well */
   143         }
   144         DBUS_dbus_connection_set_exit_on_disconnect(data->dbus, 0);
   145     }
   146 }
   147 
   148 static void
   149 X11_QuitDBus(_THIS)
   150 {
   151     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   152     if (data->dbus) {
   153         DBUS_dbus_connection_close(data->dbus);
   154         DBUS_dbus_connection_unref(data->dbus);
   155         data->dbus = NULL;
   156     }
   157 }
   158 
   159 void
   160 SDL_dbus_screensaver_tickle(_THIS)
   161 {
   162     const SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   163     DBusConnection *conn = data->dbus;
   164     if (conn != NULL) {
   165         DBusMessage *msg = DBUS_dbus_message_new_method_call("org.gnome.ScreenSaver",
   166                                                              "/org/gnome/ScreenSaver",
   167                                                              "org.gnome.ScreenSaver",
   168                                                              "SimulateUserActivity");
   169         if (msg != NULL) {
   170             if (DBUS_dbus_connection_send(conn, msg, NULL)) {
   171                 DBUS_dbus_connection_flush(conn);
   172             }
   173             DBUS_dbus_message_unref(msg);
   174         }
   175     }
   176 }
   177 #endif
   178 
   179 /* Initialization/Query functions */
   180 static int X11_VideoInit(_THIS);
   181 static void X11_VideoQuit(_THIS);
   182 
   183 /* Find out what class name we should use */
   184 static char *
   185 get_classname()
   186 {
   187     char *spot;
   188 #if defined(__LINUX__) || defined(__FREEBSD__)
   189     char procfile[1024];
   190     char linkfile[1024];
   191     int linksize;
   192 #endif
   193 
   194     /* First allow environment variable override */
   195     spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
   196     if (spot) {
   197         return SDL_strdup(spot);
   198     }
   199 
   200     /* Next look at the application's executable name */
   201 #if defined(__LINUX__) || defined(__FREEBSD__)
   202 #if defined(__LINUX__)
   203     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
   204 #elif defined(__FREEBSD__)
   205     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
   206                  getpid());
   207 #else
   208 #error Where can we find the executable name?
   209 #endif
   210     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
   211     if (linksize > 0) {
   212         linkfile[linksize] = '\0';
   213         spot = SDL_strrchr(linkfile, '/');
   214         if (spot) {
   215             return SDL_strdup(spot + 1);
   216         } else {
   217             return SDL_strdup(linkfile);
   218         }
   219     }
   220 #endif /* __LINUX__ || __FREEBSD__ */
   221 
   222     /* Finally use the default we've used forever */
   223     return SDL_strdup("SDL_App");
   224 }
   225 
   226 /* X11 driver bootstrap functions */
   227 
   228 static int
   229 X11_Available(void)
   230 {
   231     Display *display = NULL;
   232     if (SDL_X11_LoadSymbols()) {
   233         display = XOpenDisplay(NULL);
   234         if (display != NULL) {
   235             XCloseDisplay(display);
   236         }
   237         SDL_X11_UnloadSymbols();
   238     }
   239     return (display != NULL);
   240 }
   241 
   242 static void
   243 X11_DeleteDevice(SDL_VideoDevice * device)
   244 {
   245     SDL_VideoData *data = (SDL_VideoData *) device->driverdata;
   246     if (data->display) {
   247         XCloseDisplay(data->display);
   248     }
   249     SDL_free(data->windowlist);
   250     SDL_free(device->driverdata);
   251     SDL_free(device);
   252 
   253     SDL_X11_UnloadSymbols();
   254 }
   255 
   256 /* An error handler to reset the vidmode and then call the default handler. */
   257 static SDL_bool safety_net_triggered = SDL_FALSE;
   258 static int (*orig_x11_errhandler) (Display *, XErrorEvent *) = NULL;
   259 static int
   260 X11_SafetyNetErrHandler(Display * d, XErrorEvent * e)
   261 {
   262     /* if we trigger an error in our error handler, don't try again. */
   263     if (!safety_net_triggered) {
   264         safety_net_triggered = SDL_TRUE;
   265         SDL_VideoDevice *device = SDL_GetVideoDevice();
   266         if (device != NULL) {
   267             int i;
   268             for (i = 0; i < device->num_displays; i++) {
   269                 SDL_VideoDisplay *display = &device->displays[i];
   270                 if (SDL_memcmp(&display->current_mode, &display->desktop_mode,
   271                                sizeof (SDL_DisplayMode)) != 0) {
   272                     X11_SetDisplayMode(device, display, &display->desktop_mode);
   273                 }
   274             }
   275         }
   276     }
   277 
   278     if (orig_x11_errhandler != NULL) {
   279         return orig_x11_errhandler(d, e);  /* probably terminate. */
   280     }
   281 
   282     return 0;
   283 }
   284 
   285 static SDL_VideoDevice *
   286 X11_CreateDevice(int devindex)
   287 {
   288     SDL_VideoDevice *device;
   289     SDL_VideoData *data;
   290     const char *display = NULL; /* Use the DISPLAY environment variable */
   291 
   292     if (!SDL_X11_LoadSymbols()) {
   293         return NULL;
   294     }
   295 
   296     /* Need for threading gl calls. This is also required for the proprietary
   297         nVidia driver to be threaded. */
   298     XInitThreads();
   299 
   300     /* Initialize all variables that we clean on shutdown */
   301     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   302     if (!device) {
   303         SDL_OutOfMemory();
   304         return NULL;
   305     }
   306     data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   307     if (!data) {
   308         SDL_OutOfMemory();
   309         SDL_free(device);
   310         return NULL;
   311     }
   312     device->driverdata = data;
   313 
   314     /* FIXME: Do we need this?
   315        if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
   316        (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
   317        local_X11 = 1;
   318        } else {
   319        local_X11 = 0;
   320        }
   321      */
   322     data->display = XOpenDisplay(display);
   323 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
   324     /* On Tru64 if linking without -lX11, it fails and you get following message.
   325      * Xlib: connection to ":0.0" refused by server
   326      * Xlib: XDM authorization key matches an existing client!
   327      *
   328      * It succeeds if retrying 1 second later
   329      * or if running xhost +localhost on shell.
   330      */
   331     if (data->display == NULL) {
   332         SDL_Delay(1000);
   333         data->display = XOpenDisplay(display);
   334     }
   335 #endif
   336     if (data->display == NULL) {
   337         SDL_free(device->driverdata);
   338         SDL_free(device);
   339         SDL_SetError("Couldn't open X11 display");
   340         return NULL;
   341     }
   342 #ifdef X11_DEBUG
   343     XSynchronize(data->display, True);
   344 #endif
   345 
   346     /* Hook up an X11 error handler to recover the desktop resolution. */
   347     safety_net_triggered = SDL_FALSE;
   348     orig_x11_errhandler = XSetErrorHandler(X11_SafetyNetErrHandler);
   349 
   350     /* Set the function pointers */
   351     device->VideoInit = X11_VideoInit;
   352     device->VideoQuit = X11_VideoQuit;
   353     device->GetDisplayModes = X11_GetDisplayModes;
   354     device->GetDisplayBounds = X11_GetDisplayBounds;
   355     device->SetDisplayMode = X11_SetDisplayMode;
   356     device->SuspendScreenSaver = X11_SuspendScreenSaver;
   357     device->PumpEvents = X11_PumpEvents;
   358 
   359     device->CreateWindow = X11_CreateWindow;
   360     device->CreateWindowFrom = X11_CreateWindowFrom;
   361     device->SetWindowTitle = X11_SetWindowTitle;
   362     device->SetWindowIcon = X11_SetWindowIcon;
   363     device->SetWindowPosition = X11_SetWindowPosition;
   364     device->SetWindowSize = X11_SetWindowSize;
   365     device->ShowWindow = X11_ShowWindow;
   366     device->HideWindow = X11_HideWindow;
   367     device->RaiseWindow = X11_RaiseWindow;
   368     device->MaximizeWindow = X11_MaximizeWindow;
   369     device->MinimizeWindow = X11_MinimizeWindow;
   370     device->RestoreWindow = X11_RestoreWindow;
   371     device->SetWindowBordered = X11_SetWindowBordered;
   372     device->SetWindowFullscreen = X11_SetWindowFullscreen;
   373     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
   374     device->SetWindowGrab = X11_SetWindowGrab;
   375     device->DestroyWindow = X11_DestroyWindow;
   376     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
   377     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
   378     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
   379     device->GetWindowWMInfo = X11_GetWindowWMInfo;
   380 
   381     device->shape_driver.CreateShaper = X11_CreateShaper;
   382     device->shape_driver.SetWindowShape = X11_SetWindowShape;
   383     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
   384 
   385 #if SDL_VIDEO_OPENGL_GLX
   386     device->GL_LoadLibrary = X11_GL_LoadLibrary;
   387     device->GL_GetProcAddress = X11_GL_GetProcAddress;
   388     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
   389     device->GL_CreateContext = X11_GL_CreateContext;
   390     device->GL_MakeCurrent = X11_GL_MakeCurrent;
   391     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
   392     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
   393     device->GL_SwapWindow = X11_GL_SwapWindow;
   394     device->GL_DeleteContext = X11_GL_DeleteContext;
   395 #elif SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
   396     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
   397     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
   398     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
   399     device->GL_CreateContext = X11_GLES_CreateContext;
   400     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
   401     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
   402     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
   403     device->GL_SwapWindow = X11_GLES_SwapWindow;
   404     device->GL_DeleteContext = X11_GLES_DeleteContext;
   405 #endif
   406 
   407     device->SetClipboardText = X11_SetClipboardText;
   408     device->GetClipboardText = X11_GetClipboardText;
   409     device->HasClipboardText = X11_HasClipboardText;
   410 
   411     device->free = X11_DeleteDevice;
   412 
   413     return device;
   414 }
   415 
   416 VideoBootStrap X11_bootstrap = {
   417     "x11", "SDL X11 video driver",
   418     X11_Available, X11_CreateDevice
   419 };
   420 
   421 static int (*handler) (Display *, XErrorEvent *) = NULL;
   422 static int
   423 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
   424 {
   425     if (e->error_code == BadWindow) {
   426         return (0);
   427     } else {
   428         return (handler(d, e));
   429     }
   430 }
   431 
   432 static void
   433 X11_CheckWindowManager(_THIS)
   434 {
   435     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   436     Display *display = data->display;
   437     Atom _NET_SUPPORTING_WM_CHECK;
   438     int status, real_format;
   439     Atom real_type;
   440     unsigned long items_read, items_left;
   441     unsigned char *propdata;
   442     Window wm_window = 0;
   443 #ifdef DEBUG_WINDOW_MANAGER
   444     char *wm_name;
   445 #endif
   446 
   447     /* Set up a handler to gracefully catch errors */
   448     XSync(display, False);
   449     handler = XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
   450 
   451     _NET_SUPPORTING_WM_CHECK = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
   452     status = XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   453     if (status == Success && items_read) {
   454         wm_window = ((Window*)propdata)[0];
   455     }
   456     if (propdata) {
   457         XFree(propdata);
   458     }
   459 
   460     if (wm_window) {
   461         status = XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
   462         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
   463             wm_window = None;
   464         }
   465         if (propdata) {
   466             XFree(propdata);
   467         }
   468     }
   469 
   470     /* Reset the error handler, we're done checking */
   471     XSync(display, False);
   472     XSetErrorHandler(handler);
   473 
   474     if (!wm_window) {
   475 #ifdef DEBUG_WINDOW_MANAGER
   476         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
   477 #endif
   478         return;
   479     }
   480     data->net_wm = SDL_TRUE;
   481 
   482 #ifdef DEBUG_WINDOW_MANAGER
   483     wm_name = X11_GetWindowTitle(_this, wm_window);
   484     printf("Window manager: %s\n", wm_name);
   485     SDL_free(wm_name);
   486 #endif
   487 }
   488 
   489 
   490 int
   491 X11_VideoInit(_THIS)
   492 {
   493     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   494 
   495     /* Get the window class name, usually the name of the application */
   496     data->classname = get_classname();
   497 
   498     /* Get the process PID to be associated to the window */
   499     data->pid = getpid();
   500 
   501     /* Open a connection to the X input manager */
   502 #ifdef X_HAVE_UTF8_STRING
   503     if (SDL_X11_HAVE_UTF8) {
   504         data->im =
   505             XOpenIM(data->display, NULL, data->classname, data->classname);
   506     }
   507 #endif
   508 
   509     /* Look up some useful Atoms */
   510 #define GET_ATOM(X) data->X = XInternAtom(data->display, #X, False)
   511     GET_ATOM(WM_PROTOCOLS);
   512     GET_ATOM(WM_DELETE_WINDOW);
   513     GET_ATOM(_NET_WM_STATE);
   514     GET_ATOM(_NET_WM_STATE_HIDDEN);
   515     GET_ATOM(_NET_WM_STATE_FOCUSED);
   516     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
   517     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
   518     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
   519     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
   520     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
   521     GET_ATOM(_NET_WM_NAME);
   522     GET_ATOM(_NET_WM_ICON_NAME);
   523     GET_ATOM(_NET_WM_ICON);
   524     GET_ATOM(_NET_WM_PING);
   525     GET_ATOM(UTF8_STRING);
   526 
   527     /* Detect the window manager */
   528     X11_CheckWindowManager(_this);
   529 
   530     if (X11_InitModes(_this) < 0) {
   531         return -1;
   532     }
   533 
   534     X11_InitXinput2(_this);
   535 
   536     if (X11_InitKeyboard(_this) != 0) {
   537         return -1;
   538     }
   539     X11_InitMouse(_this);
   540 
   541     X11_InitTouch(_this);
   542 
   543 #if SDL_USE_LIBDBUS
   544     X11_InitDBus(_this);
   545 #endif
   546 
   547     return 0;
   548 }
   549 
   550 void
   551 X11_VideoQuit(_THIS)
   552 {
   553     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   554 
   555     if (data->classname) {
   556         SDL_free(data->classname);
   557     }
   558 #ifdef X_HAVE_UTF8_STRING
   559     if (data->im) {
   560         XCloseIM(data->im);
   561     }
   562 #endif
   563 
   564     X11_QuitModes(_this);
   565     X11_QuitKeyboard(_this);
   566     X11_QuitMouse(_this);
   567     X11_QuitTouch(_this);
   568 
   569 #if SDL_USE_LIBDBUS
   570     X11_QuitDBus(_this);
   571 #endif
   572 }
   573 
   574 SDL_bool
   575 X11_UseDirectColorVisuals(void)
   576 {
   577     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
   578 }
   579 
   580 #endif /* SDL_VIDEO_DRIVER_X11 */
   581 
   582 /* vim: set ts=4 sw=4 expandtab: */