SDL  2.0
SDL_x11video.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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_timer.h"
30 #include "../SDL_sysvideo.h"
31 #include "../SDL_pixels_c.h"
32 
33 #include "SDL_x11video.h"
34 #include "SDL_x11framebuffer.h"
35 #include "SDL_x11shape.h"
36 #include "SDL_x11touch.h"
37 #include "SDL_x11xinput2.h"
38 
39 #if SDL_VIDEO_OPENGL_EGL
40 #include "SDL_x11opengles.h"
41 #endif
42 
43 #include "SDL_x11vulkan.h"
44 
45 /* Initialization/Query functions */
46 static int X11_VideoInit(_THIS);
47 static void X11_VideoQuit(_THIS);
48 
49 /* Find out what class name we should use */
50 static char *
51 get_classname()
52 {
53  char *spot;
54 #if defined(__LINUX__) || defined(__FREEBSD__)
55  char procfile[1024];
56  char linkfile[1024];
57  int linksize;
58 #endif
59 
60  /* First allow environment variable override */
61  spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
62  if (spot) {
63  return SDL_strdup(spot);
64  }
65 
66  /* Next look at the application's executable name */
67 #if defined(__LINUX__) || defined(__FREEBSD__)
68 #if defined(__LINUX__)
69  SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
70 #elif defined(__FREEBSD__)
71  SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
72  getpid());
73 #else
74 #error Where can we find the executable name?
75 #endif
76  linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
77  if (linksize > 0) {
78  linkfile[linksize] = '\0';
79  spot = SDL_strrchr(linkfile, '/');
80  if (spot) {
81  return SDL_strdup(spot + 1);
82  } else {
83  return SDL_strdup(linkfile);
84  }
85  }
86 #endif /* __LINUX__ || __FREEBSD__ */
87 
88  /* Finally use the default we've used forever */
89  return SDL_strdup("SDL_App");
90 }
91 
92 /* X11 driver bootstrap functions */
93 
94 static int
95 X11_Available(void)
96 {
97  Display *display = NULL;
98  if (SDL_X11_LoadSymbols()) {
99  display = X11_XOpenDisplay(NULL);
100  if (display != NULL) {
101  X11_XCloseDisplay(display);
102  }
104  }
105  return (display != NULL);
106 }
107 
108 static void
109 X11_DeleteDevice(SDL_VideoDevice * device)
110 {
112  if (device->vulkan_config.loader_handle) {
113  device->Vulkan_UnloadLibrary(device);
114  }
115  if (data->display) {
116  X11_XCloseDisplay(data->display);
117  }
118  SDL_free(data->windowlist);
119  SDL_free(device->driverdata);
120  SDL_free(device);
121 
123 }
124 
125 /* An error handler to reset the vidmode and then call the default handler. */
126 static SDL_bool safety_net_triggered = SDL_FALSE;
127 static int (*orig_x11_errhandler) (Display *, XErrorEvent *) = NULL;
128 static int
129 X11_SafetyNetErrHandler(Display * d, XErrorEvent * e)
130 {
131  SDL_VideoDevice *device = NULL;
132  /* if we trigger an error in our error handler, don't try again. */
133  if (!safety_net_triggered) {
134  safety_net_triggered = SDL_TRUE;
135  device = SDL_GetVideoDevice();
136  if (device != NULL) {
137  int i;
138  for (i = 0; i < device->num_displays; i++) {
139  SDL_VideoDisplay *display = &device->displays[i];
140  if (SDL_memcmp(&display->current_mode, &display->desktop_mode,
141  sizeof (SDL_DisplayMode)) != 0) {
142  X11_SetDisplayMode(device, display, &display->desktop_mode);
143  }
144  }
145  }
146  }
147 
148  if (orig_x11_errhandler != NULL) {
149  return orig_x11_errhandler(d, e); /* probably terminate. */
150  }
151 
152  return 0;
153 }
154 
155 static SDL_VideoDevice *
156 X11_CreateDevice(int devindex)
157 {
160  const char *display = NULL; /* Use the DISPLAY environment variable */
161 
162  if (!SDL_X11_LoadSymbols()) {
163  return NULL;
164  }
165 
166  /* Need for threading gl calls. This is also required for the proprietary
167  nVidia driver to be threaded. */
168  X11_XInitThreads();
169 
170  /* Initialize all variables that we clean on shutdown */
171  device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
172  if (!device) {
173  SDL_OutOfMemory();
174  return NULL;
175  }
176  data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
177  if (!data) {
178  SDL_free(device);
179  SDL_OutOfMemory();
180  return NULL;
181  }
182  device->driverdata = data;
183 
185 
186  /* FIXME: Do we need this?
187  if ( (SDL_strncmp(X11_XDisplayName(display), ":", 1) == 0) ||
188  (SDL_strncmp(X11_XDisplayName(display), "unix:", 5) == 0) ) {
189  local_X11 = 1;
190  } else {
191  local_X11 = 0;
192  }
193  */
194  data->display = X11_XOpenDisplay(display);
195 #ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
196  /* On some systems if linking without -lX11, it fails and you get following message.
197  * Xlib: connection to ":0.0" refused by server
198  * Xlib: XDM authorization key matches an existing client!
199  *
200  * It succeeds if retrying 1 second later
201  * or if running xhost +localhost on shell.
202  */
203  if (data->display == NULL) {
204  SDL_Delay(1000);
205  data->display = X11_XOpenDisplay(display);
206  }
207 #endif
208  if (data->display == NULL) {
209  SDL_free(device->driverdata);
210  SDL_free(device);
211  SDL_SetError("Couldn't open X11 display");
212  return NULL;
213  }
214 #ifdef X11_DEBUG
215  X11_XSynchronize(data->display, True);
216 #endif
217 
218  /* Hook up an X11 error handler to recover the desktop resolution. */
219  safety_net_triggered = SDL_FALSE;
220  orig_x11_errhandler = X11_XSetErrorHandler(X11_SafetyNetErrHandler);
221 
222  /* Set the function pointers */
223  device->VideoInit = X11_VideoInit;
224  device->VideoQuit = X11_VideoQuit;
225  device->ResetTouch = X11_ResetTouch;
232  device->PumpEvents = X11_PumpEvents;
233 
246  device->ShowWindow = X11_ShowWindow;
247  device->HideWindow = X11_HideWindow;
248  device->RaiseWindow = X11_RaiseWindow;
264 
268 
269 #if SDL_VIDEO_OPENGL_GLX
270  device->GL_LoadLibrary = X11_GL_LoadLibrary;
271  device->GL_GetProcAddress = X11_GL_GetProcAddress;
272  device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
273  device->GL_CreateContext = X11_GL_CreateContext;
274  device->GL_MakeCurrent = X11_GL_MakeCurrent;
275  device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
276  device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
277  device->GL_SwapWindow = X11_GL_SwapWindow;
278  device->GL_DeleteContext = X11_GL_DeleteContext;
279 #elif SDL_VIDEO_OPENGL_EGL
280  device->GL_LoadLibrary = X11_GLES_LoadLibrary;
281  device->GL_GetProcAddress = X11_GLES_GetProcAddress;
282  device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
283  device->GL_CreateContext = X11_GLES_CreateContext;
284  device->GL_MakeCurrent = X11_GLES_MakeCurrent;
285  device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
286  device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
287  device->GL_SwapWindow = X11_GLES_SwapWindow;
288  device->GL_DeleteContext = X11_GLES_DeleteContext;
289 #endif
290 
297 
298  device->free = X11_DeleteDevice;
299 
300 #if SDL_VIDEO_VULKAN
301  device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary;
302  device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary;
303  device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions;
304  device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface;
305 #endif
306 
307  return device;
308 }
309 
311  "x11", "SDL X11 video driver",
312  X11_Available, X11_CreateDevice
313 };
314 
315 static int (*handler) (Display *, XErrorEvent *) = NULL;
316 static int
317 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
318 {
319  if (e->error_code == BadWindow) {
320  return (0);
321  } else {
322  return (handler(d, e));
323  }
324 }
325 
326 static void
327 X11_CheckWindowManager(_THIS)
328 {
330  Display *display = data->display;
331  Atom _NET_SUPPORTING_WM_CHECK;
332  int status, real_format;
333  Atom real_type;
334  unsigned long items_read = 0, items_left = 0;
335  unsigned char *propdata = NULL;
336  Window wm_window = 0;
337 #ifdef DEBUG_WINDOW_MANAGER
338  char *wm_name;
339 #endif
340 
341  /* Set up a handler to gracefully catch errors */
342  X11_XSync(display, False);
343  handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
344 
345  _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
346  status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
347  if (status == Success) {
348  if (items_read) {
349  wm_window = ((Window*)propdata)[0];
350  }
351  if (propdata) {
352  X11_XFree(propdata);
353  propdata = NULL;
354  }
355  }
356 
357  if (wm_window) {
358  status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
359  if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
360  wm_window = None;
361  }
362  if (status == Success && propdata) {
363  X11_XFree(propdata);
364  propdata = NULL;
365  }
366  }
367 
368  /* Reset the error handler, we're done checking */
369  X11_XSync(display, False);
370  X11_XSetErrorHandler(handler);
371 
372  if (!wm_window) {
373 #ifdef DEBUG_WINDOW_MANAGER
374  printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
375 #endif
376  return;
377  }
378  data->net_wm = SDL_TRUE;
379 
380 #ifdef DEBUG_WINDOW_MANAGER
381  wm_name = X11_GetWindowTitle(_this, wm_window);
382  printf("Window manager: %s\n", wm_name);
383  SDL_free(wm_name);
384 #endif
385 }
386 
387 
388 int
389 X11_VideoInit(_THIS)
390 {
392 
393  /* Get the window class name, usually the name of the application */
394  data->classname = get_classname();
395 
396  /* Get the process PID to be associated to the window */
397  data->pid = getpid();
398 
399  /* I have no idea how random this actually is, or has to be. */
400  data->window_group = (XID) (((size_t) data->pid) ^ ((size_t) _this));
401 
402  /* Look up some useful Atoms */
403 #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False)
404  GET_ATOM(WM_PROTOCOLS);
405  GET_ATOM(WM_DELETE_WINDOW);
406  GET_ATOM(WM_TAKE_FOCUS);
407  GET_ATOM(_NET_WM_STATE);
408  GET_ATOM(_NET_WM_STATE_HIDDEN);
409  GET_ATOM(_NET_WM_STATE_FOCUSED);
412  GET_ATOM(_NET_WM_STATE_FULLSCREEN);
413  GET_ATOM(_NET_WM_STATE_ABOVE);
414  GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
415  GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
416  GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
417  GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
418  GET_ATOM(_NET_WM_NAME);
419  GET_ATOM(_NET_WM_ICON_NAME);
420  GET_ATOM(_NET_WM_ICON);
421  GET_ATOM(_NET_WM_PING);
422  GET_ATOM(_NET_WM_WINDOW_OPACITY);
423  GET_ATOM(_NET_WM_USER_TIME);
424  GET_ATOM(_NET_ACTIVE_WINDOW);
425  GET_ATOM(_NET_FRAME_EXTENTS);
426  GET_ATOM(UTF8_STRING);
427  GET_ATOM(PRIMARY);
428  GET_ATOM(XdndEnter);
429  GET_ATOM(XdndPosition);
430  GET_ATOM(XdndStatus);
431  GET_ATOM(XdndTypeList);
432  GET_ATOM(XdndActionCopy);
433  GET_ATOM(XdndDrop);
434  GET_ATOM(XdndFinished);
435  GET_ATOM(XdndSelection);
436  GET_ATOM(XKLAVIER_STATE);
437 
438  /* Detect the window manager */
439  X11_CheckWindowManager(_this);
440 
441  if (X11_InitModes(_this) < 0) {
442  return -1;
443  }
444 
445  X11_InitXinput2(_this);
446 
447  if (X11_InitKeyboard(_this) != 0) {
448  return -1;
449  }
450  X11_InitMouse(_this);
451 
452  X11_InitTouch(_this);
453 
454 #if SDL_USE_LIBDBUS
455  SDL_DBus_Init();
456 #endif
457 
458  return 0;
459 }
460 
461 void
462 X11_VideoQuit(_THIS)
463 {
464  SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
465 
466  if (data->clipboard_window) {
467  X11_XDestroyWindow(data->display, data->clipboard_window);
468  }
469 
470  SDL_free(data->classname);
471 #ifdef X_HAVE_UTF8_STRING
472  if (data->im) {
473  X11_XCloseIM(data->im);
474  }
475 #endif
476 
477  X11_QuitModes(_this);
478  X11_QuitKeyboard(_this);
479  X11_QuitMouse(_this);
480  X11_QuitTouch(_this);
481 
482 /* !!! FIXME: other subsystems use D-Bus, so we shouldn't quit it here;
483  have SDL.c do this at a higher level, or add refcounting. */
484 #if SDL_USE_LIBDBUS
485  SDL_DBus_Quit();
486 #endif
487 }
488 
489 SDL_bool
491 {
492  return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
493 }
494 
495 #endif /* SDL_VIDEO_DRIVER_X11 */
496 
497 /* vim: set ts=4 sw=4 expandtab: */