change SDL 1.2 to SDL 2.0
[platform/upstream/SDL.git] / src / video / x11 / SDL_x11video.c
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 /* 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->GetDisplayDPI = X11_GetDisplayDPI;
220     device->SetDisplayMode = X11_SetDisplayMode;
221     device->SuspendScreenSaver = X11_SuspendScreenSaver;
222     device->PumpEvents = X11_PumpEvents;
223
224     device->CreateWindow = X11_CreateWindow;
225     device->CreateWindowFrom = X11_CreateWindowFrom;
226     device->SetWindowTitle = X11_SetWindowTitle;
227     device->SetWindowIcon = X11_SetWindowIcon;
228     device->SetWindowPosition = X11_SetWindowPosition;
229     device->SetWindowSize = X11_SetWindowSize;
230     device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
231     device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
232     device->ShowWindow = X11_ShowWindow;
233     device->HideWindow = X11_HideWindow;
234     device->RaiseWindow = X11_RaiseWindow;
235     device->MaximizeWindow = X11_MaximizeWindow;
236     device->MinimizeWindow = X11_MinimizeWindow;
237     device->RestoreWindow = X11_RestoreWindow;
238     device->SetWindowBordered = X11_SetWindowBordered;
239     device->SetWindowFullscreen = X11_SetWindowFullscreen;
240     device->SetWindowGammaRamp = X11_SetWindowGammaRamp;
241     device->SetWindowGrab = X11_SetWindowGrab;
242     device->DestroyWindow = X11_DestroyWindow;
243     device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
244     device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
245     device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
246     device->GetWindowWMInfo = X11_GetWindowWMInfo;
247     device->SetWindowHitTest = X11_SetWindowHitTest;
248
249     device->shape_driver.CreateShaper = X11_CreateShaper;
250     device->shape_driver.SetWindowShape = X11_SetWindowShape;
251     device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
252
253 #if SDL_VIDEO_OPENGL_GLX
254     device->GL_LoadLibrary = X11_GL_LoadLibrary;
255     device->GL_GetProcAddress = X11_GL_GetProcAddress;
256     device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
257     device->GL_CreateContext = X11_GL_CreateContext;
258     device->GL_MakeCurrent = X11_GL_MakeCurrent;
259     device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
260     device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
261     device->GL_SwapWindow = X11_GL_SwapWindow;
262     device->GL_DeleteContext = X11_GL_DeleteContext;
263 #elif SDL_VIDEO_OPENGL_EGL
264     device->GL_LoadLibrary = X11_GLES_LoadLibrary;
265     device->GL_GetProcAddress = X11_GLES_GetProcAddress;
266     device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
267     device->GL_CreateContext = X11_GLES_CreateContext;
268     device->GL_MakeCurrent = X11_GLES_MakeCurrent;
269     device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
270     device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
271     device->GL_SwapWindow = X11_GLES_SwapWindow;
272     device->GL_DeleteContext = X11_GLES_DeleteContext;
273 #endif
274
275     device->SetClipboardText = X11_SetClipboardText;
276     device->GetClipboardText = X11_GetClipboardText;
277     device->HasClipboardText = X11_HasClipboardText;
278     device->StartTextInput = X11_StartTextInput;
279     device->StopTextInput = X11_StopTextInput;
280     device->SetTextInputRect = X11_SetTextInputRect;
281     
282     device->free = X11_DeleteDevice;
283
284     return device;
285 }
286
287 VideoBootStrap X11_bootstrap = {
288     "x11", "SDL X11 video driver",
289     X11_Available, X11_CreateDevice
290 };
291
292 static int (*handler) (Display *, XErrorEvent *) = NULL;
293 static int
294 X11_CheckWindowManagerErrorHandler(Display * d, XErrorEvent * e)
295 {
296     if (e->error_code == BadWindow) {
297         return (0);
298     } else {
299         return (handler(d, e));
300     }
301 }
302
303 static void
304 X11_CheckWindowManager(_THIS)
305 {
306     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
307     Display *display = data->display;
308     Atom _NET_SUPPORTING_WM_CHECK;
309     int status, real_format;
310     Atom real_type;
311     unsigned long items_read = 0, items_left = 0;
312     unsigned char *propdata = NULL;
313     Window wm_window = 0;
314 #ifdef DEBUG_WINDOW_MANAGER
315     char *wm_name;
316 #endif
317
318     /* Set up a handler to gracefully catch errors */
319     X11_XSync(display, False);
320     handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
321
322     _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
323     status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
324     if (status == Success) {
325         if (items_read) {
326             wm_window = ((Window*)propdata)[0];
327         }
328         if (propdata) {
329             X11_XFree(propdata);
330             propdata = NULL;
331         }
332     }
333
334     if (wm_window) {
335         status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
336         if (status != Success || !items_read || wm_window != ((Window*)propdata)[0]) {
337             wm_window = None;
338         }
339         if (status == Success && propdata) {
340             X11_XFree(propdata);
341             propdata = NULL;
342         }
343     }
344
345     /* Reset the error handler, we're done checking */
346     X11_XSync(display, False);
347     X11_XSetErrorHandler(handler);
348
349     if (!wm_window) {
350 #ifdef DEBUG_WINDOW_MANAGER
351         printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
352 #endif
353         return;
354     }
355     data->net_wm = SDL_TRUE;
356
357 #ifdef DEBUG_WINDOW_MANAGER
358     wm_name = X11_GetWindowTitle(_this, wm_window);
359     printf("Window manager: %s\n", wm_name);
360     SDL_free(wm_name);
361 #endif
362 }
363
364
365 int
366 X11_VideoInit(_THIS)
367 {
368     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
369
370     /* Get the window class name, usually the name of the application */
371     data->classname = get_classname();
372
373     /* Get the process PID to be associated to the window */
374     data->pid = getpid();
375
376     /* Open a connection to the X input manager */
377 #ifdef X_HAVE_UTF8_STRING
378     if (SDL_X11_HAVE_UTF8) {
379         data->im =
380             X11_XOpenIM(data->display, NULL, data->classname, data->classname);
381     }
382 #endif
383
384     /* Look up some useful Atoms */
385 #define GET_ATOM(X) data->X = X11_XInternAtom(data->display, #X, False)
386     GET_ATOM(WM_PROTOCOLS);
387     GET_ATOM(WM_DELETE_WINDOW);
388     GET_ATOM(_NET_WM_STATE);
389     GET_ATOM(_NET_WM_STATE_HIDDEN);
390     GET_ATOM(_NET_WM_STATE_FOCUSED);
391     GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
392     GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
393     GET_ATOM(_NET_WM_STATE_FULLSCREEN);
394     GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
395     GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
396     GET_ATOM(_NET_WM_NAME);
397     GET_ATOM(_NET_WM_ICON_NAME);
398     GET_ATOM(_NET_WM_ICON);
399     GET_ATOM(_NET_WM_PING);
400     GET_ATOM(_NET_ACTIVE_WINDOW);
401     GET_ATOM(UTF8_STRING);
402     GET_ATOM(PRIMARY);
403     GET_ATOM(XdndEnter);
404     GET_ATOM(XdndPosition);
405     GET_ATOM(XdndStatus);
406     GET_ATOM(XdndTypeList);
407     GET_ATOM(XdndActionCopy);
408     GET_ATOM(XdndDrop);
409     GET_ATOM(XdndFinished);
410     GET_ATOM(XdndSelection);
411     GET_ATOM(XKLAVIER_STATE);
412
413     /* Detect the window manager */
414     X11_CheckWindowManager(_this);
415
416     if (X11_InitModes(_this) < 0) {
417         return -1;
418     }
419
420     X11_InitXinput2(_this);
421
422     if (X11_InitKeyboard(_this) != 0) {
423         return -1;
424     }
425     X11_InitMouse(_this);
426
427     X11_InitTouch(_this);
428
429 #if SDL_USE_LIBDBUS
430     SDL_DBus_Init();
431 #endif
432
433     return 0;
434 }
435
436 void
437 X11_VideoQuit(_THIS)
438 {
439     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
440
441     SDL_free(data->classname);
442 #ifdef X_HAVE_UTF8_STRING
443     if (data->im) {
444         X11_XCloseIM(data->im);
445     }
446 #endif
447
448     X11_QuitModes(_this);
449     X11_QuitKeyboard(_this);
450     X11_QuitMouse(_this);
451     X11_QuitTouch(_this);
452
453 #if SDL_USE_LIBDBUS
454     SDL_DBus_Quit();
455 #endif
456 }
457
458 SDL_bool
459 X11_UseDirectColorVisuals(void)
460 {
461     return SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ? SDL_FALSE : SDL_TRUE;
462 }
463
464 #endif /* SDL_VIDEO_DRIVER_X11 */
465
466 /* vim: set ts=4 sw=4 expandtab: */