src/video/x11/SDL_x11window.c
author Ryan C. Gordon
Mon, 17 Feb 2020 15:02:37 -0500
changeset 13534 24e8a27fa627
parent 13533 867352e06641
child 13535 dbcccb065928
permissions -rw-r--r--
x11: Don't delay an extra 10ms if we were just going to break out of the loop.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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     long fevent = 0;
   394 
   395 #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_EGL
   396     const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID);
   397 
   398     if (forced_visual_id != NULL && forced_visual_id[0] != '\0')
   399     {
   400         XVisualInfo *vi, template;
   401         int nvis;
   402 
   403         SDL_zero(template);
   404         template.visualid = SDL_strtol(forced_visual_id, NULL, 0);
   405         vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis);
   406         if (vi) {
   407             visual = vi->visual;
   408             depth = vi->depth;
   409             X11_XFree(vi);
   410         }
   411         else
   412         {
   413             return -1;
   414         }
   415     }
   416     else if ((window->flags & SDL_WINDOW_OPENGL) &&
   417         !SDL_getenv("SDL_VIDEO_X11_VISUALID")) {
   418         XVisualInfo *vinfo = NULL;
   419 
   420 #if SDL_VIDEO_OPENGL_EGL
   421         if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
   422              SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE))
   423 #if SDL_VIDEO_OPENGL_GLX            
   424             && ( !_this->gl_data || X11_GL_UseEGL(_this) )
   425 #endif
   426         ) {
   427             vinfo = X11_GLES_GetVisual(_this, display, screen);
   428         } else
   429 #endif
   430         {
   431 #if SDL_VIDEO_OPENGL_GLX
   432             vinfo = X11_GL_GetVisual(_this, display, screen);
   433 #endif
   434         }
   435 
   436         if (!vinfo) {
   437             return -1;
   438         }
   439         visual = vinfo->visual;
   440         depth = vinfo->depth;
   441         X11_XFree(vinfo);
   442     } else
   443 #endif
   444     {
   445         visual = displaydata->visual;
   446         depth = displaydata->depth;
   447     }
   448 
   449     xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU)) ? True : False;
   450     xattr.background_pixmap = None;
   451     xattr.border_pixel = 0;
   452 
   453     if (visual->class == DirectColor) {
   454         XColor *colorcells;
   455         int i;
   456         int ncolors;
   457         int rmax, gmax, bmax;
   458         int rmask, gmask, bmask;
   459         int rshift, gshift, bshift;
   460 
   461         xattr.colormap =
   462             X11_XCreateColormap(display, RootWindow(display, screen),
   463                             visual, AllocAll);
   464 
   465         /* If we can't create a colormap, then we must die */
   466         if (!xattr.colormap) {
   467             return SDL_SetError("Could not create writable colormap");
   468         }
   469 
   470         /* OK, we got a colormap, now fill it in as best as we can */
   471         colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
   472         if (!colorcells) {
   473             return SDL_OutOfMemory();
   474         }
   475         ncolors = visual->map_entries;
   476         rmax = 0xffff;
   477         gmax = 0xffff;
   478         bmax = 0xffff;
   479 
   480         rshift = 0;
   481         rmask = visual->red_mask;
   482         while (0 == (rmask & 1)) {
   483             rshift++;
   484             rmask >>= 1;
   485         }
   486 
   487         gshift = 0;
   488         gmask = visual->green_mask;
   489         while (0 == (gmask & 1)) {
   490             gshift++;
   491             gmask >>= 1;
   492         }
   493 
   494         bshift = 0;
   495         bmask = visual->blue_mask;
   496         while (0 == (bmask & 1)) {
   497             bshift++;
   498             bmask >>= 1;
   499         }
   500 
   501         /* build the color table pixel values */
   502         for (i = 0; i < ncolors; i++) {
   503             Uint32 red = (rmax * i) / (ncolors - 1);
   504             Uint32 green = (gmax * i) / (ncolors - 1);
   505             Uint32 blue = (bmax * i) / (ncolors - 1);
   506 
   507             Uint32 rbits = (rmask * i) / (ncolors - 1);
   508             Uint32 gbits = (gmask * i) / (ncolors - 1);
   509             Uint32 bbits = (bmask * i) / (ncolors - 1);
   510 
   511             Uint32 pix =
   512                 (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
   513 
   514             colorcells[i].pixel = pix;
   515 
   516             colorcells[i].red = red;
   517             colorcells[i].green = green;
   518             colorcells[i].blue = blue;
   519 
   520             colorcells[i].flags = DoRed | DoGreen | DoBlue;
   521         }
   522 
   523         X11_XStoreColors(display, xattr.colormap, colorcells, ncolors);
   524 
   525         SDL_free(colorcells);
   526     } else {
   527         xattr.colormap =
   528             X11_XCreateColormap(display, RootWindow(display, screen),
   529                             visual, AllocNone);
   530     }
   531 
   532     w = X11_XCreateWindow(display, RootWindow(display, screen),
   533                       window->x, window->y, window->w, window->h,
   534                       0, depth, InputOutput, visual,
   535                       (CWOverrideRedirect | CWBackPixmap | CWBorderPixel |
   536                        CWColormap), &xattr);
   537     if (!w) {
   538         return SDL_SetError("Couldn't create window");
   539     }
   540 
   541     SetWindowBordered(display, screen, w,
   542                       (window->flags & SDL_WINDOW_BORDERLESS) == 0);
   543 
   544     sizehints = X11_XAllocSizeHints();
   545     /* Setup the normal size hints */
   546     sizehints->flags = 0;
   547     if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
   548         sizehints->min_width = sizehints->max_width = window->w;
   549         sizehints->min_height = sizehints->max_height = window->h;
   550         sizehints->flags |= (PMaxSize | PMinSize);
   551     }
   552     sizehints->x = window->x;
   553     sizehints->y = window->y;
   554     sizehints->flags |= USPosition;
   555 
   556     /* Setup the input hints so we get keyboard input */
   557     wmhints = X11_XAllocWMHints();
   558     wmhints->input = True;
   559     wmhints->window_group = data->window_group;
   560     wmhints->flags = InputHint | WindowGroupHint;
   561 
   562     /* Setup the class hints so we can get an icon (AfterStep) */
   563     classhints = X11_XAllocClassHint();
   564     classhints->res_name = data->classname;
   565     classhints->res_class = data->classname;
   566 
   567     /* Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME */
   568     X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints);
   569 
   570     X11_XFree(sizehints);
   571     X11_XFree(wmhints);
   572     X11_XFree(classhints);
   573     /* Set the PID related to the window for the given hostname, if possible */
   574     if (data->pid > 0) {
   575         long pid = (long) data->pid;
   576         _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False);
   577         X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace,
   578                         (unsigned char *) &pid, 1);
   579     }
   580 
   581     /* Set the window manager state */
   582     X11_SetNetWMState(_this, w, window->flags);
   583 
   584     compositor = 2;  /* don't disable compositing except for "normal" windows */
   585 
   586     if (window->flags & SDL_WINDOW_UTILITY) {
   587         wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY";
   588     } else if (window->flags & SDL_WINDOW_TOOLTIP) {
   589         wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP";
   590     } else if (window->flags & SDL_WINDOW_POPUP_MENU) {
   591         wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU";
   592     } else {
   593         wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL";
   594         compositor = 1;  /* disable compositing for "normal" windows */
   595     }
   596 
   597     /* Let the window manager know what type of window we are. */
   598     _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
   599     wintype = X11_XInternAtom(display, wintype_name, False);
   600     X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   601                     PropModeReplace, (unsigned char *)&wintype, 1);
   602     if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, SDL_TRUE)) {
   603         _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False);
   604         X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
   605                         PropModeReplace,
   606                         (unsigned char *)&compositor, 1);
   607     }
   608 
   609     {
   610         Atom protocols[3];
   611         int proto_count = 0;
   612 
   613         protocols[proto_count++] = data->WM_DELETE_WINDOW; /* Allow window to be deleted by the WM */
   614         protocols[proto_count++] = data->WM_TAKE_FOCUS; /* Since we will want to set input focus explicitly */
   615 
   616         /* Default to using ping if there is no hint */
   617         if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, SDL_TRUE)) {
   618             protocols[proto_count++] = data->_NET_WM_PING; /* Respond so WM knows we're alive */
   619         }
   620 
   621         SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0]));
   622 
   623         X11_XSetWMProtocols(display, w, protocols, proto_count);
   624     }
   625 
   626     if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
   627         X11_XDestroyWindow(display, w);
   628         return -1;
   629     }
   630     windowdata = (SDL_WindowData *) window->driverdata;
   631 
   632 #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 || SDL_VIDEO_OPENGL_EGL
   633     if ((window->flags & SDL_WINDOW_OPENGL) && 
   634         ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) ||
   635          SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE))
   636 #if SDL_VIDEO_OPENGL_GLX            
   637         && ( !_this->gl_data || X11_GL_UseEGL(_this) )
   638 #endif  
   639     ) {
   640 #if SDL_VIDEO_OPENGL_EGL  
   641         if (!_this->egl_data) {
   642             return -1;
   643         }
   644 
   645         /* Create the GLES window surface */
   646         windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) w);
   647 
   648         if (windowdata->egl_surface == EGL_NO_SURFACE) {
   649             return SDL_SetError("Could not create GLES window surface");
   650         }
   651 #else
   652         return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
   653 #endif /* SDL_VIDEO_OPENGL_EGL */
   654     }
   655 #endif
   656     
   657 
   658 #ifdef X_HAVE_UTF8_STRING
   659     if (SDL_X11_HAVE_UTF8 && windowdata->ic) {
   660         X11_XGetICValues(windowdata->ic, XNFilterEvents, &fevent, NULL);
   661     }
   662 #endif
   663 
   664     X11_Xinput2SelectTouch(_this, window);
   665 
   666     X11_XSelectInput(display, w,
   667                  (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   668                  ExposureMask | ButtonPressMask | ButtonReleaseMask |
   669                  PointerMotionMask | KeyPressMask | KeyReleaseMask |
   670                  PropertyChangeMask | StructureNotifyMask |
   671                  KeymapStateMask | fevent));
   672 
   673     X11_XFlush(display);
   674 
   675     return 0;
   676 }
   677 
   678 int
   679 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   680 {
   681     Window w = (Window) data;
   682 
   683     window->title = X11_GetWindowTitle(_this, w);
   684 
   685     if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
   686         return -1;
   687     }
   688     return 0;
   689 }
   690 
   691 char *
   692 X11_GetWindowTitle(_THIS, Window xwindow)
   693 {
   694     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   695     Display *display = data->display;
   696     int status, real_format;
   697     Atom real_type;
   698     unsigned long items_read, items_left;
   699     unsigned char *propdata;
   700     char *title = NULL;
   701 
   702     status = X11_XGetWindowProperty(display, xwindow, data->_NET_WM_NAME,
   703                 0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format,
   704                 &items_read, &items_left, &propdata);
   705     if (status == Success && propdata) {
   706         title = SDL_strdup(SDL_static_cast(char*, propdata));
   707         X11_XFree(propdata);
   708     } else {
   709         status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME,
   710                     0L, 8192L, False, XA_STRING, &real_type, &real_format,
   711                     &items_read, &items_left, &propdata);
   712         if (status == Success && propdata) {
   713             title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1);
   714             X11_XFree(propdata);
   715         } else {
   716             title = SDL_strdup("");
   717         }
   718     }
   719     return title;
   720 }
   721 
   722 void
   723 X11_SetWindowTitle(_THIS, SDL_Window * window)
   724 {
   725     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   726     Display *display = data->videodata->display;
   727     XTextProperty titleprop;
   728     Status status;
   729     const char *title = window->title ? window->title : "";
   730     char *title_locale = NULL;
   731 
   732 #ifdef X_HAVE_UTF8_STRING
   733     Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME;
   734 #endif
   735 
   736     title_locale = SDL_iconv_utf8_locale(title);
   737     if (!title_locale) {
   738         SDL_OutOfMemory();
   739         return;
   740     }
   741 
   742     status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
   743     SDL_free(title_locale);
   744     if (status) {
   745         X11_XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME);
   746         X11_XFree(titleprop.value);
   747     }
   748 #ifdef X_HAVE_UTF8_STRING
   749     if (SDL_X11_HAVE_UTF8) {
   750         status = X11_Xutf8TextListToTextProperty(display, (char **) &title, 1,
   751                                             XUTF8StringStyle, &titleprop);
   752         if (status == Success) {
   753             X11_XSetTextProperty(display, data->xwindow, &titleprop,
   754                                  _NET_WM_NAME);
   755             X11_XFree(titleprop.value);
   756         }
   757     }
   758 #endif
   759 
   760     X11_XFlush(display);
   761 }
   762 
   763 void
   764 X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   765 {
   766     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   767     Display *display = data->videodata->display;
   768     Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON;
   769 
   770     if (icon) {
   771         int propsize;
   772         long *propdata;
   773 
   774         /* Set the _NET_WM_ICON property */
   775         SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888);
   776         propsize = 2 + (icon->w * icon->h);
   777         propdata = SDL_malloc(propsize * sizeof(long));
   778         if (propdata) {
   779             int x, y;
   780             Uint32 *src;
   781             long *dst;
   782 
   783             propdata[0] = icon->w;
   784             propdata[1] = icon->h;
   785             dst = &propdata[2];
   786             for (y = 0; y < icon->h; ++y) {
   787                 src = (Uint32*)((Uint8*)icon->pixels + y * icon->pitch);
   788                 for (x = 0; x < icon->w; ++x) {
   789                     *dst++ = *src++;
   790                 }
   791             }
   792             X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
   793                             32, PropModeReplace, (unsigned char *) propdata,
   794                             propsize);
   795         }
   796         SDL_free(propdata);
   797     } else {
   798         X11_XDeleteProperty(display, data->xwindow, _NET_WM_ICON);
   799     }
   800     X11_XFlush(display);
   801 }
   802 
   803 void
   804 X11_SetWindowPosition(_THIS, SDL_Window * window)
   805 {
   806     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   807     Display *display = data->videodata->display;
   808     unsigned int childCount;
   809     Window childReturn, root, parent;
   810     Window* children;
   811     XWindowAttributes attrs;
   812     int orig_x, orig_y;
   813     Uint32 timeout;
   814 
   815     X11_XSync(display, False);
   816     X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount);
   817     X11_XGetWindowAttributes(display, data->xwindow, &attrs);
   818     X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
   819                               attrs.x, attrs.y, &orig_x, &orig_y, &childReturn);
   820 
   821     /*Attempt to move the window*/
   822     X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
   823 
   824     /* Wait a brief time to see if the window manager decided to let this move happen.
   825        If the window changes at all, even to an unexpected value, we break out. */
   826     timeout = SDL_GetTicks() + 100;
   827     while (SDL_TRUE) {
   828         int x, y;
   829         X11_XSync(display, False);
   830         X11_XGetWindowAttributes(display, data->xwindow, &attrs);
   831         X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display),
   832                                   attrs.x, attrs.y, &x, &y, &childReturn);
   833 
   834         if ((x != orig_x) || (y != orig_y)) {
   835             window->x = x;
   836             window->y = y;
   837             break;  /* window moved, time to go. */
   838         } else if ((x == window->x) && (y == window->y)) {
   839             break;  /* we're at the place we wanted to be anyhow, drop out. */
   840         }
   841 
   842         if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) {
   843             break;
   844         }
   845 
   846         SDL_Delay(10);
   847     }
   848 }
   849 
   850 void
   851 X11_SetWindowMinimumSize(_THIS, SDL_Window * window)
   852 {
   853     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   854     Display *display = data->videodata->display;
   855 
   856     if (window->flags & SDL_WINDOW_RESIZABLE) {
   857          XSizeHints *sizehints = X11_XAllocSizeHints();
   858          long userhints;
   859 
   860          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
   861 
   862          sizehints->min_width = window->min_w;
   863          sizehints->min_height = window->min_h;
   864          sizehints->flags |= PMinSize;
   865 
   866          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
   867 
   868          X11_XFree(sizehints);
   869 
   870         /* See comment in X11_SetWindowSize. */
   871         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
   872         X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
   873         X11_XRaiseWindow(display, data->xwindow);
   874     }
   875 
   876     X11_XFlush(display);
   877 }
   878 
   879 void
   880 X11_SetWindowMaximumSize(_THIS, SDL_Window * window)
   881 {
   882     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   883     Display *display = data->videodata->display;
   884 
   885     if (window->flags & SDL_WINDOW_RESIZABLE) {
   886          XSizeHints *sizehints = X11_XAllocSizeHints();
   887          long userhints;
   888 
   889          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
   890 
   891          sizehints->max_width = window->max_w;
   892          sizehints->max_height = window->max_h;
   893          sizehints->flags |= PMaxSize;
   894 
   895          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
   896 
   897          X11_XFree(sizehints);
   898 
   899         /* See comment in X11_SetWindowSize. */
   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     }
   904 
   905     X11_XFlush(display);
   906 }
   907 
   908 void
   909 X11_SetWindowSize(_THIS, SDL_Window * window)
   910 {
   911     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   912     Display *display = data->videodata->display;
   913 
   914     if (SDL_IsShapedWindow(window)) {
   915         X11_ResizeWindowShape(window);
   916     }
   917     if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
   918          /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus
   919             we must set the size hints to adjust the window size. */
   920          XSizeHints *sizehints = X11_XAllocSizeHints();
   921          long userhints;
   922 
   923          X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
   924 
   925          sizehints->min_width = sizehints->max_width = window->w;
   926          sizehints->min_height = sizehints->max_height = window->h;
   927          sizehints->flags |= PMinSize | PMaxSize;
   928 
   929          X11_XSetWMNormalHints(display, data->xwindow, sizehints);
   930 
   931          X11_XFree(sizehints);
   932 
   933         /* From Pierre-Loup:
   934            WMs each have their little quirks with that.  When you change the
   935            size hints, they get a ConfigureNotify event with the
   936            WM_NORMAL_SIZE_HINTS Atom.  They all save the hints then, but they
   937            don't all resize the window right away to enforce the new hints.
   938 
   939            Some of them resize only after:
   940             - A user-initiated move or resize
   941             - A code-initiated move or resize
   942             - Hiding & showing window (Unmap & map)
   943 
   944            The following move & resize seems to help a lot of WMs that didn't
   945            properly update after the hints were changed. We don't do a
   946            hide/show, because there are supposedly subtle problems with doing so
   947            and transitioning from windowed to fullscreen in Unity.
   948          */
   949         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
   950         X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
   951         X11_XRaiseWindow(display, data->xwindow);
   952     } else {
   953         X11_XResizeWindow(display, data->xwindow, window->w, window->h);
   954     }
   955 
   956     X11_XFlush(display);
   957 }
   958 
   959 int
   960 X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right)
   961 {
   962     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   963 
   964     *left = data->border_left;
   965     *right = data->border_right;
   966     *top = data->border_top;
   967     *bottom = data->border_bottom;
   968 
   969     return 0;
   970 }
   971 
   972 int
   973 X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
   974 {
   975     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   976     Display *display = data->videodata->display;
   977     Atom _NET_WM_WINDOW_OPACITY = data->videodata->_NET_WM_WINDOW_OPACITY;
   978 
   979     if (opacity == 1.0f) {
   980         X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY);
   981     } else  {
   982         const Uint32 FullyOpaque = 0xFFFFFFFF;
   983         const long alpha = (long) ((double)opacity * (double)FullyOpaque);
   984         X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
   985             PropModeReplace, (unsigned char *)&alpha, 1);
   986     }
   987 
   988     return 0;
   989 }
   990 
   991 int 
   992 X11_SetWindowModalFor(_THIS, SDL_Window * modal_window, SDL_Window * parent_window) {
   993     SDL_WindowData *data = (SDL_WindowData *) modal_window->driverdata;
   994     SDL_WindowData *parent_data = (SDL_WindowData *) parent_window->driverdata;
   995     Display *display = data->videodata->display;
   996 
   997     X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow);
   998     return 0;
   999 }
  1000 
  1001 int
  1002 X11_SetWindowInputFocus(_THIS, SDL_Window * window) 
  1003 {
  1004     if (X11_IsWindowMapped(_this, window)) {
  1005         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1006         Display *display = data->videodata->display;
  1007         X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
  1008         X11_XFlush(display);
  1009         return 0;
  1010     }
  1011     return -1;
  1012 }
  1013 
  1014 void
  1015 X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1016 {
  1017     const SDL_bool focused = ((window->flags & SDL_WINDOW_INPUT_FOCUS) != 0);
  1018     const SDL_bool visible = ((window->flags & SDL_WINDOW_HIDDEN) == 0);
  1019     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1020     SDL_DisplayData *displaydata =
  1021         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
  1022     Display *display = data->videodata->display;
  1023     XEvent event;
  1024 
  1025     SetWindowBordered(display, displaydata->screen, data->xwindow, bordered);
  1026     X11_XFlush(display);
  1027 
  1028     if (visible) {
  1029         XWindowAttributes attr;
  1030         do {
  1031             X11_XSync(display, False);
  1032             X11_XGetWindowAttributes(display, data->xwindow, &attr);
  1033         } while (attr.map_state != IsViewable);
  1034 
  1035         if (focused) {
  1036             X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime);
  1037         }
  1038     }
  1039 
  1040     /* make sure these don't make it to the real event queue if they fired here. */
  1041     X11_XSync(display, False);
  1042     X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
  1043     X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
  1044 }
  1045 
  1046 void
  1047 X11_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
  1048 {
  1049     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1050     Display *display = data->videodata->display;
  1051 
  1052     XSizeHints *sizehints = X11_XAllocSizeHints();
  1053     long userhints;
  1054 
  1055     X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints);
  1056 
  1057     if (resizable) {
  1058         /* FIXME: Is there a better way to get max window size from X? -flibit */
  1059         const int maxsize = 0x7FFFFFFF;
  1060         sizehints->min_width = window->min_w;
  1061         sizehints->min_height = window->min_h;
  1062         sizehints->max_width = (window->max_w == 0) ? maxsize : window->max_w;
  1063         sizehints->max_height = (window->max_h == 0) ? maxsize : window->max_h;
  1064     } else {
  1065         sizehints->min_width = window->w;
  1066         sizehints->min_height = window->h;
  1067         sizehints->max_width = window->w;
  1068         sizehints->max_height = window->h;
  1069     }
  1070     sizehints->flags |= PMinSize | PMaxSize;
  1071 
  1072     X11_XSetWMNormalHints(display, data->xwindow, sizehints);
  1073 
  1074     X11_XFree(sizehints);
  1075 
  1076     /* See comment in X11_SetWindowSize. */
  1077     X11_XResizeWindow(display, data->xwindow, window->w, window->h);
  1078     X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top);
  1079     X11_XRaiseWindow(display, data->xwindow);
  1080 
  1081     X11_XFlush(display);
  1082 }
  1083 
  1084 void
  1085 X11_ShowWindow(_THIS, SDL_Window * window)
  1086 {
  1087     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1088     Display *display = data->videodata->display;
  1089     XEvent event;
  1090 
  1091     if (!X11_IsWindowMapped(_this, window)) {
  1092         X11_XMapRaised(display, data->xwindow);
  1093         /* Blocking wait for "MapNotify" event.
  1094          * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type,
  1095          * and XCheckTypedWindowEvent doesn't block */
  1096         if(!(window->flags & SDL_WINDOW_FOREIGN))
  1097             X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow);
  1098         X11_XFlush(display);
  1099     }
  1100 
  1101     if (!data->videodata->net_wm) {
  1102         /* no WM means no FocusIn event, which confuses us. Force it. */
  1103         X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime);
  1104         X11_XFlush(display);
  1105     }
  1106 }
  1107 
  1108 void
  1109 X11_HideWindow(_THIS, SDL_Window * window)
  1110 {
  1111     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1112     SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
  1113     Display *display = data->videodata->display;
  1114     XEvent event;
  1115 
  1116     if (X11_IsWindowMapped(_this, window)) {
  1117         X11_XWithdrawWindow(display, data->xwindow, displaydata->screen);
  1118         /* Blocking wait for "UnmapNotify" event */
  1119         if(!(window->flags & SDL_WINDOW_FOREIGN))
  1120             X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow);
  1121         X11_XFlush(display);
  1122     }
  1123 }
  1124 
  1125 static void
  1126 SetWindowActive(_THIS, SDL_Window * window)
  1127 {
  1128     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1129     SDL_DisplayData *displaydata =
  1130         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
  1131     Display *display = data->videodata->display;
  1132     Atom _NET_ACTIVE_WINDOW = data->videodata->_NET_ACTIVE_WINDOW;
  1133 
  1134     if (X11_IsWindowMapped(_this, window)) {
  1135         XEvent e;
  1136 
  1137         /*printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);*/
  1138 
  1139         SDL_zero(e);
  1140         e.xany.type = ClientMessage;
  1141         e.xclient.message_type = _NET_ACTIVE_WINDOW;
  1142         e.xclient.format = 32;
  1143         e.xclient.window = data->xwindow;
  1144         e.xclient.data.l[0] = 1;  /* source indication. 1 = application */
  1145         e.xclient.data.l[1] = data->user_time;
  1146         e.xclient.data.l[2] = 0;
  1147 
  1148         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1149                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1150 
  1151         X11_XFlush(display);
  1152     }
  1153 }
  1154 
  1155 void
  1156 X11_RaiseWindow(_THIS, SDL_Window * window)
  1157 {
  1158     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1159     Display *display = data->videodata->display;
  1160 
  1161     X11_XRaiseWindow(display, data->xwindow);
  1162     SetWindowActive(_this, window);
  1163     X11_XFlush(display);
  1164 }
  1165 
  1166 static void
  1167 SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized)
  1168 {
  1169     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1170     SDL_DisplayData *displaydata =
  1171         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
  1172     Display *display = data->videodata->display;
  1173     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
  1174     Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
  1175     Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
  1176 
  1177     if (maximized) {
  1178         window->flags |= SDL_WINDOW_MAXIMIZED;
  1179     } else {
  1180         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1181     }
  1182 
  1183     if (X11_IsWindowMapped(_this, window)) {
  1184         XEvent e;
  1185 
  1186         SDL_zero(e);
  1187         e.xany.type = ClientMessage;
  1188         e.xclient.message_type = _NET_WM_STATE;
  1189         e.xclient.format = 32;
  1190         e.xclient.window = data->xwindow;
  1191         e.xclient.data.l[0] =
  1192             maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1193         e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
  1194         e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
  1195         e.xclient.data.l[3] = 0l;
  1196 
  1197         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1198                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1199     } else {
  1200         X11_SetNetWMState(_this, data->xwindow, window->flags);
  1201     }
  1202     X11_XFlush(display);
  1203 }
  1204 
  1205 void
  1206 X11_MaximizeWindow(_THIS, SDL_Window * window)
  1207 {
  1208     SetWindowMaximized(_this, window, SDL_TRUE);
  1209 }
  1210 
  1211 void
  1212 X11_MinimizeWindow(_THIS, SDL_Window * window)
  1213 {
  1214     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1215     SDL_DisplayData *displaydata =
  1216         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
  1217     Display *display = data->videodata->display;
  1218 
  1219     X11_XIconifyWindow(display, data->xwindow, displaydata->screen);
  1220     X11_XFlush(display);
  1221 }
  1222 
  1223 void
  1224 X11_RestoreWindow(_THIS, SDL_Window * window)
  1225 {
  1226     SetWindowMaximized(_this, window, SDL_FALSE);
  1227     X11_ShowWindow(_this, window);
  1228     SetWindowActive(_this, window);
  1229 }
  1230 
  1231 /* This asks the Window Manager to handle fullscreen for us. This is the modern way. */
  1232 static void
  1233 X11_SetWindowFullscreenViaWM(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen)
  1234 {
  1235     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1236     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
  1237     Display *display = data->videodata->display;
  1238     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
  1239     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
  1240 
  1241     if (X11_IsWindowMapped(_this, window)) {
  1242         XEvent e;
  1243 
  1244         if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
  1245             /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we
  1246                can be resized to the fullscreen resolution (or reset so we're not resizable again) */
  1247             XSizeHints *sizehints = X11_XAllocSizeHints();
  1248             long flags = 0;
  1249             X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags);
  1250             /* set the resize flags on */
  1251             if (fullscreen) {
  1252                 /* we are going fullscreen so turn the flags off */
  1253                 sizehints->flags &= ~(PMinSize | PMaxSize);
  1254             } else {
  1255                 /* Reset the min/max width height to make the window non-resizable again */
  1256                 sizehints->flags |= PMinSize | PMaxSize;
  1257                 sizehints->min_width = sizehints->max_width = window->windowed.w;
  1258                 sizehints->min_height = sizehints->max_height = window->windowed.h;
  1259             }
  1260             X11_XSetWMNormalHints(display, data->xwindow, sizehints);
  1261             X11_XFree(sizehints);
  1262         }
  1263 
  1264         SDL_zero(e);
  1265         e.xany.type = ClientMessage;
  1266         e.xclient.message_type = _NET_WM_STATE;
  1267         e.xclient.format = 32;
  1268         e.xclient.window = data->xwindow;
  1269         e.xclient.data.l[0] =
  1270             fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  1271         e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
  1272         e.xclient.data.l[3] = 0l;
  1273 
  1274         X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1275                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1276 
  1277         /* Fullscreen windows sometimes end up being marked maximized by
  1278             window managers. Force it back to how we expect it to be. */
  1279         if (!fullscreen && ((window->flags & SDL_WINDOW_MAXIMIZED) == 0)) {
  1280             SDL_zero(e);
  1281             e.xany.type = ClientMessage;
  1282             e.xclient.message_type = _NET_WM_STATE;
  1283             e.xclient.format = 32;
  1284             e.xclient.window = data->xwindow;
  1285             e.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
  1286             e.xclient.data.l[1] = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
  1287             e.xclient.data.l[2] = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
  1288             e.xclient.data.l[3] = 0l;
  1289             X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1290                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1291         }
  1292     } else {
  1293         Uint32 flags;
  1294 
  1295         flags = window->flags;
  1296         if (fullscreen) {
  1297             flags |= SDL_WINDOW_FULLSCREEN;
  1298         } else {
  1299             flags &= ~SDL_WINDOW_FULLSCREEN;
  1300         }
  1301         X11_SetNetWMState(_this, data->xwindow, flags);
  1302     }
  1303 
  1304     if (data->visual->class == DirectColor) {
  1305         if ( fullscreen ) {
  1306             X11_XInstallColormap(display, data->colormap);
  1307         } else {
  1308             X11_XUninstallColormap(display, data->colormap);
  1309         }
  1310     }
  1311 
  1312     X11_XFlush(display);
  1313 }
  1314 
  1315 /* This handles fullscreen itself, outside the Window Manager. */
  1316 static void
  1317 X11_BeginWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display)
  1318 {
  1319     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1320     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
  1321     Visual *visual = data->visual;
  1322     Display *display = data->videodata->display;
  1323     const int screen = displaydata->screen;
  1324     Window root = RootWindow(display, screen);
  1325     const int def_vis = (visual == DefaultVisual(display, screen));
  1326     unsigned long xattrmask = 0;
  1327     XSetWindowAttributes xattr;
  1328     XEvent ev;
  1329     SDL_Rect rect;
  1330 
  1331     if ( data->fswindow ) {
  1332         return;  /* already fullscreen, I hope. */
  1333     }
  1334 
  1335     X11_GetDisplayBounds(_this, _display, &rect);
  1336 
  1337     SDL_zero(xattr);
  1338     xattr.override_redirect = True;
  1339     xattrmask |= CWOverrideRedirect;
  1340     xattr.background_pixel = def_vis ? BlackPixel(display, screen) : 0;
  1341     xattrmask |= CWBackPixel;
  1342     xattr.border_pixel = 0;
  1343     xattrmask |= CWBorderPixel;
  1344     xattr.colormap = data->colormap;
  1345     xattrmask |= CWColormap;
  1346 
  1347     data->fswindow = X11_XCreateWindow(display, root,
  1348                                    rect.x, rect.y, rect.w, rect.h, 0,
  1349                                    displaydata->depth, InputOutput,
  1350                                    visual, xattrmask, &xattr);
  1351 
  1352     X11_XSelectInput(display, data->fswindow, StructureNotifyMask);
  1353     X11_XSetWindowBackground(display, data->fswindow, 0);
  1354     X11_XInstallColormap(display, data->colormap);
  1355     X11_XClearWindow(display, data->fswindow);
  1356     X11_XMapRaised(display, data->fswindow);
  1357 
  1358     /* Make sure the fswindow is in view by warping mouse to the corner */
  1359     X11_XUngrabPointer(display, CurrentTime);
  1360     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
  1361 
  1362     /* Wait to be mapped, filter Unmap event out if it arrives. */
  1363     X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->fswindow);
  1364     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->fswindow);
  1365 
  1366 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
  1367     if ( displaydata->use_vidmode ) {
  1368         X11_XF86VidModeLockModeSwitch(display, screen, True);
  1369     }
  1370 #endif
  1371 
  1372     SetWindowBordered(display, displaydata->screen, data->xwindow, SDL_FALSE);
  1373 
  1374     /* Center actual window within our cover-the-screen window. */
  1375     X11_XReparentWindow(display, data->xwindow, data->fswindow,
  1376                     (rect.w - window->w) / 2, (rect.h - window->h) / 2);
  1377 
  1378     /* Move the mouse to the upper left to make sure it's on-screen */
  1379     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
  1380 
  1381     /* Center mouse in the fullscreen window. */
  1382     rect.x += (rect.w / 2);
  1383     rect.y += (rect.h / 2);
  1384     X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y);
  1385 
  1386     /* Wait to be mapped, filter Unmap event out if it arrives. */
  1387     X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow);
  1388     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow);
  1389 
  1390     SDL_UpdateWindowGrab(window);
  1391 }
  1392 
  1393 static void
  1394 X11_EndWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display)
  1395 {
  1396     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1397     SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
  1398     Display *display = data->videodata->display;
  1399     const int screen = displaydata->screen;
  1400     Window root = RootWindow(display, screen);
  1401     Window fswindow = data->fswindow;
  1402     XEvent ev;
  1403 
  1404     if (!data->fswindow) {
  1405         return;  /* already not fullscreen, I hope. */
  1406     }
  1407 
  1408     data->fswindow = None;
  1409 
  1410 #if SDL_VIDEO_DRIVER_X11_VIDMODE
  1411     if ( displaydata->use_vidmode ) {
  1412         X11_XF86VidModeLockModeSwitch(display, screen, False);
  1413     }
  1414 #endif
  1415 
  1416     SDL_UpdateWindowGrab(window);
  1417 
  1418     X11_XReparentWindow(display, data->xwindow, root, window->x, window->y);
  1419 
  1420     /* flush these events so they don't confuse normal event handling */
  1421     X11_XSync(display, False);
  1422     X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow);
  1423     X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow);
  1424 
  1425     SetWindowBordered(display, screen, data->xwindow,
  1426                       (window->flags & SDL_WINDOW_BORDERLESS) == 0);
  1427 
  1428     X11_XWithdrawWindow(display, fswindow, screen);
  1429 
  1430     /* Wait to be unmapped. */
  1431     X11_XIfEvent(display, &ev, &isUnmapNotify, (XPointer)&fswindow);
  1432     X11_XDestroyWindow(display, fswindow);
  1433 }
  1434 
  1435 
  1436 void
  1437 X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen)
  1438 {
  1439     /* !!! FIXME: SDL_Hint? */
  1440     SDL_bool legacy = SDL_FALSE;
  1441     const char *env = SDL_getenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN");
  1442     if (env) {
  1443         legacy = SDL_atoi(env);
  1444     } else {
  1445         SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1446         SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata;
  1447         if ( displaydata->use_vidmode ) {
  1448             legacy = SDL_TRUE;  /* the new stuff only works with XRandR. */
  1449         } else if ( !videodata->net_wm ) {
  1450             legacy = SDL_TRUE;  /* The window manager doesn't support it */
  1451         } else {
  1452             /* !!! FIXME: look at the window manager name, and blacklist certain ones? */
  1453             /* http://stackoverflow.com/questions/758648/find-the-name-of-the-x-window-manager */
  1454             legacy = SDL_FALSE;  /* try the new way. */
  1455         }
  1456     }
  1457 
  1458     if (legacy) {
  1459         if (fullscreen) {
  1460             X11_BeginWindowFullscreenLegacy(_this, window, _display);
  1461         } else {
  1462             X11_EndWindowFullscreenLegacy(_this, window, _display);
  1463         }
  1464     } else {
  1465         X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen);
  1466     }
  1467 }
  1468 
  1469 
  1470 int
  1471 X11_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1472 {
  1473     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1474     Display *display = data->videodata->display;
  1475     Visual *visual = data->visual;
  1476     Colormap colormap = data->colormap;
  1477     XColor *colorcells;
  1478     int ncolors;
  1479     int rmask, gmask, bmask;
  1480     int rshift, gshift, bshift;
  1481     int i;
  1482 
  1483     if (visual->class != DirectColor) {
  1484         return SDL_SetError("Window doesn't have DirectColor visual");
  1485     }
  1486 
  1487     ncolors = visual->map_entries;
  1488     colorcells = SDL_malloc(ncolors * sizeof(XColor));
  1489     if (!colorcells) {
  1490         return SDL_OutOfMemory();
  1491     }
  1492 
  1493     rshift = 0;
  1494     rmask = visual->red_mask;
  1495     while (0 == (rmask & 1)) {
  1496         rshift++;
  1497         rmask >>= 1;
  1498     }
  1499 
  1500     gshift = 0;
  1501     gmask = visual->green_mask;
  1502     while (0 == (gmask & 1)) {
  1503         gshift++;
  1504         gmask >>= 1;
  1505     }
  1506 
  1507     bshift = 0;
  1508     bmask = visual->blue_mask;
  1509     while (0 == (bmask & 1)) {
  1510         bshift++;
  1511         bmask >>= 1;
  1512     }
  1513 
  1514     /* build the color table pixel values */
  1515     for (i = 0; i < ncolors; i++) {
  1516         Uint32 rbits = (rmask * i) / (ncolors - 1);
  1517         Uint32 gbits = (gmask * i) / (ncolors - 1);
  1518         Uint32 bbits = (bmask * i) / (ncolors - 1);
  1519         Uint32 pix = (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
  1520 
  1521         colorcells[i].pixel = pix;
  1522 
  1523         colorcells[i].red = ramp[(0 * 256) + i];
  1524         colorcells[i].green = ramp[(1 * 256) + i];
  1525         colorcells[i].blue = ramp[(2 * 256) + i];
  1526 
  1527         colorcells[i].flags = DoRed | DoGreen | DoBlue;
  1528     }
  1529 
  1530     X11_XStoreColors(display, colormap, colorcells, ncolors);
  1531     X11_XFlush(display);
  1532     SDL_free(colorcells);
  1533 
  1534     return 0;
  1535 }
  1536 
  1537 void
  1538 X11_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1539 {
  1540     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1541     Display *display = data->videodata->display;
  1542     SDL_bool oldstyle_fullscreen;
  1543     SDL_bool grab_keyboard;
  1544 
  1545     /* ICCCM2.0-compliant window managers can handle fullscreen windows
  1546        If we're using XVidMode to change resolution we need to confine
  1547        the cursor so we don't pan around the virtual desktop.
  1548      */
  1549     oldstyle_fullscreen = X11_IsWindowLegacyFullscreen(_this, window);
  1550 
  1551     if (oldstyle_fullscreen || grabbed) {
  1552         /* Try to grab the mouse */
  1553         if (!data->videodata->broken_pointer_grab) {
  1554             const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
  1555             int attempts;
  1556             int result;
  1557 
  1558             /* Try for up to 5000ms (5s) to grab. If it still fails, stop trying. */
  1559             for (attempts = 0; attempts < 100; attempts++) {
  1560                 result = X11_XGrabPointer(display, data->xwindow, True, mask, GrabModeAsync,
  1561                                  GrabModeAsync, data->xwindow, None, CurrentTime);
  1562                 if (result == GrabSuccess) {
  1563                     break;
  1564                 }
  1565                 SDL_Delay(50);
  1566             }
  1567 
  1568             if (result != GrabSuccess) {
  1569                 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "The X server refused to let us grab the mouse. You might experience input bugs.");
  1570                 data->videodata->broken_pointer_grab = SDL_TRUE;  /* don't try again. */
  1571             }
  1572         }
  1573 
  1574         /* Raise the window if we grab the mouse */
  1575         X11_XRaiseWindow(display, data->xwindow);
  1576 
  1577         /* Now grab the keyboard */
  1578         if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) {
  1579             grab_keyboard = SDL_TRUE;
  1580         } else {
  1581             /* We need to do this with the old style override_redirect
  1582                fullscreen window otherwise we won't get keyboard focus.
  1583             */
  1584             grab_keyboard = oldstyle_fullscreen;
  1585         }
  1586         if (grab_keyboard) {
  1587             X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
  1588                           GrabModeAsync, CurrentTime);
  1589         }
  1590     } else {
  1591         X11_XUngrabPointer(display, CurrentTime);
  1592         X11_XUngrabKeyboard(display, CurrentTime);
  1593     }
  1594     X11_XSync(display, False);
  1595 }
  1596 
  1597 void
  1598 X11_DestroyWindow(_THIS, SDL_Window * window)
  1599 {
  1600     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1601 
  1602     if (data) {
  1603         SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
  1604         Display *display = videodata->display;
  1605         int numwindows = videodata->numwindows;
  1606         SDL_WindowData **windowlist = videodata->windowlist;
  1607         int i;
  1608 
  1609         if (windowlist) {
  1610             for (i = 0; i < numwindows; ++i) {
  1611                 if (windowlist[i] && (windowlist[i]->window == window)) {
  1612                     windowlist[i] = windowlist[numwindows - 1];
  1613                     windowlist[numwindows - 1] = NULL;
  1614                     videodata->numwindows--;
  1615                     break;
  1616                 }
  1617             }
  1618         }
  1619 #ifdef X_HAVE_UTF8_STRING
  1620         if (data->ic) {
  1621             X11_XDestroyIC(data->ic);
  1622         }
  1623 #endif
  1624         if (data->created) {
  1625             X11_XDestroyWindow(display, data->xwindow);
  1626             X11_XFlush(display);
  1627         }
  1628         SDL_free(data);
  1629     }
  1630     window->driverdata = NULL;
  1631 }
  1632 
  1633 SDL_bool
  1634 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1635 {
  1636     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1637     Display *display = data->videodata->display;
  1638 
  1639     if (info->version.major == SDL_MAJOR_VERSION &&
  1640         info->version.minor == SDL_MINOR_VERSION) {
  1641         info->subsystem = SDL_SYSWM_X11;
  1642         info->info.x11.display = display;
  1643         info->info.x11.window = data->xwindow;
  1644         return SDL_TRUE;
  1645     } else {
  1646         SDL_SetError("Application not compiled with SDL %d.%d",
  1647                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1648         return SDL_FALSE;
  1649     }
  1650 }
  1651 
  1652 int
  1653 X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
  1654 {
  1655     return 0;  /* just succeed, the real work is done elsewhere. */
  1656 }
  1657 
  1658 void
  1659 X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
  1660 {
  1661     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1662     Display *display = data->videodata->display;
  1663     Atom XdndAware = X11_XInternAtom(display, "XdndAware", False);
  1664 
  1665     if (accept) {
  1666         Atom xdnd_version = 5;
  1667         X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
  1668                      PropModeReplace, (unsigned char*)&xdnd_version, 1);
  1669     } else {
  1670         X11_XDeleteProperty(display, data->xwindow, XdndAware);
  1671     }
  1672 }
  1673 
  1674 #endif /* SDL_VIDEO_DRIVER_X11 */
  1675 
  1676 /* vi: set ts=4 sw=4 expandtab: */