• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef X_HAVE_UTF8_STRING
43 #include <locale.h>
44 #endif
45 
46 /* Initialization/Query functions */
47 static int X11_VideoInit(_THIS);
48 static void X11_VideoQuit(_THIS);
49 
50 /* Find out what class name we should use */
51 static char *
get_classname()52 get_classname()
53 {
54     char *spot;
55 #if defined(__LINUX__) || defined(__FREEBSD__)
56     char procfile[1024];
57     char linkfile[1024];
58     int linksize;
59 #endif
60 
61     /* First allow environment variable override */
62     spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
63     if (spot) {
64         return SDL_strdup(spot);
65     }
66 
67     /* Next look at the application's executable name */
68 #if defined(__LINUX__) || defined(__FREEBSD__)
69 #if defined(__LINUX__)
70     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
71 #elif defined(__FREEBSD__)
72     SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
73                  getpid());
74 #else
75 #error Where can we find the executable name?
76 #endif
77     linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
78     if (linksize > 0) {
79         linkfile[linksize] = '\0';
80         spot = SDL_strrchr(linkfile, '/');
81         if (spot) {
82             return SDL_strdup(spot + 1);
83         } else {
84             return SDL_strdup(linkfile);
85         }
86     }
87 #endif /* __LINUX__ || __FREEBSD__ */
88 
89     /* Finally use the default we've used forever */
90     return SDL_strdup("SDL_App");
91 }
92 
93 /* X11 driver bootstrap functions */
94 
95 static int
X11_Available(void)96 X11_Available(void)
97 {
98     Display *display = NULL;
99     if (SDL_X11_LoadSymbols()) {
100         display = X11_XOpenDisplay(NULL);
101         if (display != NULL) {
102             X11_XCloseDisplay(display);
103         }
104         SDL_X11_UnloadSymbols();
105     }
106     return (display != NULL);
107 }
108 
109 static void
X11_DeleteDevice(SDL_VideoDevice * device)110 X11_DeleteDevice(SDL_VideoDevice * device)
111 {
112     SDL_VideoData *data = (SDL_VideoData *) device->driverdata;
113     if (data->display) {
114         X11_XCloseDisplay(data->display);
115     }
116     SDL_free(data->windowlist);
117     SDL_free(device->driverdata);
118     SDL_free(device);
119 
120     SDL_X11_UnloadSymbols();
121 }
122 
123 /* An error handler to reset the vidmode and then call the default handler. */
124 static SDL_bool safety_net_triggered = SDL_FALSE;
125 static int (*orig_x11_errhandler) (Display *, XErrorEvent *) = NULL;
126 static int
X11_SafetyNetErrHandler(Display * d,XErrorEvent * e)127 X11_SafetyNetErrHandler(Display * d, XErrorEvent * e)
128 {
129     SDL_VideoDevice *device = NULL;
130     /* if we trigger an error in our error handler, don't try again. */
131     if (!safety_net_triggered) {
132         safety_net_triggered = SDL_TRUE;
133         device = SDL_GetVideoDevice();
134         if (device != NULL) {
135             int i;
136             for (i = 0; i < device->num_displays; i++) {
137                 SDL_VideoDisplay *display = &device->displays[i];
138                 if (SDL_memcmp(&display->current_mode, &display->desktop_mode,
139                                sizeof (SDL_DisplayMode)) != 0) {
140                     X11_SetDisplayMode(device, display, &display->desktop_mode);
141                 }
142             }
143         }
144     }
145 
146     if (orig_x11_errhandler != NULL) {
147         return orig_x11_errhandler(d, e);  /* probably terminate. */
148     }
149 
150     return 0;
151 }
152 
153 static SDL_VideoDevice *
X11_CreateDevice(int devindex)154 X11_CreateDevice(int devindex)
155 {
156     SDL_VideoDevice *device;
157     SDL_VideoData *data;
158     const char *display = NULL; /* Use the DISPLAY environment variable */
159 
160     if (!SDL_X11_LoadSymbols()) {
161         return NULL;
162     }
163 
164     /* Need for threading gl calls. This is also required for the proprietary
165         nVidia driver to be threaded. */
166     X11_XInitThreads();
167 
168     /* Initialize all variables that we clean on shutdown */
169     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
170     if (!device) {
171         SDL_OutOfMemory();
172         return NULL;
173     }
174     data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
175     if (!data) {
176         SDL_free(device);
177         SDL_OutOfMemory();
178         return NULL;
179     }
180     device->driverdata = data;
181 
182     data->global_mouse_changed = SDL_TRUE;
183 
184     /* FIXME: Do we need this?
185        if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) ||
186        (SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) {
187        local_X11 = 1;
188        } else {
189        local_X11 = 0;
190        }
191      */
192     data->display = X11_XOpenDisplay(display);
193 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
194     /* On Tru64 if linking without -lX11, it fails and you get following message.
195      * Xlib: connection to ":0.0" refused by server
196      * Xlib: XDM authorization key matches an existing client!
197      *
198      * It succeeds if retrying 1 second later
199      * or if running xhost +localhost on shell.
200      */
201     if (data->display == NULL) {
202         SDL_Delay(1000);
203         data->display = X11_XOpenDisplay(display);
204     }
205 #endif
206     if (data->display == NULL) {
207         SDL_free(device->driverdata);
208         SDL_free(device);
209         SDL_SetError("Couldn't open X11 display");
210         return NULL;
211     }
212 #ifdef X11_DEBUG
213     X11_XSynchronize(data->display, True);
214 #endif
215 
216     /* Hook up an X11 error handler to recover the desktop resolution. */
217     safety_net_triggered = SDL_FALSE;
218     orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler);
219 
220     /* Set the function pointers */
221     device->VideoInit = X11_VideoInit;
222     device->VideoQuit = X11_VideoQuit;
223     device->GetDisplayModes = X11_GetDisplayModes;
224     device->GetDisplayBounds = X11_GetDisplayBounds;
225     device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds;
226     device->GetDisplayDPI = X11_GetDisplayDPI;
227     device->SetDisplayMode = X11_SetDisplayMode;
228     device->SuspendScreenSaver = X11_SuspendScreenSaver;
229     device->PumpEvents = X11_PumpEvents;
230 
231     device->CreateWindow = X11_CreateWindow;
232     device->CreateWindowFrom = X11_CreateWindowFrom;
233     device->SetWindowTitle = X11_SetWindowTitle;
234     device->SetWindowIcon = X11_SetWindowIcon;
235     device->SetWindowPosition = X11_SetWindowPosition;
236     device->SetWindowSize = X11_SetWindowSize;
237     device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
238     device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
239     device->GetWindowBordersSize = X11_GetWindowBordersSize;
240     device->SetWindowOpacity = X11_SetWindowOpacity;
241     device->SetWindowModalFor = X11_SetWindowModalFor;
242     device->SetWindowInputFocus = X11_SetWindowInputFocus;
243     device->ShowWindow = X11_ShowWindow;
244     device->HideWindow = X11_HideWindow;
245     device->RaiseWindow = X11_RaiseWindow;
246     device->MaximizeWindow = X11_MaximizeWindow;
247     device->MinimizeWindow = X11_MinimizeWindow;
248     device->RestoreWindow = X11_RestoreWindow;
249     device->SetWindowBordered = X11_SetWindowBordered;
250     device->SetWindowResizable = X11_SetWindowResizable;
251     device->SetWindowFullscreen = X11_SetWindowFullscreen;
252     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
253     device->SetWindowGrab = X11_SetWindowGrab;
254     device->DestroyWindow = X11_DestroyWindow;
255     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
256     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
257     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
258     device->GetWindowWMInfo = X11_GetWindowWMInfo;
259     device->SetWindowHitTest = X11_SetWindowHitTest;
260 
261     device->shape_driver.CreateShaper = X11_CreateShaper;
262     device->shape_driver.SetWindowShape = X11_SetWindowShape;
263     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
264 
265 #if SDL_VIDEO_OPENGL_GLX
266     device->GL_LoadLibrary = X11_GL_LoadLibrary;
267     device->GL_GetProcAddress = X11_GL_GetProcAddress;
268     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
269     device->GL_CreateContext = X11_GL_CreateContext;
270     device->GL_MakeCurrent = X11_GL_MakeCurrent;
271     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
272     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
273     device->GL_SwapWindow = X11_GL_SwapWindow;
274     device->GL_DeleteContext = X11_GL_DeleteContext;
275 #elif SDL_VIDEO_OPENGL_EGL
276     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
277     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
278     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
279     device->GL_CreateContext = X11_GLES_CreateContext;
280     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
281     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
282     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
283     device->GL_SwapWindow = X11_GLES_SwapWindow;
284     device->GL_DeleteContext = X11_GLES_DeleteContext;
285 #endif
286 
287     device->SetClipboardText = X11_SetClipboardText;
288     device->GetClipboardText = X11_GetClipboardText;
289     device->HasClipboardText = X11_HasClipboardText;
290     device->StartTextInput = X11_StartTextInput;
291     device->StopTextInput = X11_StopTextInput;
292     device->SetTextInputRect = X11_SetTextInputRect;
293 
294     device->free = X11_DeleteDevice;
295 
296     return device;
297 }
298 
299 VideoBootStrap X11_bootstrap = {
300     "x11", "SDL X11 video driver",
301     X11_Available, X11_CreateDevice
302 };
303 
304 static int (*handler) (Display *, XErrorEvent *) = NULL;
305 static int
X11_CheckWindowManagerErrorHandler(Display * d,XErrorEvent * e)306 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
307 {
308     if (e->error_code == BadWindow) {
309         return (0);
310     } else {
311         return (handler(d, e));
312     }
313 }
314 
315 static void
X11_CheckWindowManager(_THIS)316 X11_CheckWindowManager(_THIS)
317 {
318     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
319     Display *display = data->display;
320     Atom _NET_SUPPORTING_WM_CHECK;
321     int status, real_format;
322     Atom real_type;
323     unsigned long items_read = 0, items_left = 0;
324     unsigned char *propdata = NULL;
325     Window wm_window = 0;
326 #ifdef DEBUG_WINDOW_MANAGER
327     char *wm_name;
328 #endif
329 
330     /* Set up a handler to gracefully catch errors */
331     X11_XSync(display, False);
332     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
333 
334     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
335     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
336     if (status == Success) {
337         if (items_read) {
338             wm_window = ((Window*)propdata)[0];
339         }
340         if (propdata) {
341             X11_XFree(propdata);
342             propdata = NULL;
343         }
344     }
345 
346     if (wm_window) {
347         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
348         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
349             wm_window = None;
350         }
351         if (status == Success && propdata) {
352             X11_XFree(propdata);
353             propdata = NULL;
354         }
355     }
356 
357     /* Reset the error handler, we're done checking */
358     X11_XSync(display, False);
359     X11_XSetErrorHandler(handler);
360 
361     if (!wm_window) {
362 #ifdef DEBUG_WINDOW_MANAGER
363         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
364 #endif
365         return;
366     }
367     data->net_wm = SDL_TRUE;
368 
369 #ifdef DEBUG_WINDOW_MANAGER
370     wm_name = X11_GetWindowTitle(_this, wm_window);
371     printf("Window manager: %s\n", wm_name);
372     SDL_free(wm_name);
373 #endif
374 }
375 
376 
377 int
X11_VideoInit(_THIS)378 X11_VideoInit(_THIS)
379 {
380     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
381 
382     /* Get the window class name, usually the name of the application */
383     data->classname = get_classname();
384 
385     /* Get the process PID to be associated to the window */
386     data->pid = getpid();
387 
388     /* I have no idea how random this actually is, or has to be. */
389     data->window_group = (XID) (((size_t) data->pid) ^ ((size_t) _this));
390 
391     /* Open a connection to the X input manager */
392 #ifdef X_HAVE_UTF8_STRING
393     if (SDL_X11_HAVE_UTF8) {
394         /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
395            Compose keys will work correctly. */
396         char *prev_locale = setlocale(LC_ALL, NULL);
397         char *prev_xmods  = X11_XSetLocaleModifiers(NULL);
398         const char *new_xmods = "";
399 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX_FRONTEND_H)
400         const char *env_xmods = SDL_getenv("XMODIFIERS");
401 #endif
402         SDL_bool has_dbus_ime_support = SDL_FALSE;
403 
404         if (prev_locale) {
405             prev_locale = SDL_strdup(prev_locale);
406         }
407 
408         if (prev_xmods) {
409             prev_xmods = SDL_strdup(prev_xmods);
410         }
411 
412         /* IBus resends some key events that were filtered by XFilterEvents
413            when it is used via XIM which causes issues. Prevent this by forcing
414            @im=none if XMODIFIERS contains @im=ibus. IBus can still be used via
415            the DBus implementation, which also has support for pre-editing. */
416 #ifdef HAVE_IBUS_IBUS_H
417         if (env_xmods && SDL_strstr(env_xmods, "@im=ibus") != NULL) {
418             has_dbus_ime_support = SDL_TRUE;
419         }
420 #endif
421 #ifdef HAVE_FCITX_FRONTEND_H
422         if (env_xmods && SDL_strstr(env_xmods, "@im=fcitx") != NULL) {
423             has_dbus_ime_support = SDL_TRUE;
424         }
425 #endif
426         if (has_dbus_ime_support) {
427             new_xmods = "@im=none";
428         }
429 
430         setlocale(LC_ALL, "");
431         X11_XSetLocaleModifiers(new_xmods);
432 
433         data->im = X11_XOpenIM(data->display, NULL, data->classname, data->classname);
434 
435         /* Reset the locale + X locale modifiers back to how they were,
436            locale first because the X locale modifiers depend on it. */
437         setlocale(LC_ALL, prev_locale);
438         X11_XSetLocaleModifiers(prev_xmods);
439 
440         if (prev_locale) {
441             SDL_free(prev_locale);
442         }
443 
444         if (prev_xmods) {
445             SDL_free(prev_xmods);
446         }
447     }
448 #endif
449 
450     /* Look up some useful Atoms */
451 #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False)
452     GET_ATOM(WM_PROTOCOLS);
453     GET_ATOM(WM_DELETE_WINDOW);
454     GET_ATOM(WM_TAKE_FOCUS);
455     GET_ATOM(_NET_WM_STATE);
456     GET_ATOM(_NET_WM_STATE_HIDDEN);
457     GET_ATOM(_NET_WM_STATE_FOCUSED);
458     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
459     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
460     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
461     GET_ATOM(_NET_WM_STATE_ABOVE);
462     GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
463     GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
464     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
465     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
466     GET_ATOM(_NET_WM_NAME);
467     GET_ATOM(_NET_WM_ICON_NAME);
468     GET_ATOM(_NET_WM_ICON);
469     GET_ATOM(_NET_WM_PING);
470     GET_ATOM(_NET_WM_WINDOW_OPACITY);
471     GET_ATOM(_NET_WM_USER_TIME);
472     GET_ATOM(_NET_ACTIVE_WINDOW);
473     GET_ATOM(_NET_FRAME_EXTENTS);
474     GET_ATOM(UTF8_STRING);
475     GET_ATOM(PRIMARY);
476     GET_ATOM(XdndEnter);
477     GET_ATOM(XdndPosition);
478     GET_ATOM(XdndStatus);
479     GET_ATOM(XdndTypeList);
480     GET_ATOM(XdndActionCopy);
481     GET_ATOM(XdndDrop);
482     GET_ATOM(XdndFinished);
483     GET_ATOM(XdndSelection);
484     GET_ATOM(XKLAVIER_STATE);
485 
486     /* Detect the window manager */
487     X11_CheckWindowManager(_this);
488 
489     if (X11_InitModes(_this) < 0) {
490         return -1;
491     }
492 
493     X11_InitXinput2(_this);
494 
495     if (X11_InitKeyboard(_this) != 0) {
496         return -1;
497     }
498     X11_InitMouse(_this);
499 
500     X11_InitTouch(_this);
501 
502 #if SDL_USE_LIBDBUS
503     SDL_DBus_Init();
504 #endif
505 
506     return 0;
507 }
508 
509 void
X11_VideoQuit(_THIS)510 X11_VideoQuit(_THIS)
511 {
512     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
513 
514     SDL_free(data->classname);
515 #ifdef X_HAVE_UTF8_STRING
516     if (data->im) {
517         X11_XCloseIM(data->im);
518     }
519 #endif
520 
521     X11_QuitModes(_this);
522     X11_QuitKeyboard(_this);
523     X11_QuitMouse(_this);
524     X11_QuitTouch(_this);
525 
526 #if SDL_USE_LIBDBUS
527     SDL_DBus_Quit();
528 #endif
529 }
530 
531 SDL_bool
X11_UseDirectColorVisuals(void)532 X11_UseDirectColorVisuals(void)
533 {
534     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
535 }
536 
537 #endif /* SDL_VIDEO_DRIVER_X11 */
538 
539 /* vim: set ts=4 sw=4 expandtab: */
540