• 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 "SDL_assert.h"
26 #include "SDL_hints.h"
27 #include "../SDL_sysvideo.h"
28 #include "../SDL_pixels_c.h"
29 #include "../../events/SDL_keyboard_c.h"
30 #include "../../events/SDL_mouse_c.h"
31 
32 #include "SDL_x11video.h"
33 #include "SDL_x11mouse.h"
34 #include "SDL_x11shape.h"
35 #include "SDL_x11xinput2.h"
36 
37 #if SDL_VIDEO_OPENGL_EGL
38 #include "SDL_x11opengles.h"
39 #endif
40 
41 #include "SDL_timer.h"
42 #include "SDL_syswm.h"
43 #include "SDL_assert.h"
44 
45 #define _NET_WM_STATE_REMOVE    0l
46 #define _NET_WM_STATE_ADD       1l
47 #define _NET_WM_STATE_TOGGLE    2l
48 
isMapNotify(Display * dpy,XEvent * ev,XPointer win)49 static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win)
50 {
51     return ev->type == MapNotify && ev->xmap.window == *((Window*)win);
52 }
isUnmapNotify(Display * dpy,XEvent * ev,XPointer win)53 static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win)
54 {
55     return ev->type == UnmapNotify && ev->xunmap.window == *((Window*)win);
56 }
57 
58 /*
59 static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win)
60 {
61     return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window*)win);
62 }
63 static Bool
64 X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS)
65 {
66     Uint32 start = SDL_GetTicks();
67 
68     while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) {
69         if (SDL_TICKS_PASSED(SDL_GetTicks(), start + timeoutMS)) {
70             return False;
71         }
72     }
73     return True;
74 }
75 */
76 
77 static SDL_bool
X11_IsWindowLegacyFullscreen(_THIS,SDL_Window * window)78 X11_IsWindowLegacyFullscreen(_THIS, SDL_Window * window)
79 {
80     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
81     return (data->fswindow != 0);
82 }
83 
84 static SDL_bool
X11_IsWindowMapped(_THIS,SDL_Window * window)85 X11_IsWindowMapped(_THIS, SDL_Window * window)
86 {
87     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
88     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
89     XWindowAttributes attr;
90 
91     X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr);
92     if (attr.map_state != IsUnmapped) {
93         return SDL_TRUE;
94     } else {
95         return SDL_FALSE;
96     }
97 }
98 
99 #if 0
100 static SDL_bool
101 X11_IsActionAllowed(SDL_Window *window, Atom action)
102 {
103     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
104     Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS;
105     Atom type;
106     Display *display = data->videodata->display;
107     int form;
108     unsigned long remain;
109     unsigned long len, i;
110     Atom *list;
111     SDL_bool ret = SDL_FALSE;
112 
113     if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success)
114     {
115         for (i=0; i<len; ++i)
116         {
117             if (list[i] == action) {
118                 ret = SDL_TRUE;
119                 break;
120             }
121         }
122         X11_XFree(list);
123     }
124     return ret;
125 }
126 #endif /* 0 */
127 
128 void
X11_SetNetWMState(_THIS,Window xwindow,Uint32 flags)129 X11_SetNetWMState(_THIS, Window xwindow, Uint32 flags)
130 {
131     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
132     Display *display = videodata->display;
133     /* !!! FIXME: just dereference videodata below instead of copying to locals. */
134     Atom _NET_WM_STATE = videodata->_NET_WM_STATE;
135     /* Atom _NET_WM_STATE_HIDDEN = videodata->_NET_WM_STATE_HIDDEN; */
136     Atom _NET_WM_STATE_FOCUSED = videodata->_NET_WM_STATE_FOCUSED;
137     Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->_NET_WM_STATE_MAXIMIZED_VERT;
138     Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
139     Atom _NET_WM_STATE_FULLSCREEN = videodata->_NET_WM_STATE_FULLSCREEN;
140     Atom _NET_WM_STATE_ABOVE = videodata->_NET_WM_STATE_ABOVE;
141     Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->_NET_WM_STATE_SKIP_TASKBAR;
142     Atom _NET_WM_STATE_SKIP_PAGER = videodata->_NET_WM_STATE_SKIP_PAGER;
143     Atom atoms[16];
144     int count = 0;
145 
146     /* The window manager sets this property, we shouldn't set it.
147        If we did, this would indicate to the window manager that we don't
148        actually want to be mapped during X11_XMapRaised(), which would be bad.
149      *
150     if (flags & SDL_WINDOW_HIDDEN) {
151         atoms[count++] = _NET_WM_STATE_HIDDEN;
152     }
153     */
154 
155     if (flags & SDL_WINDOW_ALWAYS_ON_TOP) {
156         atoms[count++] = _NET_WM_STATE_ABOVE;
157     }
158     if (flags & SDL_WINDOW_SKIP_TASKBAR) {
159         atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR;
160         atoms[count++] = _NET_WM_STATE_SKIP_PAGER;
161     }
162     if (flags & SDL_WINDOW_INPUT_FOCUS) {
163         atoms[count++] = _NET_WM_STATE_FOCUSED;
164     }
165     if (flags & SDL_WINDOW_MAXIMIZED) {
166         atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
167         atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
168     }
169     if (flags & SDL_WINDOW_FULLSCREEN) {
170         atoms[count++] = _NET_WM_STATE_FULLSCREEN;
171     }
172 
173     SDL_assert(count <= SDL_arraysize(atoms));
174 
175     if (count > 0) {
176         X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32,
177                         PropModeReplace, (unsigned char *)atoms, count);
178     } else {
179         X11_XDeleteProperty(display, xwindow, _NET_WM_STATE);
180     }
181 }
182 
183 Uint32
X11_GetNetWMState(_THIS,Window xwindow)184 X11_GetNetWMState(_THIS, Window xwindow)
185 {
186     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
187     Display *display = videodata->display;
188     Atom _NET_WM_STATE = videodata->_NET_WM_STATE;
189     Atom _NET_WM_STATE_HIDDEN = videodata->_NET_WM_STATE_HIDDEN;
190     Atom _NET_WM_STATE_FOCUSED = videodata->_NET_WM_STATE_FOCUSED;
191     Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->_NET_WM_STATE_MAXIMIZED_VERT;
192     Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
193     Atom _NET_WM_STATE_FULLSCREEN = videodata->_NET_WM_STATE_FULLSCREEN;
194     Atom actualType;
195     int actualFormat;
196     unsigned long i, numItems, bytesAfter;
197     unsigned char *propertyValue = NULL;
198     long maxLength = 1024;
199     Uint32 flags = 0;
200 
201     if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE,
202                            0l, maxLength, False, XA_ATOM, &actualType,
203                            &actualFormat, &numItems, &bytesAfter,
204                            &propertyValue) == Success) {
205         Atom *atoms = (Atom *) propertyValue;
206         int maximized = 0;
207         int fullscreen = 0;
208 
209         for (i = 0; i < numItems; ++i) {
210             if (atoms[i] == _NET_WM_STATE_HIDDEN) {
211                 flags |= SDL_WINDOW_HIDDEN;
212             } else if (atoms[i] == _NET_WM_STATE_FOCUSED) {
213                 flags |= SDL_WINDOW_INPUT_FOCUS;
214             } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
215                 maximized |= 1;
216             } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
217                 maximized |= 2;
218             } else if ( atoms[i] == _NET_WM_STATE_FULLSCREEN) {
219                 fullscreen = 1;
220             }
221         }
222         if (maximized == 3) {
223             flags |= SDL_WINDOW_MAXIMIZED;
224         }
225 
226         if (fullscreen == 1) {
227             flags |= SDL_WINDOW_FULLSCREEN;
228         }
229         X11_XFree(propertyValue);
230     }
231 
232     /* FIXME, check the size hints for resizable */
233     /* flags |= SDL_WINDOW_RESIZABLE; */
234 
235     return flags;
236 }
237 
238 static int
SetupWindowData(_THIS,SDL_Window * window,Window w,BOOL created)239 SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
240 {
241     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
242     SDL_WindowData *data;
243     int numwindows = videodata->numwindows;
244     int windowlistlength = videodata->windowlistlength;
245     SDL_WindowData **windowlist = videodata->windowlist;
246 
247     /* Allocate the window data */
248     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
249     if (!data) {
250         return SDL_OutOfMemory();
251     }
252     data->window = window;
253     data->xwindow = w;
254 #ifdef X_HAVE_UTF8_STRING
255     if (SDL_X11_HAVE_UTF8 && videodata->im) {
256         data->ic =
257             X11_XCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w,
258                        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
259                        NULL);
260     }
261 #endif
262     data->created = created;
263     data->videodata = videodata;
264 
265     /* Associate the data with the window */
266 
267     if (numwindows < windowlistlength) {
268         windowlist[numwindows] = data;
269         videodata->numwindows++;
270     } else {
271         windowlist =
272             (SDL_WindowData **) SDL_realloc(windowlist,
273                                             (numwindows +
274                                              1) * sizeof(*windowlist));
275         if (!windowlist) {
276             SDL_free(data);
277             return SDL_OutOfMemory();
278         }
279         windowlist[numwindows] = data;
280         videodata->numwindows++;
281         videodata->windowlistlength++;
282         videodata->windowlist = windowlist;
283     }
284 
285     /* Fill in the SDL window with the window data */
286     {
287         XWindowAttributes attrib;
288 
289         X11_XGetWindowAttributes(data->videodata->display, w, &attrib);
290         window->x = attrib.x;
291         window->y = attrib.y;
292         window->w = attrib.width;
293         window->h = attrib.height;
294         if (attrib.map_state != IsUnmapped) {
295             window->flags |= SDL_WINDOW_SHOWN;
296         } else {
297             window->flags &= ~SDL_WINDOW_SHOWN;
298         }
299         data->visual = attrib.visual;
300         data->colormap = attrib.colormap;
301     }
302 
303     window->flags |= X11_GetNetWMState(_this, w);
304 
305     {
306         Window FocalWindow;
307         int RevertTo=0;
308         X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo);
309         if (FocalWindow==w)
310         {
311             window->flags |= SDL_WINDOW_INPUT_FOCUS;
312         }
313 
314         if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
315             SDL_SetKeyboardFocus(data->window);
316         }
317 
318         if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
319             /* Tell x11 to clip mouse */
320         }
321     }
322 
323     /* All done! */
324     window->driverdata = data;
325     return 0;
326 }
327 
328 static void
SetWindowBordered(Display * display,int screen,Window window,SDL_bool border)329 SetWindowBordered(Display *display, int screen, Window window, SDL_bool border)
330 {
331     /*
332      * this code used to check for KWM_WIN_DECORATION, but KDE hasn't
333      *  supported it for years and years. It now respects _MOTIF_WM_HINTS.
334      *  Gnome is similar: just use the Motif atom.
335      */
336 
337     Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True);
338     if (WM_HINTS != None) {
339         /* Hints used by Motif compliant window managers */
340         struct
341         {
342             unsigned long flags;
343             unsigned long functions;
344             unsigned long decorations;
345             long input_mode;
346             unsigned long status;
347         } MWMHints = {
348             (1L << 1), 0, border ? 1 : 0, 0, 0
349         };
350 
351         X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32,
352                         PropModeReplace, (unsigned char *) &MWMHints,
353                         sizeof(MWMHints) / sizeof(long));
354     } else {  /* set the transient hints instead, if necessary */
355         X11_XSetTransientForHint(display, window, RootWindow(display, screen));
356     }
357 }
358 
359 int
X11_CreateWindow(_THIS,SDL_Window * window)360 X11_CreateWindow(_THIS, SDL_Window * window)
361 {
362     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
363     SDL_DisplayData *displaydata =
364         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
365     SDL_WindowData *windowdata;
366     Display *display = data->display;
367     int screen = displaydata->screen;
368     Visual *visual;
369     int depth;
370     XSetWindowAttributes xattr;
371     Window w;
372     XSizeHints *sizehints;
373     XWMHints *wmhints;
374     XClassHint *classhints;
375     Atom _NET_WM_BYPASS_COMPOSITOR;
376     Atom _NET_WM_WINDOW_TYPE;
377     Atom wintype;
378     const char *wintype_name = NULL;
379     int compositor = 1;
380     Atom _NET_WM_PID;
381     Atom XdndAware, xdnd_version = 5;
382     long fevent = 0;
383 
384 #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_EGL
385     if ((window->flags & SDL_WINDOW_OPENGL) &&
386         !SDL_getenv("SDL_VIDEO_X11_VISUALID")) {
387         XVisualInfo *vinfo = NULL;
388 
389 #if SDL_VIDEO_OPENGL_EGL
390         if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
391 #if SDL_VIDEO_OPENGL_GLX
392             && ( !_this->gl_data || ! _this->gl_data->HAS_GLX_EXT_create_context_es2_profile )
393 #endif
394         ) {
395             vinfo = X11_GLES_GetVisual(_this, display, screen);
396         } else
397 #endif
398         {
399 #if SDL_VIDEO_OPENGL_GLX
400             vinfo = X11_GL_GetVisual(_this, display, screen);
401 #endif
402         }
403 
404         if (!vinfo) {
405             return -1;
406         }
407         visual = vinfo->visual;
408         depth = vinfo->depth;
409         X11_XFree(vinfo);
410     } else
411 #endif
412     {
413         visual = displaydata->visual;
414         depth = displaydata->depth;
415     }
416 
417     xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU)) ? True : False;
418     xattr.background_pixmap = None;
419     xattr.border_pixel = 0;
420 
421     if (visual->class == DirectColor) {
422         XColor *colorcells;
423         int i;
424         int ncolors;
425         int rmax, gmax, bmax;
426         int rmask, gmask, bmask;
427         int rshift, gshift, bshift;
428 
429         xattr.colormap =
430             X11_XCreateColormap(display, RootWindow(display, screen),
431                             visual, AllocAll);
432 
433         /* If we can't create a colormap, then we must die */
434         if (!xattr.colormap) {
435             return SDL_SetError("Could not create writable colormap");
436         }
437 
438         /* OK, we got a colormap, now fill it in as best as we can */
439         colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
440         if (!colorcells) {
441             return SDL_OutOfMemory();
442         }
443         ncolors = visual->map_entries;
444         rmax = 0xffff;
445         gmax = 0xffff;
446         bmax = 0xffff;
447 
448         rshift = 0;
449         rmask = visual->red_mask;
450         while (0 == (rmask & 1)) {
451             rshift++;
452             rmask >>= 1;
453         }
454 
455         gshift = 0;
456         gmask = visual->green_mask;
457         while (0 == (gmask & 1)) {
458             gshift++;
459             gmask >>= 1;
460         }
461 
462         bshift = 0;
463         bmask = visual->blue_mask;
464         while (0 == (bmask & 1)) {
465             bshift++;
466             bmask >>= 1;
467         }
468 
469         /* build the color table pixel values */
470         for (i = 0; i < ncolors; i++) {
471             Uint32 red = (rmax * i) / (ncolors - 1);
472             Uint32 green = (gmax * i) / (ncolors - 1);
473             Uint32 blue = (bmax * i) / (ncolors - 1);
474 
475             Uint32 rbits = (rmask * i) / (ncolors - 1);
476             Uint32 gbits = (gmask * i) / (ncolors - 1);
477             Uint32 bbits = (bmask * i) / (ncolors - 1);
478 
479             Uint32 pix =
480                 (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
481 
482             colorcells[i].pixel = pix;
483 
484             colorcells[i].red = red;
485             colorcells[i].green = green;
486             colorcells[i].blue = blue;
487 
488             colorcells[i].flags = DoRed | DoGreen | DoBlue;
489         }
490 
491         X11_XStoreColors(display, xattr.colormap, colorcells, ncolors);
492 
493         SDL_free(colorcells);
494     } else {
495         xattr.colormap =
496             X11_XCreateColormap(display, RootWindow(display, screen),
497                             visual, AllocNone);
498     }
499 
500     w = X11_XCreateWindow(display, RootWindow(display, screen),
501                       window->x, window->y, window->w, window->h,
502                       0, depth, InputOutput, visual,
503                       (CWOverrideRedirect | CWBackPixmap | CWBorderPixel |
504                        CWColormap), &xattr);
505     if (!w) {
506         return SDL_SetError("Couldn't create window");
507     }
508 
509     SetWindowBordered(display, screen, w,
510                       (window->flags & SDL_WINDOW_BORDERLESS) == 0);
511 
512     sizehints = X11_XAllocSizeHints();
513     /* Setup the normal size hints */
514     sizehints->flags = 0;
515     if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
516         sizehints->min_width = sizehints->max_width = window->w;
517         sizehints->min_height = sizehints->max_height = window->h;
518         sizehints->flags |= (PMaxSize | PMinSize);
519     }
520     sizehints->x = window->x;
521     sizehints->y = window->y;
522     sizehints->flags |= USPosition;
523 
524     /* Setup the input hints so we get keyboard input */
525     wmhints = X11_XAllocWMHints();
526     wmhints->input = True;
527     wmhints->window_group = data->window_group;
528     wmhints->flags = InputHint | WindowGroupHint;
529 
530     /* Setup the class hints so we can get an icon (AfterStep) */
531     classhints = X11_XAllocClassHint();
532     classhints->res_name = data->classname;
533     classhints->res_class = data->classname;
534 
535     /* Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME */
536     X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints);
537 
538     X11_XFree(sizehints);
539     X11_XFree(wmhints);
540     X11_XFree(classhints);
541     /* Set the PID related to the window for the given hostname, if possible */
542     if (data->pid > 0) {
543         long pid = (long) data->pid;
544         _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False);
545         X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace,
546                         (unsigned char *) &pid, 1);
547     }
548 
549     /* Set the window manager state */
550     X11_SetNetWMState(_this, w, window->flags);
551 
552     compositor = 2;  /* don't disable compositing except for "normal" windows */
553 
554     if (window->flags & SDL_WINDOW_UTILITY) {
555         wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY";
556     } else if (window->flags & SDL_WINDOW_TOOLTIP) {
557         wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP";
558     } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
559         wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU";
560     } else {
561         wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL";
562         compositor = 1;  /* disable compositing for "normal" windows */
563     }
564 
565     /* Let the window manager know what type of window we are. */
566     _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
567     wintype = X11_XInternAtom(display, wintype_name, False);
568     X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
569                     PropModeReplace, (unsigned char *)&wintype, 1);
570 
571     _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
572     X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
573                     PropModeReplace,
574                     (unsigned char *)&compositor, 1);
575 
576     {
577         Atom protocols[3];
578         int proto_count = 0;
579 
580         protocols[proto_count++] = data->WM_DELETE_WINDOW; /* Allow window to be deleted by the WM */
581         protocols[proto_count++] = data->WM_TAKE_FOCUS; /* Since we will want to set input focus explicitly */
582 
583         /* Default to using ping if there is no hint */
584         if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, SDL_TRUE)) {
585             protocols[proto_count++] = data->_NET_WM_PING; /* Respond so WM knows we're alive */
586         }
587 
588         SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0]));
589 
590         X11_XSetWMProtocols(display, w, protocols, proto_count);
591     }
592 
593     if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
594         X11_XDestroyWindow(display, w);
595         return -1;
596     }
597     windowdata = (SDL_WindowData *) window->driverdata;
598 
599 #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
600     if ((window->flags & SDL_WINDOW_OPENGL) &&
601         _this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
602 #if SDL_VIDEO_OPENGL_GLX
603         && ( !_this->gl_data || ! _this->gl_data->HAS_GLX_EXT_create_context_es2_profile )
604 #endif
605     ) {
606 #if SDL_VIDEO_OPENGL_EGL
607         if (!_this->egl_data) {
608             X11_XDestroyWindow(display, w);
609             return -1;
610         }
611 
612         /* Create the GLES window surface */
613         windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) w);
614 
615         if (windowdata->egl_surface == EGL_NO_SURFACE) {
616             X11_XDestroyWindow(display, w);
617             return SDL_SetError("Could not create GLES window surface");
618         }
619 #else
620         return SDL_SetError("Could not create GLES window surface (no EGL support available)");
621 #endif /* SDL_VIDEO_OPENGL_EGL */
622     }
623 #endif
624 
625 
626 #ifdef X_HAVE_UTF8_STRING
627     if (SDL_X11_HAVE_UTF8 && windowdata->ic) {
628         X11_XGetICValues(windowdata->ic, XNFilterEvents, &fevent, NULL);
629     }
630 #endif
631 
632     X11_Xinput2SelectTouch(_this, window);
633 
634     X11_XSelectInput(display, w,
635                  (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
636                  ExposureMask | ButtonPressMask | ButtonReleaseMask |
637                  PointerMotionMask | KeyPressMask | KeyReleaseMask |
638                  PropertyChangeMask | StructureNotifyMask |
639                  KeymapStateMask | fevent));
640 
641     XdndAware = X11_XInternAtom(display, "XdndAware", False);
642     X11_XChangeProperty(display, w, XdndAware, XA_ATOM, 32,
643                  PropModeReplace,
644                  (unsigned char*)&xdnd_version, 1);
645 
646     X11_XFlush(display);
647 
648     return 0;
649 }
650 
651 int
X11_CreateWindowFrom(_THIS,SDL_Window * window,const void * data)652 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
653 {
654     Window w = (Window) data;
655 
656     window->title = X11_GetWindowTitle(_this, w);
657 
658     if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
659         return -1;
660     }
661     return 0;
662 }
663 
664 char *
X11_GetWindowTitle(_THIS,Window xwindow)665 X11_GetWindowTitle(_THIS, Window xwindow)
666 {
667     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
668     Display *display = data->display;
669     int status, real_format;
670     Atom real_type;
671     unsigned long items_read, items_left;
672     unsigned char *propdata;
673     char *title = NULL;
674 
675     status = X11_XGetWindowProperty(display, xwindow, data->_NET_WM_NAME,
676                 0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format,
677                 &items_read, &items_left, &propdata);
678     if (status == Success && propdata) {
679         title = SDL_strdup(SDL_static_cast(char*, propdata));
680         X11_XFree(propdata);
681     } else {
682         status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME,
683                     0L, 8192L, False, XA_STRING, &real_type, &real_format,
684                     &items_read, &items_left, &propdata);
685         if (status == Success && propdata) {
686             title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1);
687             X11_XFree(propdata);
688         } else {
689             title = SDL_strdup("");
690         }
691     }
692     return title;
693 }
694 
695 void
X11_SetWindowTitle(_THIS,SDL_Window * window)696 X11_SetWindowTitle(_THIS, SDL_Window * window)
697 {
698     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
699     Display *display = data->videodata->display;
700     XTextProperty titleprop;
701     Status status;
702     const char *title = window->title ? window->title : "";
703     char *title_locale = NULL;
704 
705 #ifdef X_HAVE_UTF8_STRING
706     Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME;
707 #endif
708 
709     title_locale = SDL_iconv_utf8_locale(title);
710     if (!title_locale) {
711         SDL_OutOfMemory();
712         return;
713     }
714 
715     status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
716     SDL_free(title_locale);
717     if (status) {
718         X11_XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME);
719         X11_XFree(titleprop.value);
720     }
721 #ifdef X_HAVE_UTF8_STRING
722     if (SDL_X11_HAVE_UTF8) {
723         status = X11_Xutf8TextListToTextProperty(display, (char **) &title, 1,
724                                             XUTF8StringStyle, &titleprop);
725         if (status == Success) {
726             X11_XSetTextProperty(display, data->xwindow, &titleprop,
727                                  _NET_WM_NAME);
728             X11_XFree(titleprop.value);
729         }
730     }
731 #endif
732 
733     X11_XFlush(display);
734 }
735 
736 void
X11_SetWindowIcon(_THIS,SDL_Window * window,SDL_Surface * icon)737 X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
738 {
739     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
740     Display *display = data->videodata->display;
741     Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON;
742 
743     if (icon) {
744         int propsize;
745         long *propdata;
746 
747         /* Set the _NET_WM_ICON property */
748         SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888);
749         propsize = 2 + (icon->w * icon->h);
750         propdata = SDL_malloc(propsize * sizeof(long));
751         if (propdata) {
752             int x, y;
753             Uint32 *src;
754             long *dst;
755 
756             propdata[0] = icon->w;
757             propdata[1] = icon->h;
758             dst = &propdata[2];
759             for (y = 0; y < icon->h; ++y) {
760                 src = (Uint32*)((Uint8*)icon->pixels + y * icon->pitch);
761                 for (x = 0; x < icon->w; ++x) {
762                     *dst++ = *src++;
763                 }
764             }
765             X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
766                             32, PropModeReplace, (unsigned char *) propdata,
767                             propsize);
768         }
769         SDL_free(propdata);
770     } else {
771         X11_XDeleteProperty(display, data->xwindow, _NET_WM_ICON);
772     }
773     X11_XFlush(display);
774 }
775 
776 void
X11_SetWindowPosition(_THIS,SDL_Window * window)777 X11_SetWindowPosition(_THIS, SDL_Window * window)
778 {
779     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
780     Display *display = data->videodata->display;
781 
782     X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
783     X11_XFlush(display);
784 }
785 
786 void
X11_SetWindowMinimumSize(_THIS,SDL_Window * window)787 X11_SetWindowMinimumSize(_THIS, SDL_Window * window)
788 {
789     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
790     Display *display = data->videodata->display;
791 
792     if (window->flags & SDL_WINDOW_RESIZABLE) {
793          XSizeHints *sizehints = X11_XAllocSizeHints();
794          long userhints;
795 
796          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
797 
798          sizehints->min_width = window->min_w;
799          sizehints->min_height = window->min_h;
800          sizehints->flags |= PMinSize;
801 
802          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
803 
804          X11_XFree(sizehints);
805 
806         /* See comment in X11_SetWindowSize. */
807         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
808         X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
809         X11_XRaiseWindow(display, data->xwindow);
810     }
811 
812     X11_XFlush(display);
813 }
814 
815 void
X11_SetWindowMaximumSize(_THIS,SDL_Window * window)816 X11_SetWindowMaximumSize(_THIS, SDL_Window * window)
817 {
818     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
819     Display *display = data->videodata->display;
820 
821     if (window->flags & SDL_WINDOW_RESIZABLE) {
822          XSizeHints *sizehints = X11_XAllocSizeHints();
823          long userhints;
824 
825          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
826 
827          sizehints->max_width = window->max_w;
828          sizehints->max_height = window->max_h;
829          sizehints->flags |= PMaxSize;
830 
831          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
832 
833          X11_XFree(sizehints);
834 
835         /* See comment in X11_SetWindowSize. */
836         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
837         X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
838         X11_XRaiseWindow(display, data->xwindow);
839     }
840 
841     X11_XFlush(display);
842 }
843 
844 void
X11_SetWindowSize(_THIS,SDL_Window * window)845 X11_SetWindowSize(_THIS, SDL_Window * window)
846 {
847     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
848     Display *display = data->videodata->display;
849 
850     if (SDL_IsShapedWindow(window)) {
851         X11_ResizeWindowShape(window);
852     }
853     if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
854          /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
855             we must set the size hints to adjust the window size. */
856          XSizeHints *sizehints = X11_XAllocSizeHints();
857          long userhints;
858 
859          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
860 
861          sizehints->min_width = sizehints->max_width = window->w;
862          sizehints->min_height = sizehints->max_height = window->h;
863          sizehints->flags |= PMinSize | PMaxSize;
864 
865          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
866 
867          X11_XFree(sizehints);
868 
869         /* From Pierre-Loup:
870            WMs each have their little quirks with that.  When you change the
871            size hints, they get a ConfigureNotify event with the
872            WM_NORMAL_SIZE_HINTS Atom.  They all save the hints then, but they
873            don't all resize the window right away to enforce the new hints.
874 
875            Some of them resize only after:
876             - A user-initiated move or resize
877             - A code-initiated move or resize
878             - Hiding & showing window (Unmap & map)
879 
880            The following move & resize seems to help a lot of WMs that didn't
881            properly update after the hints were changed. We don't do a
882            hide/show, because there are supposedly subtle problems with doing so
883            and transitioning from windowed to fullscreen in Unity.
884          */
885         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
886         X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
887         X11_XRaiseWindow(display, data->xwindow);
888     } else {
889         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
890     }
891 
892     X11_XFlush(display);
893 }
894 
895 int
X11_GetWindowBordersSize(_THIS,SDL_Window * window,int * top,int * left,int * bottom,int * right)896 X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right)
897 {
898     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
899 
900     *left = data->border_left;
901     *right = data->border_right;
902     *top = data->border_top;
903     *bottom = data->border_bottom;
904 
905     return 0;
906 }
907 
908 int
X11_SetWindowOpacity(_THIS,SDL_Window * window,float opacity)909 X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
910 {
911     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
912     Display *display = data->videodata->display;
913     Atom _NET_WM_WINDOW_OPACITY = data->videodata->_NET_WM_WINDOW_OPACITY;
914 
915     if (opacity == 1.0f) {
916         X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY);
917     } else  {
918         const Uint32 FullyOpaque = 0xFFFFFFFF;
919         const long alpha = (long) ((double)opacity * (double)FullyOpaque);
920         X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
921             PropModeReplace, (unsigned char *)&alpha, 1);
922     }
923 
924     return 0;
925 }
926 
927 int
X11_SetWindowModalFor(_THIS,SDL_Window * modal_window,SDL_Window * parent_window)928 X11_SetWindowModalFor(_THIS, SDL_Window * modal_window, SDL_Window * parent_window) {
929     SDL_WindowData *data = (SDL_WindowData *) modal_window->driverdata;
930     SDL_WindowData *parent_data = (SDL_WindowData *) parent_window->driverdata;
931     Display *display = data->videodata->display;
932 
933     X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
934     return 0;
935 }
936 
937 int
X11_SetWindowInputFocus(_THIS,SDL_Window * window)938 X11_SetWindowInputFocus(_THIS, SDL_Window * window)
939 {
940     if (X11_IsWindowMapped(_this, window)) {
941         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
942         Display *display = data->videodata->display;
943         X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
944         X11_XFlush(display);
945         return 0;
946     }
947     return -1;
948 }
949 
950 void
X11_SetWindowBordered(_THIS,SDL_Window * window,SDL_bool bordered)951 X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
952 {
953     const SDL_bool focused = ((window->flags & SDL_WINDOW_INPUT_FOCUS) != 0);
954     const SDL_bool visible = ((window->flags & SDL_WINDOW_HIDDEN) == 0);
955     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
956     SDL_DisplayData *displaydata =
957         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
958     Display *display = data->videodata->display;
959     XEvent event;
960 
961     SetWindowBordered(display, displaydata->screen, data->xwindow, bordered);
962     X11_XFlush(display);
963 
964     if (visible) {
965         XWindowAttributes attr;
966         do {
967             X11_XSync(display, False);
968             X11_XGetWindowAttributes(display, data->xwindow, &attr);
969         } while (attr.map_state != IsViewable);
970 
971         if (focused) {
972             X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime);
973         }
974     }
975 
976     /* make sure these don't make it to the real event queue if they fired here. */
977     X11_XSync(display, False);
978     X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
979     X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
980 }
981 
982 void
X11_SetWindowResizable(_THIS,SDL_Window * window,SDL_bool resizable)983 X11_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
984 {
985     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
986     Display *display = data->videodata->display;
987 
988     XSizeHints *sizehints = X11_XAllocSizeHints();
989     long userhints;
990 
991     X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
992 
993     if (resizable) {
994         /* FIXME: Is there a better way to get max window size from X? -flibit */
995         const int maxsize = 0x7FFFFFFF;
996         sizehints->min_width = window->min_w;
997         sizehints->min_height = window->min_h;
998         sizehints->max_width = (window->max_w == 0) ? maxsize : window->max_w;
999         sizehints->max_height = (window->max_h == 0) ? maxsize : window->max_h;
1000     } else {
1001         sizehints->min_width = window->w;
1002         sizehints->min_height = window->h;
1003         sizehints->max_width = window->w;
1004         sizehints->max_height = window->h;
1005     }
1006     sizehints->flags |= PMinSize | PMaxSize;
1007 
1008     X11_XSetWMNormalHints(display, data->xwindow, sizehints);
1009 
1010     X11_XFree(sizehints);
1011 
1012     /* See comment in X11_SetWindowSize. */
1013     X11_XResizeWindow(display, data->xwindow, window->w, window->h);
1014     X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
1015     X11_XRaiseWindow(display, data->xwindow);
1016 
1017     X11_XFlush(display);
1018 }
1019 
1020 void
X11_ShowWindow(_THIS,SDL_Window * window)1021 X11_ShowWindow(_THIS, SDL_Window * window)
1022 {
1023     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1024     Display *display = data->videodata->display;
1025     XEvent event;
1026 
1027     if (!X11_IsWindowMapped(_this, window)) {
1028         X11_XMapRaised(display, data->xwindow);
1029         /* Blocking wait for "MapNotify" event.
1030          * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
1031          * and XCheckTypedWindowEvent doesn't block */
1032         X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
1033         X11_XFlush(display);
1034     }
1035 
1036     if (!data->videodata->net_wm) {
1037         /* no WM means no FocusIn event, which confuses us. Force it. */
1038         X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
1039         X11_XFlush(display);
1040     }
1041 }
1042 
1043 void
X11_HideWindow(_THIS,SDL_Window * window)1044 X11_HideWindow(_THIS, SDL_Window * window)
1045 {
1046     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1047     SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
1048     Display *display = data->videodata->display;
1049     XEvent event;
1050 
1051     if (X11_IsWindowMapped(_this, window)) {
1052         X11_XWithdrawWindow(display, data->xwindow, displaydata->screen);
1053         /* Blocking wait for "UnmapNotify" event */
1054         X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
1055         X11_XFlush(display);
1056     }
1057 }
1058 
1059 static void
SetWindowActive(_THIS,SDL_Window * window)1060 SetWindowActive(_THIS, SDL_Window * window)
1061 {
1062     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1063     SDL_DisplayData *displaydata =
1064         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
1065     Display *display = data->videodata->display;
1066     Atom _NET_ACTIVE_WINDOW = data->videodata->_NET_ACTIVE_WINDOW;
1067 
1068     if (X11_IsWindowMapped(_this, window)) {
1069         XEvent e;
1070 
1071         /*printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);*/
1072 
1073         SDL_zero(e);
1074         e.xany.type = ClientMessage;
1075         e.xclient.message_type = _NET_ACTIVE_WINDOW;
1076         e.xclient.format = 32;
1077         e.xclient.window = data->xwindow;
1078         e.xclient.data.l[0] = 1;  /* source indication. 1 = application */
1079         e.xclient.data.l[1] = data->user_time;
1080         e.xclient.data.l[2] = 0;
1081 
1082         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1083                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
1084 
1085         X11_XFlush(display);
1086     }
1087 }
1088 
1089 void
X11_RaiseWindow(_THIS,SDL_Window * window)1090 X11_RaiseWindow(_THIS, SDL_Window * window)
1091 {
1092     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1093     Display *display = data->videodata->display;
1094 
1095     X11_XRaiseWindow(display, data->xwindow);
1096     SetWindowActive(_this, window);
1097     X11_XFlush(display);
1098 }
1099 
1100 static void
SetWindowMaximized(_THIS,SDL_Window * window,SDL_bool maximized)1101 SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized)
1102 {
1103     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1104     SDL_DisplayData *displaydata =
1105         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
1106     Display *display = data->videodata->display;
1107     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
1108     Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
1109     Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
1110 
1111     if (maximized) {
1112         window->flags |= SDL_WINDOW_MAXIMIZED;
1113     } else {
1114         window->flags &= ~SDL_WINDOW_MAXIMIZED;
1115     }
1116 
1117     if (X11_IsWindowMapped(_this, window)) {
1118         XEvent e;
1119 
1120         SDL_zero(e);
1121         e.xany.type = ClientMessage;
1122         e.xclient.message_type = _NET_WM_STATE;
1123         e.xclient.format = 32;
1124         e.xclient.window = data->xwindow;
1125         e.xclient.data.l[0] =
1126             maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1127         e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
1128         e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
1129         e.xclient.data.l[3] = 0l;
1130 
1131         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1132                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
1133     } else {
1134         X11_SetNetWMState(_this, data->xwindow, window->flags);
1135     }
1136     X11_XFlush(display);
1137 }
1138 
1139 void
X11_MaximizeWindow(_THIS,SDL_Window * window)1140 X11_MaximizeWindow(_THIS, SDL_Window * window)
1141 {
1142     SetWindowMaximized(_this, window, SDL_TRUE);
1143 }
1144 
1145 void
X11_MinimizeWindow(_THIS,SDL_Window * window)1146 X11_MinimizeWindow(_THIS, SDL_Window * window)
1147 {
1148     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1149     SDL_DisplayData *displaydata =
1150         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
1151     Display *display = data->videodata->display;
1152 
1153     X11_XIconifyWindow(display, data->xwindow, displaydata->screen);
1154     X11_XFlush(display);
1155 }
1156 
1157 void
X11_RestoreWindow(_THIS,SDL_Window * window)1158 X11_RestoreWindow(_THIS, SDL_Window * window)
1159 {
1160     SetWindowMaximized(_this, window, SDL_FALSE);
1161     X11_ShowWindow(_this, window);
1162     SetWindowActive(_this, window);
1163 }
1164 
1165 /* This asks the Window Manager to handle fullscreen for us. This is the modern way. */
1166 static void
X11_SetWindowFullscreenViaWM(_THIS,SDL_Window * window,SDL_VideoDisplay * _display,SDL_bool fullscreen)1167 X11_SetWindowFullscreenViaWM(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen)
1168 {
1169     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1170     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
1171     Display *display = data->videodata->display;
1172     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
1173     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
1174 
1175     if (X11_IsWindowMapped(_this, window)) {
1176         XEvent e;
1177 
1178         if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
1179             /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we
1180                can be resized to the fullscreen resolution (or reset so we're not resizable again) */
1181             XSizeHints *sizehints = X11_XAllocSizeHints();
1182             long flags = 0;
1183             X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags);
1184             /* set the resize flags on */
1185             if (fullscreen) {
1186                 /* we are going fullscreen so turn the flags off */
1187                 sizehints->flags &= ~(PMinSize | PMaxSize);
1188             } else {
1189                 /* Reset the min/max width height to make the window non-resizable again */
1190                 sizehints->flags |= PMinSize | PMaxSize;
1191                 sizehints->min_width = sizehints->max_width = window->windowed.w;
1192                 sizehints->min_height = sizehints->max_height = window->windowed.h;
1193             }
1194             X11_XSetWMNormalHints(display, data->xwindow, sizehints);
1195             X11_XFree(sizehints);
1196         }
1197 
1198         SDL_zero(e);
1199         e.xany.type = ClientMessage;
1200         e.xclient.message_type = _NET_WM_STATE;
1201         e.xclient.format = 32;
1202         e.xclient.window = data->xwindow;
1203         e.xclient.data.l[0] =
1204             fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
1205         e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
1206         e.xclient.data.l[3] = 0l;
1207 
1208         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1209                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
1210 
1211         /* Fullscreen windows sometimes end up being marked maximized by
1212             window managers. Force it back to how we expect it to be. */
1213         if (!fullscreen && ((window->flags & SDL_WINDOW_MAXIMIZED) == 0)) {
1214             SDL_zero(e);
1215             e.xany.type = ClientMessage;
1216             e.xclient.message_type = _NET_WM_STATE;
1217             e.xclient.format = 32;
1218             e.xclient.window = data->xwindow;
1219             e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
1220             e.xclient.data.l[1] = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
1221             e.xclient.data.l[2] = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
1222             e.xclient.data.l[3] = 0l;
1223             X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
1224                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
1225         }
1226     } else {
1227         Uint32 flags;
1228 
1229         flags = window->flags;
1230         if (fullscreen) {
1231             flags |= SDL_WINDOW_FULLSCREEN;
1232         } else {
1233             flags &= ~SDL_WINDOW_FULLSCREEN;
1234         }
1235         X11_SetNetWMState(_this, data->xwindow, flags);
1236     }
1237 
1238     if (data->visual->class == DirectColor) {
1239         if ( fullscreen ) {
1240             X11_XInstallColormap(display, data->colormap);
1241         } else {
1242             X11_XUninstallColormap(display, data->colormap);
1243         }
1244     }
1245 
1246     X11_XFlush(display);
1247 }
1248 
1249 /* This handles fullscreen itself, outside the Window Manager. */
1250 static void
X11_BeginWindowFullscreenLegacy(_THIS,SDL_Window * window,SDL_VideoDisplay * _display)1251 X11_BeginWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display)
1252 {
1253     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1254     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
1255     Visual *visual = data->visual;
1256     Display *display = data->videodata->display;
1257     const int screen = displaydata->screen;
1258     Window root = RootWindow(display, screen);
1259     const int def_vis = (visual == DefaultVisual(display, screen));
1260     unsigned long xattrmask = 0;
1261     XSetWindowAttributes xattr;
1262     XEvent ev;
1263     SDL_Rect rect;
1264 
1265     if ( data->fswindow ) {
1266         return;  /* already fullscreen, I hope. */
1267     }
1268 
1269     X11_GetDisplayBounds(_this, _display, &rect);
1270 
1271     SDL_zero(xattr);
1272     xattr.override_redirect = True;
1273     xattrmask |= CWOverrideRedirect;
1274     xattr.background_pixel = def_vis ? BlackPixel(display, screen) : 0;
1275     xattrmask |= CWBackPixel;
1276     xattr.border_pixel = 0;
1277     xattrmask |= CWBorderPixel;
1278     xattr.colormap = data->colormap;
1279     xattrmask |= CWColormap;
1280 
1281     data->fswindow = X11_XCreateWindow(display, root,
1282                                    rect.x, rect.y, rect.w, rect.h, 0,
1283                                    displaydata->depth, InputOutput,
1284                                    visual, xattrmask, &xattr);
1285 
1286     X11_XSelectInput(display, data->fswindow, StructureNotifyMask);
1287     X11_XSetWindowBackground(display, data->fswindow, 0);
1288     X11_XInstallColormap(display, data->colormap);
1289     X11_XClearWindow(display, data->fswindow);
1290     X11_XMapRaised(display, data->fswindow);
1291 
1292     /* Make sure the fswindow is in view by warping mouse to the corner */
1293     X11_XUngrabPointer(display, CurrentTime);
1294     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
1295 
1296     /* Wait to be mapped, filter Unmap event out if it arrives. */
1297     X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->fswindow);
1298     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->fswindow);
1299 
1300 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
1301     if ( displaydata->use_vidmode ) {
1302         X11_XF86VidModeLockModeSwitch(display, screen, True);
1303     }
1304 #endif
1305 
1306     SetWindowBordered(display, displaydata->screen, data->xwindow, SDL_FALSE);
1307 
1308     /* Center actual window within our cover-the-screen window. */
1309     X11_XReparentWindow(display, data->xwindow, data->fswindow,
1310                     (rect.w - window->w) / 2, (rect.h - window->h) / 2);
1311 
1312     /* Move the mouse to the upper left to make sure it's on-screen */
1313     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
1314 
1315     /* Center mouse in the fullscreen window. */
1316     rect.x += (rect.w / 2);
1317     rect.y += (rect.h / 2);
1318     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
1319 
1320     /* Wait to be mapped, filter Unmap event out if it arrives. */
1321     X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow);
1322     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow);
1323 
1324     SDL_UpdateWindowGrab(window);
1325 }
1326 
1327 static void
X11_EndWindowFullscreenLegacy(_THIS,SDL_Window * window,SDL_VideoDisplay * _display)1328 X11_EndWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display)
1329 {
1330     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1331     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
1332     Display *display = data->videodata->display;
1333     const int screen = displaydata->screen;
1334     Window root = RootWindow(display, screen);
1335     Window fswindow = data->fswindow;
1336     XEvent ev;
1337 
1338     if (!data->fswindow) {
1339         return;  /* already not fullscreen, I hope. */
1340     }
1341 
1342     data->fswindow = None;
1343 
1344 #if SDL_VIDEO_DRIVER_X11_VIDMODE
1345     if ( displaydata->use_vidmode ) {
1346         X11_XF86VidModeLockModeSwitch(display, screen, False);
1347     }
1348 #endif
1349 
1350     SDL_UpdateWindowGrab(window);
1351 
1352     X11_XReparentWindow(display, data->xwindow, root, window->x, window->y);
1353 
1354     /* flush these events so they don't confuse normal event handling */
1355     X11_XSync(display, False);
1356     X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow);
1357     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow);
1358 
1359     SetWindowBordered(display, screen, data->xwindow,
1360                       (window->flags & SDL_WINDOW_BORDERLESS) == 0);
1361 
1362     X11_XWithdrawWindow(display, fswindow, screen);
1363 
1364     /* Wait to be unmapped. */
1365     X11_XIfEvent(display, &ev, &isUnmapNotify, (XPointer)&fswindow);
1366     X11_XDestroyWindow(display, fswindow);
1367 }
1368 
1369 
1370 void
X11_SetWindowFullscreen(_THIS,SDL_Window * window,SDL_VideoDisplay * _display,SDL_bool fullscreen)1371 X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen)
1372 {
1373     /* !!! FIXME: SDL_Hint? */
1374     SDL_bool legacy = SDL_FALSE;
1375     const char *env = SDL_getenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN");
1376     if (env) {
1377         legacy = SDL_atoi(env);
1378     } else {
1379         SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
1380         SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
1381         if ( displaydata->use_vidmode ) {
1382             legacy = SDL_TRUE;  /* the new stuff only works with XRandR. */
1383         } else if ( !videodata->net_wm ) {
1384             legacy = SDL_TRUE;  /* The window manager doesn't support it */
1385         } else {
1386             /* !!! FIXME: look at the window manager name, and blacklist certain ones? */
1387             /* http://stackoverflow.com/questions/758648/find-the-name-of-the-x-window-manager */
1388             legacy = SDL_FALSE;  /* try the new way. */
1389         }
1390     }
1391 
1392     if (legacy) {
1393         if (fullscreen) {
1394             X11_BeginWindowFullscreenLegacy(_this, window, _display);
1395         } else {
1396             X11_EndWindowFullscreenLegacy(_this, window, _display);
1397         }
1398     } else {
1399         X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
1400     }
1401 }
1402 
1403 
1404 int
X11_SetWindowGammaRamp(_THIS,SDL_Window * window,const Uint16 * ramp)1405 X11_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
1406 {
1407     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1408     Display *display = data->videodata->display;
1409     Visual *visual = data->visual;
1410     Colormap colormap = data->colormap;
1411     XColor *colorcells;
1412     int ncolors;
1413     int rmask, gmask, bmask;
1414     int rshift, gshift, bshift;
1415     int i;
1416 
1417     if (visual->class != DirectColor) {
1418         return SDL_SetError("Window doesn't have DirectColor visual");
1419     }
1420 
1421     ncolors = visual->map_entries;
1422     colorcells = SDL_malloc(ncolors * sizeof(XColor));
1423     if (!colorcells) {
1424         return SDL_OutOfMemory();
1425     }
1426 
1427     rshift = 0;
1428     rmask = visual->red_mask;
1429     while (0 == (rmask & 1)) {
1430         rshift++;
1431         rmask >>= 1;
1432     }
1433 
1434     gshift = 0;
1435     gmask = visual->green_mask;
1436     while (0 == (gmask & 1)) {
1437         gshift++;
1438         gmask >>= 1;
1439     }
1440 
1441     bshift = 0;
1442     bmask = visual->blue_mask;
1443     while (0 == (bmask & 1)) {
1444         bshift++;
1445         bmask >>= 1;
1446     }
1447 
1448     /* build the color table pixel values */
1449     for (i = 0; i < ncolors; i++) {
1450         Uint32 rbits = (rmask * i) / (ncolors - 1);
1451         Uint32 gbits = (gmask * i) / (ncolors - 1);
1452         Uint32 bbits = (bmask * i) / (ncolors - 1);
1453         Uint32 pix = (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
1454 
1455         colorcells[i].pixel = pix;
1456 
1457         colorcells[i].red = ramp[(0 * 256) + i];
1458         colorcells[i].green = ramp[(1 * 256) + i];
1459         colorcells[i].blue = ramp[(2 * 256) + i];
1460 
1461         colorcells[i].flags = DoRed | DoGreen | DoBlue;
1462     }
1463 
1464     X11_XStoreColors(display, colormap, colorcells, ncolors);
1465     X11_XFlush(display);
1466     SDL_free(colorcells);
1467 
1468     return 0;
1469 }
1470 
1471 void
X11_SetWindowGrab(_THIS,SDL_Window * window,SDL_bool grabbed)1472 X11_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
1473 {
1474     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1475     Display *display = data->videodata->display;
1476     SDL_bool oldstyle_fullscreen;
1477     SDL_bool grab_keyboard;
1478 
1479     /* ICCCM2.0-compliant window managers can handle fullscreen windows
1480        If we're using XVidMode to change resolution we need to confine
1481        the cursor so we don't pan around the virtual desktop.
1482      */
1483     oldstyle_fullscreen = X11_IsWindowLegacyFullscreen(_this, window);
1484 
1485     if (oldstyle_fullscreen || grabbed) {
1486         /* Try to grab the mouse */
1487         for (;;) {
1488             int result =
1489                 X11_XGrabPointer(display, data->xwindow, True, 0, GrabModeAsync,
1490                              GrabModeAsync, data->xwindow, None, CurrentTime);
1491             if (result == GrabSuccess) {
1492                 break;
1493             }
1494             SDL_Delay(50);
1495         }
1496 
1497         /* Raise the window if we grab the mouse */
1498         X11_XRaiseWindow(display, data->xwindow);
1499 
1500         /* Now grab the keyboard */
1501         if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) {
1502             grab_keyboard = SDL_TRUE;
1503         } else {
1504             /* We need to do this with the old style override_redirect
1505                fullscreen window otherwise we won't get keyboard focus.
1506             */
1507             grab_keyboard = oldstyle_fullscreen;
1508         }
1509         if (grab_keyboard) {
1510             X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
1511                           GrabModeAsync, CurrentTime);
1512         }
1513     } else {
1514         X11_XUngrabPointer(display, CurrentTime);
1515         X11_XUngrabKeyboard(display, CurrentTime);
1516     }
1517     X11_XSync(display, False);
1518 }
1519 
1520 void
X11_DestroyWindow(_THIS,SDL_Window * window)1521 X11_DestroyWindow(_THIS, SDL_Window * window)
1522 {
1523     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1524 
1525     if (data) {
1526         SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
1527         Display *display = videodata->display;
1528         int numwindows = videodata->numwindows;
1529         SDL_WindowData **windowlist = videodata->windowlist;
1530         int i;
1531 
1532         if (windowlist) {
1533             for (i = 0; i < numwindows; ++i) {
1534                 if (windowlist[i] && (windowlist[i]->window == window)) {
1535                     windowlist[i] = windowlist[numwindows - 1];
1536                     windowlist[numwindows - 1] = NULL;
1537                     videodata->numwindows--;
1538                     break;
1539                 }
1540             }
1541         }
1542 #ifdef X_HAVE_UTF8_STRING
1543         if (data->ic) {
1544             X11_XDestroyIC(data->ic);
1545         }
1546 #endif
1547         if (data->created) {
1548             X11_XDestroyWindow(display, data->xwindow);
1549             X11_XFlush(display);
1550         }
1551         SDL_free(data);
1552     }
1553     window->driverdata = NULL;
1554 }
1555 
1556 SDL_bool
X11_GetWindowWMInfo(_THIS,SDL_Window * window,SDL_SysWMinfo * info)1557 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
1558 {
1559     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1560     Display *display = data->videodata->display;
1561 
1562     if (info->version.major == SDL_MAJOR_VERSION &&
1563         info->version.minor == SDL_MINOR_VERSION) {
1564         info->subsystem = SDL_SYSWM_X11;
1565         info->info.x11.display = display;
1566         info->info.x11.window = data->xwindow;
1567         return SDL_TRUE;
1568     } else {
1569         SDL_SetError("Application not compiled with SDL %d.%d\n",
1570                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
1571         return SDL_FALSE;
1572     }
1573 }
1574 
1575 int
X11_SetWindowHitTest(SDL_Window * window,SDL_bool enabled)1576 X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
1577 {
1578     return 0;  /* just succeed, the real work is done elsewhere. */
1579 }
1580 
1581 #endif /* SDL_VIDEO_DRIVER_X11 */
1582 
1583 /* vi: set ts=4 sw=4 expandtab: */
1584