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