src/video/x11/SDL_x11window.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 15 Feb 2011 17:23:02 -0800
changeset 5302 b1b9ec9874ba
parent 5288 d4381f3b0d1e
child 5305 75f5ff92ad08
permissions -rw-r--r--
Implemented X11 fullscreen toggling with modern window managers
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2011 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #include "../SDL_sysvideo.h"
    25 #include "../SDL_pixels_c.h"
    26 #include "../../events/SDL_keyboard_c.h"
    27 #include "../../events/SDL_mouse_c.h"
    28 
    29 #include "SDL_x11video.h"
    30 #include "SDL_x11mouse.h"
    31 #include "SDL_x11shape.h"
    32 
    33 #ifdef SDL_VIDEO_DRIVER_PANDORA
    34 #include "SDL_x11opengles.h"
    35 #endif
    36 
    37 #include "SDL_timer.h"
    38 #include "SDL_syswm.h"
    39 
    40 #define _NET_WM_STATE_REMOVE    0l
    41 #define _NET_WM_STATE_ADD       1l
    42 #define _NET_WM_STATE_TOGGLE    2l
    43 
    44 static SDL_bool
    45 X11_IsWindowOldFullscreen(_THIS, SDL_Window * window)
    46 {
    47     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    48 
    49     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    50     if ((window->flags & SDL_WINDOW_FULLSCREEN) && !videodata->net_wm) {
    51         return SDL_TRUE;
    52     } else {
    53         return SDL_FALSE;
    54     }
    55 }
    56 
    57 static SDL_bool
    58 X11_IsWindowMapped(_THIS, SDL_Window * window)
    59 {
    60     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    61     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    62     XWindowAttributes attr;
    63 
    64     XGetWindowAttributes(videodata->display, data->xwindow, &attr);
    65     if (attr.map_state != IsUnmapped) {
    66         return SDL_TRUE;
    67     } else {
    68         return SDL_FALSE;
    69     }
    70 }
    71 
    72 static int
    73 X11_GetWMStateProperty(_THIS, SDL_Window * window, Atom atoms[3])
    74 {
    75     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    76     int count = 0;
    77 
    78     if (window->flags & SDL_WINDOW_FULLSCREEN) {
    79         atoms[count++] = data->_NET_WM_STATE_FULLSCREEN;
    80     }
    81     if (window->flags & SDL_WINDOW_MAXIMIZED) {
    82         atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_VERT;
    83         atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_HORZ;
    84     }
    85     return count;
    86 }
    87 
    88 static void
    89 X11_GetDisplaySize(_THIS, SDL_Window * window, int *w, int *h)
    90 {
    91     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    92     SDL_DisplayData *displaydata =
    93         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
    94     XWindowAttributes attr;
    95 
    96     XGetWindowAttributes(data->display, RootWindow(data->display, displaydata->screen), &attr);
    97     if (window->flags & SDL_WINDOW_FULLSCREEN) {
    98         /* The bounds when this window is visible is the fullscreen mode */
    99         SDL_DisplayMode fullscreen_mode;
   100         if (SDL_GetWindowDisplayMode(window, &fullscreen_mode) == 0) {
   101             attr.width = fullscreen_mode.w;
   102             attr.height = fullscreen_mode.h;
   103         }
   104     }
   105     if (w) {
   106         *w = attr.width;
   107     }
   108     if (h) {
   109         *h = attr.height;
   110     }
   111 }
   112 
   113 static int
   114 SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
   115 {
   116     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   117     SDL_WindowData *data;
   118     int numwindows = videodata->numwindows;
   119     int windowlistlength = videodata->windowlistlength;
   120     SDL_WindowData **windowlist = videodata->windowlist;
   121 
   122     /* Allocate the window data */
   123     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
   124     if (!data) {
   125         SDL_OutOfMemory();
   126         return -1;
   127     }
   128     data->window = window;
   129     data->xwindow = w;
   130 #ifdef X_HAVE_UTF8_STRING
   131     if (SDL_X11_HAVE_UTF8) {
   132         data->ic =
   133             pXCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w,
   134                        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
   135                        XNResourceName, videodata->classname, XNResourceClass,
   136                        videodata->classname, NULL);
   137     }
   138 #endif
   139     data->created = created;
   140     data->videodata = videodata;
   141 
   142     /* Associate the data with the window */
   143 
   144     if (numwindows < windowlistlength) {
   145         windowlist[numwindows] = data;
   146         videodata->numwindows++;
   147     } else {
   148         windowlist =
   149             (SDL_WindowData **) SDL_realloc(windowlist,
   150                                             (numwindows +
   151                                              1) * sizeof(*windowlist));
   152         if (!windowlist) {
   153             SDL_OutOfMemory();
   154             SDL_free(data);
   155             return -1;
   156         }
   157         windowlist[numwindows] = data;
   158         videodata->numwindows++;
   159         videodata->windowlistlength++;
   160         videodata->windowlist = windowlist;
   161     }
   162 
   163     /* Fill in the SDL window with the window data */
   164     {
   165         XWindowAttributes attrib;
   166 
   167         XGetWindowAttributes(data->videodata->display, w, &attrib);
   168         window->x = attrib.x;
   169         window->y = attrib.y;
   170         window->w = attrib.width;
   171         window->h = attrib.height;
   172         if (attrib.map_state != IsUnmapped) {
   173             window->flags |= SDL_WINDOW_SHOWN;
   174         } else {
   175             window->flags &= ~SDL_WINDOW_SHOWN;
   176         }
   177         data->visual = attrib.visual;
   178     }
   179 
   180     {
   181         Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
   182         Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
   183         Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
   184         Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
   185         Atom actualType;
   186         int actualFormat;
   187         unsigned long i, numItems, bytesAfter;
   188         unsigned char *propertyValue = NULL;
   189         long maxLength = 1024;
   190 
   191         if (XGetWindowProperty(data->videodata->display, w, _NET_WM_STATE,
   192                                0l, maxLength, False, XA_ATOM, &actualType,
   193                                &actualFormat, &numItems, &bytesAfter,
   194                                &propertyValue) == Success) {
   195             Atom *atoms = (Atom *) propertyValue;
   196             int maximized = 0;
   197             int fullscreen = 0;
   198 
   199             for (i = 0; i < numItems; ++i) {
   200                 if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
   201                     maximized |= 1;
   202                 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
   203                     maximized |= 2;
   204                 } else if ( atoms[i] == _NET_WM_STATE_FULLSCREEN) {
   205                     fullscreen = 1;
   206                 }
   207             }
   208             if (maximized == 3) {
   209                 window->flags |= SDL_WINDOW_MAXIMIZED;
   210             }  else if (fullscreen == 1) {
   211                 window->flags |= SDL_WINDOW_FULLSCREEN;
   212             }
   213             XFree(propertyValue);
   214         }
   215     }
   216 
   217     /* FIXME: How can I tell?
   218        {
   219        DWORD style = GetWindowLong(hwnd, GWL_STYLE);
   220        if (style & WS_VISIBLE) {
   221        if (style & (WS_BORDER | WS_THICKFRAME)) {
   222        window->flags &= ~SDL_WINDOW_BORDERLESS;
   223        } else {
   224        window->flags |= SDL_WINDOW_BORDERLESS;
   225        }
   226        if (style & WS_THICKFRAME) {
   227        window->flags |= SDL_WINDOW_RESIZABLE;
   228        } else {
   229        window->flags &= ~SDL_WINDOW_RESIZABLE;
   230        }
   231        if (style & WS_MINIMIZE) {
   232        window->flags |= SDL_WINDOW_MINIMIZED;
   233        } else {
   234        window->flags &= ~SDL_WINDOW_MINIMIZED;
   235        }
   236        }
   237        if (GetFocus() == hwnd) {
   238        int index = data->videodata->keyboard;
   239        window->flags |= SDL_WINDOW_INPUT_FOCUS;
   240        SDL_SetKeyboardFocus(index, data->window);
   241 
   242        if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   243        RECT rect;
   244        GetClientRect(hwnd, &rect);
   245        ClientToScreen(hwnd, (LPPOINT) & rect);
   246        ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   247        ClipCursor(&rect);
   248        }
   249        }
   250      */
   251 
   252     /* All done! */
   253     window->driverdata = data;
   254     return 0;
   255 }
   256 
   257 int
   258 X11_CreateWindow(_THIS, SDL_Window * window)
   259 {
   260     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   261     SDL_DisplayData *displaydata =
   262         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   263     Display *display = data->display;
   264     int screen = displaydata->screen;
   265     Visual *visual;
   266     int depth;
   267     XSetWindowAttributes xattr;
   268     int x, y;
   269     Window w;
   270     XSizeHints *sizehints;
   271     XWMHints *wmhints;
   272     XClassHint *classhints;
   273     SDL_bool oldstyle_fullscreen;
   274     Atom _NET_WM_WINDOW_TYPE;
   275     Atom _NET_WM_WINDOW_TYPE_NORMAL;
   276     int wmstate_count;
   277     Atom wmstate_atoms[3];
   278 
   279     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
   280     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
   281 
   282 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   283 /* FIXME
   284     if ( use_xinerama ) {
   285         x = xinerama_info.x_org;
   286         y = xinerama_info.y_org;
   287     }
   288 */
   289 #endif
   290 #if SDL_VIDEO_OPENGL_GLX
   291     if (window->flags & SDL_WINDOW_OPENGL) {
   292         XVisualInfo *vinfo;
   293 
   294         vinfo = X11_GL_GetVisual(_this, display, screen);
   295         if (!vinfo) {
   296             return -1;
   297         }
   298         visual = vinfo->visual;
   299         depth = vinfo->depth;
   300         XFree(vinfo);
   301     } else
   302 #endif
   303 #ifdef SDL_VIDEO_DRIVER_PANDORA
   304     if (window->flags & SDL_WINDOW_OPENGL) {
   305         XVisualInfo *vinfo;
   306 
   307         vinfo = X11_GLES_GetVisual(_this, display, screen);
   308         if (!vinfo) {
   309             return -1;
   310         }
   311         visual = vinfo->visual;
   312         depth = vinfo->depth;
   313         XFree(vinfo);
   314     } else
   315 #endif
   316     {
   317         visual = displaydata->visual;
   318         depth = displaydata->depth;
   319     }
   320 
   321     if (oldstyle_fullscreen) {
   322         xattr.override_redirect = True;
   323     } else {
   324         xattr.override_redirect = False;
   325     }
   326     xattr.background_pixel = 0;
   327     xattr.border_pixel = 0;
   328     xattr.colormap = XCreateColormap(display, RootWindow(display, screen), visual, AllocNone);
   329 
   330     if (oldstyle_fullscreen
   331         || SDL_WINDOWPOS_ISCENTERED(window->x)) {
   332         X11_GetDisplaySize(_this, window, &x, NULL);
   333         x = (x - window->w) / 2;
   334     } else if (SDL_WINDOWPOS_ISUNDEFINED(window->x)) {
   335         x = 0;
   336     } else {
   337         x = window->x;
   338     }
   339     if (oldstyle_fullscreen
   340         || SDL_WINDOWPOS_ISCENTERED(window->y)) {
   341         X11_GetDisplaySize(_this, window, NULL, &y);
   342         y = (y - window->h) / 2;
   343     } else if (SDL_WINDOWPOS_ISUNDEFINED(window->y)) {
   344         y = 0;
   345     } else {
   346         y = window->y;
   347     }
   348 
   349     w = XCreateWindow(display, RootWindow(display, screen), x, y,
   350                       window->w, window->h, 0, depth, InputOutput, visual,
   351                       (CWOverrideRedirect | CWBackPixel | CWBorderPixel |
   352                        CWColormap), &xattr);
   353     if (!w) {
   354         SDL_SetError("Couldn't create window");
   355         return -1;
   356     }
   357 #if SDL_VIDEO_DRIVER_PANDORA
   358     /* Create the GLES window surface */
   359     _this->gles_data->egl_surface =
   360         _this->gles_data->eglCreateWindowSurface(_this->gles_data->
   361                                                  egl_display,
   362                                                  _this->gles_data->egl_config,
   363                                                  (NativeWindowType) w, NULL);
   364 
   365     if (_this->gles_data->egl_surface == EGL_NO_SURFACE) {
   366         SDL_SetError("Could not create GLES window surface");
   367         return -1;
   368     }
   369 #endif
   370 
   371     sizehints = XAllocSizeHints();
   372     if (sizehints) {
   373         if (!(window->flags & SDL_WINDOW_RESIZABLE)
   374             || oldstyle_fullscreen) {
   375             sizehints->min_width = sizehints->max_width = window->w;
   376             sizehints->min_height = sizehints->max_height = window->h;
   377             sizehints->flags = PMaxSize | PMinSize;
   378         }
   379         if (!oldstyle_fullscreen
   380             && !SDL_WINDOWPOS_ISUNDEFINED(window->x)
   381             && !SDL_WINDOWPOS_ISUNDEFINED(window->y)) {
   382             sizehints->x = x;
   383             sizehints->y = y;
   384             sizehints->flags |= USPosition;
   385         }
   386         XSetWMNormalHints(display, w, sizehints);
   387         XFree(sizehints);
   388     }
   389 
   390     if ((window->flags & SDL_WINDOW_BORDERLESS) || oldstyle_fullscreen) {
   391         SDL_bool set;
   392         Atom WM_HINTS;
   393 
   394         /* We haven't modified the window manager hints yet */
   395         set = SDL_FALSE;
   396 
   397         /* First try to set MWM hints */
   398         WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
   399         if (WM_HINTS != None) {
   400             /* Hints used by Motif compliant window managers */
   401             struct
   402             {
   403                 unsigned long flags;
   404                 unsigned long functions;
   405                 unsigned long decorations;
   406                 long input_mode;
   407                 unsigned long status;
   408             } MWMHints = {
   409             (1L << 1), 0, 0, 0, 0};
   410 
   411             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   412                             PropModeReplace, (unsigned char *) &MWMHints,
   413                             sizeof(MWMHints) / 4);
   414             set = SDL_TRUE;
   415         }
   416         /* Now try to set KWM hints */
   417         WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
   418         if (WM_HINTS != None) {
   419             long KWMHints = 0;
   420 
   421             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   422                             PropModeReplace,
   423                             (unsigned char *) &KWMHints,
   424                             sizeof(KWMHints) / 4);
   425             set = SDL_TRUE;
   426         }
   427         /* Now try to set GNOME hints */
   428         WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
   429         if (WM_HINTS != None) {
   430             long GNOMEHints = 0;
   431 
   432             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   433                             PropModeReplace,
   434                             (unsigned char *) &GNOMEHints,
   435                             sizeof(GNOMEHints) / 4);
   436             set = SDL_TRUE;
   437         }
   438         /* Finally set the transient hints if necessary */
   439         if (!set) {
   440             XSetTransientForHint(display, w, RootWindow(display, screen));
   441         }
   442     } else {
   443         SDL_bool set;
   444         Atom WM_HINTS;
   445 
   446         /* We haven't modified the window manager hints yet */
   447         set = SDL_FALSE;
   448 
   449         /* First try to unset MWM hints */
   450         WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
   451         if (WM_HINTS != None) {
   452             XDeleteProperty(display, w, WM_HINTS);
   453             set = SDL_TRUE;
   454         }
   455         /* Now try to unset KWM hints */
   456         WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
   457         if (WM_HINTS != None) {
   458             XDeleteProperty(display, w, WM_HINTS);
   459             set = SDL_TRUE;
   460         }
   461         /* Now try to unset GNOME hints */
   462         WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
   463         if (WM_HINTS != None) {
   464             XDeleteProperty(display, w, WM_HINTS);
   465             set = SDL_TRUE;
   466         }
   467         /* Finally unset the transient hints if necessary */
   468         if (!set) {
   469             XDeleteProperty(display, w, XA_WM_TRANSIENT_FOR);
   470         }
   471     }
   472 
   473     /* Set the input hints so we get keyboard input */
   474     wmhints = XAllocWMHints();
   475     if (wmhints) {
   476         wmhints->input = True;
   477         wmhints->flags = InputHint;
   478         XSetWMHints(display, w, wmhints);
   479         XFree(wmhints);
   480     }
   481 
   482     /* Set the class hints so we can get an icon (AfterStep) */
   483     classhints = XAllocClassHint();
   484     if (classhints != NULL) {
   485         classhints->res_name = data->classname;
   486         classhints->res_class = data->classname;
   487         XSetClassHint(display, w, classhints);
   488         XFree(classhints);
   489     }
   490 
   491     /* Set the window manager state */
   492     wmstate_count = X11_GetWMStateProperty(_this, window, wmstate_atoms);
   493     if (wmstate_count > 0) {
   494         XChangeProperty(display, w, data->_NET_WM_STATE, XA_ATOM, 32,
   495                         PropModeReplace,
   496                         (unsigned char *)wmstate_atoms, wmstate_count);
   497     } else {
   498         XDeleteProperty(display, w, data->_NET_WM_STATE);
   499     }
   500 
   501     /* Let the window manager know we're a "normal" window */
   502     _NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
   503     _NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
   504     XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   505                     PropModeReplace,
   506                     (unsigned char *)&_NET_WM_WINDOW_TYPE_NORMAL, 1);
   507 
   508     /* Allow the window to be deleted by the window manager */
   509     XSetWMProtocols(display, w, &data->WM_DELETE_WINDOW, 1);
   510 
   511     if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
   512         XDestroyWindow(display, w);
   513         return -1;
   514     }
   515 #ifdef X_HAVE_UTF8_STRING
   516     {
   517         Uint32 fevent = 0;
   518         pXGetICValues(((SDL_WindowData *) window->driverdata)->ic,
   519                       XNFilterEvents, &fevent, NULL);
   520         XSelectInput(display, w,
   521                      (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   522                       ExposureMask | ButtonPressMask | ButtonReleaseMask |
   523                       PointerMotionMask | KeyPressMask | KeyReleaseMask |
   524                       PropertyChangeMask | StructureNotifyMask |
   525                       KeymapStateMask | fevent));
   526     }
   527 #else
   528     {
   529         XSelectInput(display, w,
   530                      (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   531                       ExposureMask | ButtonPressMask | ButtonReleaseMask |
   532                       PointerMotionMask | KeyPressMask | KeyReleaseMask |
   533                       PropertyChangeMask | StructureNotifyMask |
   534                       KeymapStateMask));
   535     }
   536 #endif
   537 
   538     XFlush(display);
   539 
   540     return 0;
   541 }
   542 
   543 int
   544 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   545 {
   546     Window w = (Window) data;
   547 
   548     window->title = X11_GetWindowTitle(_this, w);
   549 
   550     if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
   551         return -1;
   552     }
   553     return 0;
   554 }
   555 
   556 char *
   557 X11_GetWindowTitle(_THIS, Window xwindow)
   558 {
   559     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   560     Display *display = data->display;
   561     int status, real_format;
   562     Atom real_type;
   563     unsigned long items_read, items_left;
   564     unsigned char *propdata;
   565     char *title = NULL;
   566 
   567     status = XGetWindowProperty(display, xwindow, data->_NET_WM_NAME,
   568                 0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format,
   569                 &items_read, &items_left, &propdata);
   570     if (status == Success) {
   571         title = SDL_strdup(SDL_static_cast(char*, propdata));
   572         XFree(propdata);
   573     } else {
   574         status = XGetWindowProperty(display, xwindow, XA_WM_NAME,
   575                     0L, 8192L, False, XA_STRING, &real_type, &real_format,
   576                     &items_read, &items_left, &propdata);
   577         if (status == Success) {
   578             title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1);
   579         } else {
   580             title = SDL_strdup("");
   581         }
   582     }
   583     return title;
   584 }
   585 
   586 void
   587 X11_SetWindowTitle(_THIS, SDL_Window * window)
   588 {
   589     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   590     Display *display = data->videodata->display;
   591     XTextProperty titleprop, iconprop;
   592     Status status;
   593     const char *title = window->title;
   594     const char *icon = NULL;
   595 
   596 #ifdef X_HAVE_UTF8_STRING
   597     Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME;
   598     Atom _NET_WM_ICON_NAME = data->videodata->_NET_WM_ICON_NAME;
   599 #endif
   600 
   601     if (title != NULL) {
   602         char *title_locale = SDL_iconv_utf8_locale(title);
   603         if (!title_locale) {
   604             SDL_OutOfMemory();
   605             return;
   606         }
   607         status = XStringListToTextProperty(&title_locale, 1, &titleprop);
   608         SDL_free(title_locale);
   609         if (status) {
   610             XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME);
   611             XFree(titleprop.value);
   612         }
   613 #ifdef X_HAVE_UTF8_STRING
   614         if (SDL_X11_HAVE_UTF8) {
   615             status =
   616                 Xutf8TextListToTextProperty(display, (char **) &title, 1,
   617                                             XUTF8StringStyle, &titleprop);
   618             if (status == Success) {
   619                 XSetTextProperty(display, data->xwindow, &titleprop,
   620                                  _NET_WM_NAME);
   621                 XFree(titleprop.value);
   622             }
   623         }
   624 #endif
   625     }
   626     if (icon != NULL) {
   627         char *icon_locale = SDL_iconv_utf8_locale(icon);
   628         if (!icon_locale) {
   629             SDL_OutOfMemory();
   630             return;
   631         }
   632         status = XStringListToTextProperty(&icon_locale, 1, &iconprop);
   633         SDL_free(icon_locale);
   634         if (status) {
   635             XSetTextProperty(display, data->xwindow, &iconprop,
   636                              XA_WM_ICON_NAME);
   637             XFree(iconprop.value);
   638         }
   639 #ifdef X_HAVE_UTF8_STRING
   640         if (SDL_X11_HAVE_UTF8) {
   641             status =
   642                 Xutf8TextListToTextProperty(display, (char **) &icon, 1,
   643                                             XUTF8StringStyle, &iconprop);
   644             if (status == Success) {
   645                 XSetTextProperty(display, data->xwindow, &iconprop,
   646                                  _NET_WM_ICON_NAME);
   647                 XFree(iconprop.value);
   648             }
   649         }
   650 #endif
   651     }
   652     XFlush(display);
   653 }
   654 
   655 void
   656 X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   657 {
   658     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   659     Display *display = data->videodata->display;
   660     Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON;
   661 
   662     if (icon) {
   663         SDL_PixelFormat format;
   664         SDL_Surface *surface;
   665         int propsize;
   666         long *propdata;
   667 
   668         /* Convert the icon to ARGB for modern window managers */
   669         SDL_InitFormat(&format, SDL_PIXELFORMAT_ARGB8888);
   670         surface = SDL_ConvertSurface(icon, &format, 0);
   671         if (!surface) {
   672             return;
   673         }
   674 
   675         /* Set the _NET_WM_ICON property */
   676         propsize = 2 + (icon->w * icon->h);
   677         propdata = SDL_malloc(propsize * sizeof(long));
   678         if (propdata) {
   679             int x, y;
   680             Uint32 *src;
   681             long *dst;
   682 
   683             propdata[0] = icon->w;
   684             propdata[1] = icon->h;
   685             dst = &propdata[2];
   686             for (y = 0; y < icon->h; ++y) {
   687                 src = (Uint32*)((Uint8*)surface->pixels + y * surface->pitch);
   688                 for (x = 0; x < icon->w; ++x) {
   689                     *dst++ = *src++;
   690                 }
   691             }
   692             XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
   693                             32, PropModeReplace, (unsigned char *) propdata,
   694                             propsize);
   695         }
   696         SDL_FreeSurface(surface);
   697     } else {
   698         XDeleteProperty(display, data->xwindow, _NET_WM_ICON);
   699     }
   700     XFlush(display);
   701 }
   702 
   703 void
   704 X11_SetWindowPosition(_THIS, SDL_Window * window)
   705 {
   706     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   707     Display *display = data->videodata->display;
   708     SDL_bool oldstyle_fullscreen;
   709     int x, y;
   710 
   711     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
   712     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
   713 
   714     if (oldstyle_fullscreen
   715         || SDL_WINDOWPOS_ISCENTERED(window->x)) {
   716         X11_GetDisplaySize(_this, window, &x, NULL);
   717         x = (x - window->w) / 2;
   718     } else {
   719         x = window->x;
   720     }
   721     if (oldstyle_fullscreen
   722         || SDL_WINDOWPOS_ISCENTERED(window->y)) {
   723         X11_GetDisplaySize(_this, window, NULL, &y);
   724         y = (y - window->h) / 2;
   725     } else {
   726         y = window->y;
   727     }
   728     XMoveWindow(display, data->xwindow, x, y);
   729     XFlush(display);
   730 }
   731 
   732 void
   733 X11_SetWindowSize(_THIS, SDL_Window * window)
   734 {
   735     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   736     Display *display = data->videodata->display;
   737 
   738     if (SDL_IsShapedWindow(window))
   739         X11_ResizeWindowShape(window);
   740     XResizeWindow(display, data->xwindow, window->w, window->h);
   741     XFlush(display);
   742 }
   743 
   744 void
   745 X11_ShowWindow(_THIS, SDL_Window * window)
   746 {
   747     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   748     Display *display = data->videodata->display;
   749 
   750     XMapRaised(display, data->xwindow);
   751     XFlush(display);
   752 }
   753 
   754 void
   755 X11_HideWindow(_THIS, SDL_Window * window)
   756 {
   757     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   758     Display *display = data->videodata->display;
   759 
   760     XUnmapWindow(display, data->xwindow);
   761     XFlush(display);
   762 }
   763 
   764 void
   765 X11_RaiseWindow(_THIS, SDL_Window * window)
   766 {
   767     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   768     Display *display = data->videodata->display;
   769 
   770     XRaiseWindow(display, data->xwindow);
   771     XFlush(display);
   772 }
   773 
   774 static void
   775 SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized)
   776 {
   777     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   778     SDL_DisplayData *displaydata =
   779         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   780     Display *display = data->videodata->display;
   781     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
   782     Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
   783     Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
   784     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
   785 
   786     if (X11_IsWindowMapped(_this, window)) {
   787         XEvent e;
   788 
   789         SDL_zero(e);
   790         e.xany.type = ClientMessage;
   791         e.xclient.message_type = _NET_WM_STATE;
   792         e.xclient.format = 32;
   793         e.xclient.window = data->xwindow;
   794         e.xclient.data.l[0] =
   795             maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   796         e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
   797         e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
   798         e.xclient.data.l[3] = 0l;
   799 
   800         XSendEvent(display, RootWindow(display, displaydata->screen), 0,
   801                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
   802     } else {
   803         int count = 0;
   804         Atom atoms[3];
   805 
   806         if (window->flags & SDL_WINDOW_FULLSCREEN) {
   807             atoms[count++] = _NET_WM_STATE_FULLSCREEN;
   808         }
   809         if (maximized) {
   810             atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
   811             atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
   812         }
   813         if (count > 0) {
   814             XChangeProperty(display, data->xwindow, _NET_WM_STATE, XA_ATOM, 32,
   815                             PropModeReplace, (unsigned char *)atoms, count);
   816         } else {
   817             XDeleteProperty(display, data->xwindow, _NET_WM_STATE);
   818         }
   819     }
   820     XFlush(display);
   821 }
   822 
   823 void
   824 X11_MaximizeWindow(_THIS, SDL_Window * window)
   825 {
   826     SetWindowMaximized(_this, window, SDL_TRUE);
   827 }
   828 
   829 void
   830 X11_MinimizeWindow(_THIS, SDL_Window * window)
   831 {
   832     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   833     SDL_DisplayData *displaydata =
   834         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   835     Display *display = data->videodata->display;
   836  
   837     XIconifyWindow(display, data->xwindow, displaydata->screen);
   838     XFlush(display);
   839 }
   840 
   841 void
   842 X11_RestoreWindow(_THIS, SDL_Window * window)
   843 {
   844     SetWindowMaximized(_this, window, SDL_FALSE);
   845     X11_ShowWindow(_this, window);
   846 }
   847 
   848 static void
   849 SetWindowFullscreen(_THIS, SDL_Window * window, SDL_bool fullscreen)
   850 {
   851     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   852     SDL_DisplayData *displaydata =
   853         (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
   854     Display *display = data->videodata->display;
   855     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
   856     Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
   857     Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
   858     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
   859 
   860     if (X11_IsWindowMapped(_this, window)) {
   861         XEvent e;
   862 
   863         SDL_zero(e);
   864         e.xany.type = ClientMessage;
   865         e.xclient.message_type = _NET_WM_STATE;
   866         e.xclient.format = 32;
   867         e.xclient.window = data->xwindow;
   868         e.xclient.data.l[0] =
   869             fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   870         e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN;
   871         e.xclient.data.l[3] = 0l;
   872 
   873         XSendEvent(display, RootWindow(display, displaydata->screen), 0,
   874                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
   875     } else {
   876         int count = 0;
   877         Atom atoms[3];
   878 
   879         if (fullscreen) {
   880             atoms[count++] = _NET_WM_STATE_FULLSCREEN;
   881         }
   882         if (window->flags & SDL_WINDOW_MAXIMIZED) {
   883             atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
   884             atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
   885         }
   886         if (count > 0) {
   887             XChangeProperty(display, data->xwindow, _NET_WM_STATE, XA_ATOM, 32,
   888                             PropModeReplace, (unsigned char *)atoms, count);
   889         } else {
   890             XDeleteProperty(display, data->xwindow, _NET_WM_STATE);
   891         }
   892     }
   893     XFlush(display);
   894 }
   895 
   896 void
   897 X11_SetWindowFullscreen(_THIS, SDL_Window * window)
   898 {
   899     if (FULLSCREEN_VISIBLE(window)) {
   900         SetWindowFullscreen(_this, window, SDL_TRUE);
   901     } else {
   902         SetWindowFullscreen(_this, window, SDL_FALSE);
   903     }
   904 }
   905 
   906 void
   907 X11_SetWindowGrab(_THIS, SDL_Window * window)
   908 {
   909     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   910     Display *display = data->videodata->display;
   911     SDL_bool oldstyle_fullscreen;
   912 
   913     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
   914     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
   915 
   916     if (((window->flags & SDL_WINDOW_INPUT_GRABBED) || oldstyle_fullscreen)
   917         && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
   918         /* Try to grab the mouse */
   919         for (;;) {
   920             int result =
   921                 XGrabPointer(display, data->xwindow, True, 0, GrabModeAsync,
   922                              GrabModeAsync, data->xwindow, None, CurrentTime);
   923             if (result == GrabSuccess) {
   924                 break;
   925             }
   926             SDL_Delay(100);
   927         }
   928 
   929         /* Raise the window if we grab the mouse */
   930         XRaiseWindow(display, data->xwindow);
   931 
   932         /* Now grab the keyboard */
   933         XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
   934                       GrabModeAsync, CurrentTime);
   935     } else {
   936         XUngrabPointer(display, CurrentTime);
   937         XUngrabKeyboard(display, CurrentTime);
   938     }
   939 }
   940 
   941 void
   942 X11_DestroyWindow(_THIS, SDL_Window * window)
   943 {
   944     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   945     window->driverdata = NULL;
   946 
   947     if (data) {
   948         SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
   949         Display *display = videodata->display;
   950         int numwindows = videodata->numwindows;
   951         SDL_WindowData **windowlist = videodata->windowlist;
   952         int i;
   953 
   954         if (windowlist) {
   955             for (i = 0; i < numwindows; ++i) {
   956                 if (windowlist[i] && (windowlist[i]->window == window)) {
   957                     windowlist[i] = windowlist[numwindows - 1];
   958                     windowlist[numwindows - 1] = NULL;
   959                     videodata->numwindows--;
   960                     break;
   961                 }
   962             }
   963         }
   964 #ifdef X_HAVE_UTF8_STRING
   965         if (data->ic) {
   966             XDestroyIC(data->ic);
   967         }
   968 #endif
   969         if (data->created) {
   970             XDestroyWindow(display, data->xwindow);
   971             XFlush(display);
   972         }
   973         SDL_free(data);
   974     }
   975 }
   976 
   977 SDL_bool
   978 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   979 {
   980     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   981     Display *display = data->videodata->display;
   982 
   983     if (info->version.major == SDL_MAJOR_VERSION &&
   984         info->version.minor == SDL_MINOR_VERSION) {
   985         info->subsystem = SDL_SYSWM_X11;
   986         info->info.x11.display = display;
   987         info->info.x11.window = data->xwindow;
   988         return SDL_TRUE;
   989     } else {
   990         SDL_SetError("Application not compiled with SDL %d.%d\n",
   991                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   992         return SDL_FALSE;
   993     }
   994 }
   995 
   996 /* vi: set ts=4 sw=4 expandtab: */