src/video/x11/SDL_x11window.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 29 Sep 2010 21:13:52 -0700
changeset 4902 50d0bff24d81
parent 4862 7b1d35d98294
child 4937 24d44c7c4c63
permissions -rw-r--r--
Make the union nameless to reduce the complexity of the API.
(Are there any compilers still in use that don't support this?)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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_x11gamma.h"
    32 #include "SDL_x11shape.h"
    33 #include "../Xext/extensions/StdCmap.h"
    34 
    35 #ifdef SDL_VIDEO_DRIVER_PANDORA
    36 #include "SDL_x11opengles.h"
    37 #endif
    38 
    39 #include "SDL_timer.h"
    40 #include "SDL_syswm.h"
    41 
    42 #define _NET_WM_STATE_REMOVE    0l
    43 #define _NET_WM_STATE_ADD       1l
    44 #define _NET_WM_STATE_TOGGLE    2l
    45 
    46 static SDL_bool
    47 X11_IsWindowOldFullscreen(_THIS, SDL_Window * window)
    48 {
    49     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    50 
    51     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    52     if ((window->flags & SDL_WINDOW_FULLSCREEN) && !videodata->net_wm) {
    53         return SDL_TRUE;
    54     } else {
    55         return SDL_FALSE;
    56     }
    57 }
    58 
    59 static SDL_bool
    60 X11_IsWindowMapped(_THIS, SDL_Window * window)
    61 {
    62     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    63     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    64     XWindowAttributes attr;
    65 
    66     XGetWindowAttributes(videodata->display, data->xwindow, &attr);
    67     if (attr.map_state != IsUnmapped) {
    68         return SDL_TRUE;
    69     } else {
    70         return SDL_FALSE;
    71     }
    72 }
    73 
    74 static int
    75 X11_GetWMStateProperty(_THIS, SDL_Window * window, Atom atoms[3])
    76 {
    77     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    78     int count = 0;
    79 
    80     if (window->flags & SDL_WINDOW_FULLSCREEN) {
    81         atoms[count++] = data->_NET_WM_STATE_FULLSCREEN;
    82     }
    83     if (window->flags & SDL_WINDOW_MAXIMIZED) {
    84         atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_VERT;
    85         atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_HORZ;
    86     }
    87     return count;
    88 }
    89 
    90 static void
    91 X11_GetDisplaySize(_THIS, SDL_Window * window, int *w, int *h)
    92 {
    93     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    94     SDL_DisplayData *displaydata =
    95         (SDL_DisplayData *) window->display->driverdata;
    96     XWindowAttributes attr;
    97 
    98     XGetWindowAttributes(data->display, RootWindow(data->display, displaydata->screen), &attr);
    99     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   100         /* The bounds when this window is visible is the fullscreen mode */
   101         SDL_DisplayMode fullscreen_mode;
   102         if (SDL_GetWindowDisplayMode(window, &fullscreen_mode) == 0) {
   103             attr.width = fullscreen_mode.w;
   104             attr.height = fullscreen_mode.h;
   105         }
   106     }
   107     if (w) {
   108         *w = attr.width;
   109     }
   110     if (h) {
   111         *h = attr.height;
   112     }
   113 }
   114 
   115 static int
   116 SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
   117 {
   118     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   119     SDL_WindowData *data;
   120     int numwindows = videodata->numwindows;
   121     int windowlistlength = videodata->windowlistlength;
   122     SDL_WindowData **windowlist = videodata->windowlist;
   123 
   124     /* Allocate the window data */
   125     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
   126     if (!data) {
   127         SDL_OutOfMemory();
   128         return -1;
   129     }
   130     data->window = window;
   131     data->xwindow = w;
   132 #ifdef X_HAVE_UTF8_STRING
   133     if (SDL_X11_HAVE_UTF8) {
   134         data->ic =
   135             pXCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w,
   136                        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
   137                        XNResourceName, videodata->classname, XNResourceClass,
   138                        videodata->classname, NULL);
   139     }
   140 #endif
   141     data->created = created;
   142     data->videodata = videodata;
   143 
   144     /* Associate the data with the window */
   145 
   146     if (numwindows < windowlistlength) {
   147         windowlist[numwindows] = data;
   148         videodata->numwindows++;
   149     } else {
   150         windowlist =
   151             (SDL_WindowData **) SDL_realloc(windowlist,
   152                                             (numwindows +
   153                                              1) * sizeof(*windowlist));
   154         if (!windowlist) {
   155             SDL_OutOfMemory();
   156             SDL_free(data);
   157             return -1;
   158         }
   159         windowlist[numwindows] = data;
   160         videodata->numwindows++;
   161         videodata->windowlistlength++;
   162         videodata->windowlist = windowlist;
   163     }
   164 
   165     /* Fill in the SDL window with the window data */
   166     {
   167         XWindowAttributes attrib;
   168 
   169         XGetWindowAttributes(data->videodata->display, w, &attrib);
   170         window->x = attrib.x;
   171         window->y = attrib.y;
   172         window->w = attrib.width;
   173         window->h = attrib.height;
   174         if (attrib.map_state != IsUnmapped) {
   175             window->flags |= SDL_WINDOW_SHOWN;
   176         } else {
   177             window->flags &= ~SDL_WINDOW_SHOWN;
   178         }
   179     }
   180 
   181     {
   182         Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
   183         Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
   184         Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
   185         Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
   186         Atom actualType;
   187         int actualFormat;
   188         unsigned long i, numItems, bytesAfter;
   189         unsigned char *propertyValue = NULL;
   190         long maxLength = 1024;
   191 
   192         if (XGetWindowProperty(data->videodata->display, w, _NET_WM_STATE,
   193                                0l, maxLength, False, XA_ATOM, &actualType,
   194                                &actualFormat, &numItems, &bytesAfter,
   195                                &propertyValue) == Success) {
   196             Atom *atoms = (Atom *) propertyValue;
   197             int maximized = 0;
   198             int fullscreen = 0;
   199 
   200             for (i = 0; i < numItems; ++i) {
   201                 if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
   202                     maximized |= 1;
   203                 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
   204                     maximized |= 2;
   205                 } else if ( atoms[i] == _NET_WM_STATE_FULLSCREEN) {
   206                     fullscreen = 1;
   207                 }
   208             }
   209             if (maximized == 3) {
   210                 window->flags |= SDL_WINDOW_MAXIMIZED;
   211             }  else if (fullscreen == 1) {
   212                 window->flags |= SDL_WINDOW_FULLSCREEN;
   213             }
   214             XFree(propertyValue);
   215         }
   216     }
   217 
   218     /* FIXME: How can I tell?
   219        {
   220        DWORD style = GetWindowLong(hwnd, GWL_STYLE);
   221        if (style & WS_VISIBLE) {
   222        if (style & (WS_BORDER | WS_THICKFRAME)) {
   223        window->flags &= ~SDL_WINDOW_BORDERLESS;
   224        } else {
   225        window->flags |= SDL_WINDOW_BORDERLESS;
   226        }
   227        if (style & WS_THICKFRAME) {
   228        window->flags |= SDL_WINDOW_RESIZABLE;
   229        } else {
   230        window->flags &= ~SDL_WINDOW_RESIZABLE;
   231        }
   232        if (style & WS_MINIMIZE) {
   233        window->flags |= SDL_WINDOW_MINIMIZED;
   234        } else {
   235        window->flags &= ~SDL_WINDOW_MINIMIZED;
   236        }
   237        }
   238        if (GetFocus() == hwnd) {
   239        int index = data->videodata->keyboard;
   240        window->flags |= SDL_WINDOW_INPUT_FOCUS;
   241        SDL_SetKeyboardFocus(index, data->window);
   242 
   243        if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   244        RECT rect;
   245        GetClientRect(hwnd, &rect);
   246        ClientToScreen(hwnd, (LPPOINT) & rect);
   247        ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   248        ClipCursor(&rect);
   249        }
   250        }
   251      */
   252 
   253     /* All done! */
   254     window->driverdata = data;
   255     return 0;
   256 }
   257 
   258 int
   259 X11_CreateWindow(_THIS, SDL_Window * window)
   260 {
   261     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   262     SDL_DisplayData *displaydata =
   263         (SDL_DisplayData *) window->display->driverdata;
   264     Display *display = data->display;
   265     int screen = displaydata->screen;
   266     Visual *visual;
   267     int depth;
   268     XSetWindowAttributes xattr;
   269     int x, y;
   270     Window w;
   271     XSizeHints *sizehints;
   272     XWMHints *wmhints;
   273     XClassHint *classhints;
   274     SDL_bool oldstyle_fullscreen;
   275     Atom _NET_WM_WINDOW_TYPE;
   276     Atom _NET_WM_WINDOW_TYPE_NORMAL;
   277     int wmstate_count;
   278     Atom wmstate_atoms[3];
   279 
   280     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
   281     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
   282 
   283 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   284 /* FIXME
   285     if ( use_xinerama ) {
   286         x = xinerama_info.x_org;
   287         y = xinerama_info.y_org;
   288     }
   289 */
   290 #endif
   291 #ifdef SDL_VIDEO_OPENGL_GLX
   292     if (window->flags & SDL_WINDOW_OPENGL) {
   293         XVisualInfo *vinfo;
   294 
   295         vinfo = X11_GL_GetVisual(_this, display, screen);
   296         if (!vinfo) {
   297             return -1;
   298         }
   299         visual = vinfo->visual;
   300         depth = vinfo->depth;
   301         XFree(vinfo);
   302     } else
   303 #endif
   304 #ifdef SDL_VIDEO_DRIVER_PANDORA
   305     if (window->flags & SDL_WINDOW_OPENGL) {
   306         XVisualInfo *vinfo;
   307 
   308         vinfo = X11_GLES_GetVisual(_this, display, screen);
   309         if (!vinfo) {
   310             return -1;
   311         }
   312         visual = vinfo->visual;
   313         depth = vinfo->depth;
   314         XFree(vinfo);
   315     } else
   316 #endif
   317     {
   318         visual = displaydata->visual;
   319         depth = displaydata->depth;
   320     }
   321 
   322     if (oldstyle_fullscreen) {
   323         xattr.override_redirect = True;
   324     } else {
   325         xattr.override_redirect = False;
   326     }
   327     xattr.background_pixel = 0;
   328     xattr.border_pixel = 0;
   329 
   330     if (visual->class == PseudoColor) {
   331         printf("asking for PseudoColor\n");
   332 
   333 /*      Status status; */
   334         XColor *colorcells;
   335         Colormap colormap;
   336         Sint32 pix;
   337         Sint32 ncolors;
   338         Sint32 nbits;
   339         Sint32 rmax, gmax, bmax;
   340         Sint32 rwidth, gwidth, bwidth;
   341         Sint32 rmask, gmask, bmask;
   342         Sint32 rshift, gshift, bshift;
   343         Sint32 r, g, b;
   344 
   345         /* Is the colormap we need already registered in SDL? */
   346         if ((colormap =
   347             X11_LookupColormap(display, screen, visual->visualid))) {
   348             xattr.colormap = colormap;
   349 /*             printf("found existing colormap\n"); */
   350         } else {
   351             /* The colormap is not known to SDL so we will create it */
   352             colormap = XCreateColormap(display, RootWindow(display, screen),
   353                                        visual, AllocAll);
   354 /*             printf("colormap = %x\n", colormap); */
   355 
   356             /* If we can't create a colormap, then we must die */
   357             if (!colormap) {
   358                 SDL_SetError
   359                     ("Couldn't create window: Could not create writable colormap");
   360                 return -1;
   361             }
   362 
   363             /* OK, we got a colormap, now fill it in as best as we can */
   364 
   365             colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
   366             if (NULL == colorcells) {
   367                 SDL_SetError("out of memory in X11_CreateWindow");
   368                 return -1;
   369             }
   370 
   371             ncolors = visual->map_entries;
   372             nbits = visual->bits_per_rgb;
   373 
   374 /* 	    printf("ncolors = %d nbits = %d\n", ncolors, nbits); */
   375 
   376             /* what if ncolors != (1 << nbits)? That can happen on a
   377                true PseudoColor display.  I'm assuming that we will
   378                always have ncolors == (1 << nbits) */
   379 
   380             /* I'm making a lot of assumptions here. */
   381 
   382             /* Compute the width of each field. If there is one extra
   383                bit, give it to green. If there are two extra bits give
   384                them to red and greed.  We can get extra bits when the
   385                number of bits per pixel is not a multiple of 3. For
   386                example when we have 16 bits per pixel and need a 5/6/5
   387                layout for the RGB fields */
   388 
   389             rwidth = (nbits / 3) + (((nbits % 3) == 2) ? 1 : 0);
   390             gwidth = (nbits / 3) + (((nbits % 3) >= 1) ? 1 : 0);
   391             bwidth = (nbits / 3);
   392 
   393             rshift = gwidth + bwidth;
   394             gshift = bwidth;
   395             bshift = 0;
   396 
   397             rmax = 1 << rwidth;
   398             gmax = 1 << gwidth;
   399             bmax = 1 << bwidth;
   400 
   401             rmask = rmax - 1;
   402             gmask = gmax - 1;
   403             bmask = bmax - 1;
   404 
   405 /*             printf("red   mask = %4x shift = %4d width = %d\n", rmask, rshift, rwidth); */
   406 /*             printf("green mask = %4x shift = %4d width = %d\n", gmask, gshift, gwidth); */
   407 /*             printf("blue  mask = %4x shift = %4d width = %d\n", bmask, bshift, bwidth); */
   408 
   409             /* build the color table pixel values */
   410             pix = 0;
   411             for (r = 0; r < rmax; r++) {
   412                 for (g = 0; g < gmax; g++) {
   413                     for (b = 0; b < bmax; b++) {
   414                         colorcells[pix].pixel =
   415                             (r << rshift) | (g << gshift) | (b << bshift);
   416                         colorcells[pix].red = (0xffff * r) / rmask;
   417                         colorcells[pix].green = (0xffff * g) / gmask;
   418                         colorcells[pix].blue = (0xffff * b) / bmask;
   419 /* 		  printf("%4x:%4x [%4x %4x %4x]\n",  */
   420 /* 			 pix,  */
   421 /* 			 colorcells[pix].pixel, */
   422 /* 			 colorcells[pix].red, */
   423 /* 			 colorcells[pix].green, */
   424 /* 			 colorcells[pix].blue); */
   425                         pix++;
   426                     }
   427                 }
   428             }
   429 
   430 /*             status = */
   431 /*                 XStoreColors(display, colormap, colorcells, ncolors); */
   432 
   433             xattr.colormap = colormap;
   434             X11_TrackColormap(display, screen, colormap, visual, NULL);
   435 
   436             SDL_free(colorcells);
   437         }
   438     } else if (visual->class == DirectColor) {
   439         Status status;
   440         XColor *colorcells;
   441         Colormap colormap;
   442         int i;
   443         int ncolors;
   444         int rmax, gmax, bmax;
   445         int rmask, gmask, bmask;
   446         int rshift, gshift, bshift;
   447 
   448         /* Is the colormap we need already registered in SDL? */
   449         if ((colormap =
   450              X11_LookupColormap(display, screen, visual->visualid))) {
   451             xattr.colormap = colormap;
   452 /*             printf("found existing colormap\n"); */
   453         } else {
   454             /* The colormap is not known to SDL so we will create it */
   455             colormap = XCreateColormap(display, RootWindow(display, screen),
   456                                        visual, AllocAll);
   457 /*             printf("colormap = %x\n", colormap); */
   458 
   459             /* If we can't create a colormap, then we must die */
   460             if (!colormap) {
   461                 SDL_SetError
   462                     ("Couldn't create window: Could not create writable colormap");
   463                 return -1;
   464             }
   465 
   466             /* OK, we got a colormap, now fill it in as best as we can */
   467             colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
   468             if (NULL == colorcells) {
   469                 SDL_SetError("out of memory in X11_CreateWindow");
   470                 return -1;
   471             }
   472             ncolors = visual->map_entries;
   473             rmax = 0xffff;
   474             gmax = 0xffff;
   475             bmax = 0xffff;
   476 
   477             rshift = 0;
   478             rmask = visual->red_mask;
   479             while (0 == (rmask & 1)) {
   480                 rshift++;
   481                 rmask >>= 1;
   482             }
   483 
   484 /*             printf("rmask = %4x rshift = %4d\n", rmask, rshift); */
   485 
   486             gshift = 0;
   487             gmask = visual->green_mask;
   488             while (0 == (gmask & 1)) {
   489                 gshift++;
   490                 gmask >>= 1;
   491             }
   492 
   493 /*             printf("gmask = %4x gshift = %4d\n", gmask, gshift); */
   494 
   495             bshift = 0;
   496             bmask = visual->blue_mask;
   497             while (0 == (bmask & 1)) {
   498                 bshift++;
   499                 bmask >>= 1;
   500             }
   501 
   502 /*             printf("bmask = %4x bshift = %4d\n", bmask, bshift); */
   503 
   504             /* build the color table pixel values */
   505             for (i = 0; i < ncolors; i++) {
   506                 Uint32 red = (rmax * i) / (ncolors - 1);
   507                 Uint32 green = (gmax * i) / (ncolors - 1);
   508                 Uint32 blue = (bmax * i) / (ncolors - 1);
   509 
   510                 Uint32 rbits = (rmask * i) / (ncolors - 1);
   511                 Uint32 gbits = (gmask * i) / (ncolors - 1);
   512                 Uint32 bbits = (bmask * i) / (ncolors - 1);
   513 
   514                 Uint32 pix =
   515                     (rbits << rshift) | (gbits << gshift) | (bbits << bshift);
   516 
   517                 colorcells[i].pixel = pix;
   518 
   519                 colorcells[i].red = red;
   520                 colorcells[i].green = green;
   521                 colorcells[i].blue = blue;
   522 
   523                 colorcells[i].flags = DoRed | DoGreen | DoBlue;
   524 /* 		printf("%2d:%4x [%4x %4x %4x]\n", i, pix, red, green, blue); */
   525             }
   526 
   527             status =
   528                 XStoreColors(display, colormap, colorcells, ncolors);
   529 
   530             xattr.colormap = colormap;
   531             X11_TrackColormap(display, screen, colormap, visual, colorcells);
   532 
   533             SDL_free(colorcells);
   534         }
   535     } else {
   536         xattr.colormap =
   537             XCreateColormap(display, RootWindow(display, screen),
   538                             visual, AllocNone);
   539     }
   540 
   541     if (oldstyle_fullscreen
   542         || window->x == SDL_WINDOWPOS_CENTERED) {
   543         X11_GetDisplaySize(_this, window, &x, NULL);
   544         x = (x - window->w) / 2;
   545     } else if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   546         x = 0;
   547     } else {
   548         x = window->x;
   549     }
   550     if (oldstyle_fullscreen
   551         || window->y == SDL_WINDOWPOS_CENTERED) {
   552         X11_GetDisplaySize(_this, window, NULL, &y);
   553         y = (y - window->h) / 2;
   554     } else if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   555         y = 0;
   556     } else {
   557         y = window->y;
   558     }
   559 
   560     w = XCreateWindow(display, RootWindow(display, screen), x, y,
   561                       window->w, window->h, 0, depth, InputOutput, visual,
   562                       (CWOverrideRedirect | CWBackPixel | CWBorderPixel |
   563                        CWColormap), &xattr);
   564     if (!w) {
   565         SDL_SetError("Couldn't create window");
   566         return -1;
   567     }
   568 #if SDL_VIDEO_DRIVER_PANDORA
   569     /* Create the GLES window surface */
   570     _this->gles_data->egl_surface =
   571         _this->gles_data->eglCreateWindowSurface(_this->gles_data->
   572                                                  egl_display,
   573                                                  _this->gles_data->egl_config,
   574                                                  (NativeWindowType) w, NULL);
   575 
   576     if (_this->gles_data->egl_surface == EGL_NO_SURFACE) {
   577         SDL_SetError("Could not create GLES window surface");
   578         return -1;
   579     }
   580 #endif
   581 
   582     sizehints = XAllocSizeHints();
   583     if (sizehints) {
   584         if (!(window->flags & SDL_WINDOW_RESIZABLE)
   585             || oldstyle_fullscreen) {
   586             sizehints->min_width = sizehints->max_width = window->w;
   587             sizehints->min_height = sizehints->max_height = window->h;
   588             sizehints->flags = PMaxSize | PMinSize;
   589         }
   590         if (!oldstyle_fullscreen
   591             && window->x != SDL_WINDOWPOS_UNDEFINED
   592             && window->y != SDL_WINDOWPOS_UNDEFINED) {
   593             sizehints->x = x;
   594             sizehints->y = y;
   595             sizehints->flags |= USPosition;
   596         }
   597         XSetWMNormalHints(display, w, sizehints);
   598         XFree(sizehints);
   599     }
   600 
   601     if ((window->flags & SDL_WINDOW_BORDERLESS) || oldstyle_fullscreen) {
   602         SDL_bool set;
   603         Atom WM_HINTS;
   604 
   605         /* We haven't modified the window manager hints yet */
   606         set = SDL_FALSE;
   607 
   608         /* First try to set MWM hints */
   609         WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
   610         if (WM_HINTS != None) {
   611             /* Hints used by Motif compliant window managers */
   612             struct
   613             {
   614                 unsigned long flags;
   615                 unsigned long functions;
   616                 unsigned long decorations;
   617                 long input_mode;
   618                 unsigned long status;
   619             } MWMHints = {
   620             (1L << 1), 0, 0, 0, 0};
   621 
   622             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   623                             PropModeReplace, (unsigned char *) &MWMHints,
   624                             sizeof(MWMHints) / 4);
   625             set = SDL_TRUE;
   626         }
   627         /* Now try to set KWM hints */
   628         WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
   629         if (WM_HINTS != None) {
   630             long KWMHints = 0;
   631 
   632             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   633                             PropModeReplace,
   634                             (unsigned char *) &KWMHints,
   635                             sizeof(KWMHints) / 4);
   636             set = SDL_TRUE;
   637         }
   638         /* Now try to set GNOME hints */
   639         WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
   640         if (WM_HINTS != None) {
   641             long GNOMEHints = 0;
   642 
   643             XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
   644                             PropModeReplace,
   645                             (unsigned char *) &GNOMEHints,
   646                             sizeof(GNOMEHints) / 4);
   647             set = SDL_TRUE;
   648         }
   649         /* Finally set the transient hints if necessary */
   650         if (!set) {
   651             XSetTransientForHint(display, w, RootWindow(display, screen));
   652         }
   653     } else {
   654         SDL_bool set;
   655         Atom WM_HINTS;
   656 
   657         /* We haven't modified the window manager hints yet */
   658         set = SDL_FALSE;
   659 
   660         /* First try to unset MWM hints */
   661         WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
   662         if (WM_HINTS != None) {
   663             XDeleteProperty(display, w, WM_HINTS);
   664             set = SDL_TRUE;
   665         }
   666         /* Now try to unset KWM hints */
   667         WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
   668         if (WM_HINTS != None) {
   669             XDeleteProperty(display, w, WM_HINTS);
   670             set = SDL_TRUE;
   671         }
   672         /* Now try to unset GNOME hints */
   673         WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
   674         if (WM_HINTS != None) {
   675             XDeleteProperty(display, w, WM_HINTS);
   676             set = SDL_TRUE;
   677         }
   678         /* Finally unset the transient hints if necessary */
   679         if (!set) {
   680             XDeleteProperty(display, w, XA_WM_TRANSIENT_FOR);
   681         }
   682     }
   683 
   684     /* Set the input hints so we get keyboard input */
   685     wmhints = XAllocWMHints();
   686     if (wmhints) {
   687         wmhints->input = True;
   688         wmhints->flags = InputHint;
   689         XSetWMHints(display, w, wmhints);
   690         XFree(wmhints);
   691     }
   692 
   693     /* Set the class hints so we can get an icon (AfterStep) */
   694     classhints = XAllocClassHint();
   695     if (classhints != NULL) {
   696         classhints->res_name = data->classname;
   697         classhints->res_class = data->classname;
   698         XSetClassHint(display, w, classhints);
   699         XFree(classhints);
   700     }
   701 
   702     /* Set the window manager state */
   703     wmstate_count = X11_GetWMStateProperty(_this, window, wmstate_atoms);
   704     if (wmstate_count > 0) {
   705         XChangeProperty(display, w, data->_NET_WM_STATE, XA_ATOM, 32,
   706                         PropModeReplace,
   707                         (unsigned char *)wmstate_atoms, wmstate_count);
   708     } else {
   709         XDeleteProperty(display, w, data->_NET_WM_STATE);
   710     }
   711 
   712     /* Let the window manager know we're a "normal" window */
   713     _NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
   714     _NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
   715     XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   716                     PropModeReplace,
   717                     (unsigned char *)&_NET_WM_WINDOW_TYPE_NORMAL, 1);
   718 
   719     /* Allow the window to be deleted by the window manager */
   720     XSetWMProtocols(display, w, &data->WM_DELETE_WINDOW, 1);
   721 
   722     if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
   723         XDestroyWindow(display, w);
   724         return -1;
   725     }
   726 #ifdef X_HAVE_UTF8_STRING
   727     {
   728         Uint32 fevent = 0;
   729         pXGetICValues(((SDL_WindowData *) window->driverdata)->ic,
   730                       XNFilterEvents, &fevent, NULL);
   731         XSelectInput(display, w,
   732                      (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   733                       ExposureMask | ButtonPressMask | ButtonReleaseMask |
   734                       PointerMotionMask | KeyPressMask | KeyReleaseMask |
   735                       PropertyChangeMask | StructureNotifyMask |
   736                       KeymapStateMask | fevent));
   737     }
   738 #else
   739     {
   740         XSelectInput(display, w,
   741                      (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   742                       ExposureMask | ButtonPressMask | ButtonReleaseMask |
   743                       PointerMotionMask | KeyPressMask | KeyReleaseMask |
   744                       PropertyChangeMask | StructureNotifyMask |
   745                       KeymapStateMask));
   746     }
   747 #endif
   748 
   749     return 0;
   750 }
   751 
   752 int
   753 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   754 {
   755     Window w = (Window) data;
   756 
   757     window->title = X11_GetWindowTitle(_this, w);
   758 
   759     if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
   760         return -1;
   761     }
   762     return 0;
   763 }
   764 
   765 char *
   766 X11_GetWindowTitle(_THIS, Window xwindow)
   767 {
   768     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   769     Display *display = data->display;
   770     int status, real_format;
   771     Atom real_type;
   772     unsigned long items_read, items_left;
   773     unsigned char *propdata;
   774     char *title = NULL;
   775 
   776     status = XGetWindowProperty(display, xwindow, data->_NET_WM_NAME,
   777                 0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format,
   778                 &items_read, &items_left, &propdata);
   779     if (status == Success) {
   780         title = SDL_strdup(SDL_static_cast(char*, propdata));
   781         XFree(propdata);
   782     } else {
   783         status = XGetWindowProperty(display, xwindow, XA_WM_NAME,
   784                     0L, 8192L, False, XA_STRING, &real_type, &real_format,
   785                     &items_read, &items_left, &propdata);
   786         if (status == Success) {
   787             title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1);
   788         } else {
   789             title = SDL_strdup("");
   790         }
   791     }
   792     return title;
   793 }
   794 
   795 void
   796 X11_SetWindowTitle(_THIS, SDL_Window * window)
   797 {
   798     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   799     Display *display = data->videodata->display;
   800     XTextProperty titleprop, iconprop;
   801     Status status;
   802     const char *title = window->title;
   803     const char *icon = NULL;
   804 
   805 #ifdef X_HAVE_UTF8_STRING
   806     Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME;
   807     Atom _NET_WM_ICON_NAME = data->videodata->_NET_WM_ICON_NAME;
   808 #endif
   809 
   810     if (title != NULL) {
   811         char *title_locale = SDL_iconv_utf8_locale(title);
   812         if (!title_locale) {
   813             SDL_OutOfMemory();
   814             return;
   815         }
   816         status = XStringListToTextProperty(&title_locale, 1, &titleprop);
   817         SDL_free(title_locale);
   818         if (status) {
   819             XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME);
   820             XFree(titleprop.value);
   821         }
   822 #ifdef X_HAVE_UTF8_STRING
   823         if (SDL_X11_HAVE_UTF8) {
   824             status =
   825                 Xutf8TextListToTextProperty(display, (char **) &title, 1,
   826                                             XUTF8StringStyle, &titleprop);
   827             if (status == Success) {
   828                 XSetTextProperty(display, data->xwindow, &titleprop,
   829                                  _NET_WM_NAME);
   830                 XFree(titleprop.value);
   831             }
   832         }
   833 #endif
   834     }
   835     if (icon != NULL) {
   836         char *icon_locale = SDL_iconv_utf8_locale(icon);
   837         if (!icon_locale) {
   838             SDL_OutOfMemory();
   839             return;
   840         }
   841         status = XStringListToTextProperty(&icon_locale, 1, &iconprop);
   842         SDL_free(icon_locale);
   843         if (status) {
   844             XSetTextProperty(display, data->xwindow, &iconprop,
   845                              XA_WM_ICON_NAME);
   846             XFree(iconprop.value);
   847         }
   848 #ifdef X_HAVE_UTF8_STRING
   849         if (SDL_X11_HAVE_UTF8) {
   850             status =
   851                 Xutf8TextListToTextProperty(display, (char **) &icon, 1,
   852                                             XUTF8StringStyle, &iconprop);
   853             if (status == Success) {
   854                 XSetTextProperty(display, data->xwindow, &iconprop,
   855                                  _NET_WM_ICON_NAME);
   856                 XFree(iconprop.value);
   857             }
   858         }
   859 #endif
   860     }
   861 }
   862 
   863 void
   864 X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   865 {
   866     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   867     Display *display = data->videodata->display;
   868     Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON;
   869 
   870     if (icon) {
   871         SDL_PixelFormat format;
   872         SDL_Surface *surface;
   873         int propsize;
   874         long *propdata;
   875 
   876         /* Convert the icon to ARGB for modern window managers */
   877         SDL_InitFormat(&format, 32, 0x00FF0000, 0x0000FF00, 0x000000FF,
   878                        0xFF000000);
   879         surface = SDL_ConvertSurface(icon, &format, 0);
   880         if (!surface) {
   881             return;
   882         }
   883 
   884         /* Set the _NET_WM_ICON property */
   885         propsize = 2 + (icon->w * icon->h);
   886         propdata = SDL_malloc(propsize * sizeof(long));
   887         if (propdata) {
   888             int x, y;
   889             Uint32 *src;
   890             long *dst;
   891 
   892             propdata[0] = icon->w;
   893             propdata[1] = icon->h;
   894             dst = &propdata[2];
   895             for (y = 0; y < icon->h; ++y) {
   896                 src = (Uint32*)((Uint8*)surface->pixels + y * surface->pitch);
   897                 for (x = 0; x < icon->w; ++x) {
   898                     *dst++ = *src++;
   899                 }
   900             }
   901             XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
   902                             32, PropModeReplace, (unsigned char *) propdata,
   903                             propsize);
   904         }
   905         SDL_FreeSurface(surface);
   906     } else {
   907         XDeleteProperty(display, data->xwindow, _NET_WM_ICON);
   908     }
   909 }
   910 
   911 void
   912 X11_SetWindowPosition(_THIS, SDL_Window * window)
   913 {
   914     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   915     Display *display = data->videodata->display;
   916     SDL_bool oldstyle_fullscreen;
   917     int x, y;
   918 
   919     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
   920     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
   921 
   922     if (oldstyle_fullscreen
   923         || window->x == SDL_WINDOWPOS_CENTERED) {
   924         X11_GetDisplaySize(_this, window, &x, NULL);
   925         x = (x - window->w) / 2;
   926     } else {
   927         x = window->x;
   928     }
   929     if (oldstyle_fullscreen
   930         || window->y == SDL_WINDOWPOS_CENTERED) {
   931         X11_GetDisplaySize(_this, window, NULL, &y);
   932         y = (y - window->h) / 2;
   933     } else {
   934         y = window->y;
   935     }
   936     XMoveWindow(display, data->xwindow, x, y);
   937 }
   938 
   939 void
   940 X11_SetWindowSize(_THIS, SDL_Window * window)
   941 {
   942     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   943     Display *display = data->videodata->display;
   944 
   945     if(SDL_IsShapedWindow(window))
   946         X11_ResizeWindowShape(window);
   947     XResizeWindow(display, data->xwindow, window->w, window->h);
   948 }
   949 
   950 void
   951 X11_ShowWindow(_THIS, SDL_Window * window)
   952 {
   953     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   954     Display *display = data->videodata->display;
   955 
   956     XMapRaised(display, data->xwindow);
   957 }
   958 
   959 void
   960 X11_HideWindow(_THIS, SDL_Window * window)
   961 {
   962     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   963     Display *display = data->videodata->display;
   964 
   965     XUnmapWindow(display, data->xwindow);
   966 }
   967 
   968 void
   969 X11_RaiseWindow(_THIS, SDL_Window * window)
   970 {
   971     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   972     Display *display = data->videodata->display;
   973 
   974     XRaiseWindow(display, data->xwindow);
   975 }
   976 
   977 static void
   978 X11_SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized)
   979 {
   980     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   981     SDL_DisplayData *displaydata =
   982         (SDL_DisplayData *) window->display->driverdata;
   983     Display *display = data->videodata->display;
   984     Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
   985     Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
   986     Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
   987     Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
   988 
   989     if (X11_IsWindowMapped(_this, window)) {
   990         XEvent e;
   991 
   992         SDL_zero(e);
   993         e.xany.type = ClientMessage;
   994         e.xclient.message_type = _NET_WM_STATE;
   995         e.xclient.format = 32;
   996         e.xclient.window = data->xwindow;
   997         e.xclient.data.l[0] =
   998             maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   999         e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
  1000         e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
  1001         e.xclient.data.l[3] = 0l;
  1002 
  1003         XSendEvent(display, RootWindow(display, displaydata->screen), 0,
  1004                    SubstructureNotifyMask | SubstructureRedirectMask, &e);
  1005     } else {
  1006         int count = 0;
  1007         Atom atoms[3];
  1008 
  1009         if (window->flags & SDL_WINDOW_FULLSCREEN) {
  1010             atoms[count++] = _NET_WM_STATE_FULLSCREEN;
  1011         }
  1012         if (maximized) {
  1013             atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
  1014             atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
  1015         }
  1016         if (count > 0) {
  1017             XChangeProperty(display, data->xwindow, _NET_WM_STATE, XA_ATOM, 32,
  1018                             PropModeReplace, (unsigned char *)atoms, count);
  1019         } else {
  1020             XDeleteProperty(display, data->xwindow, _NET_WM_STATE);
  1021         }
  1022     }
  1023 }
  1024 
  1025 void
  1026 X11_MaximizeWindow(_THIS, SDL_Window * window)
  1027 {
  1028     X11_SetWindowMaximized(_this, window, SDL_TRUE);
  1029 }
  1030 
  1031 void
  1032 X11_MinimizeWindow(_THIS, SDL_Window * window)
  1033 {
  1034     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1035     SDL_DisplayData *displaydata =
  1036         (SDL_DisplayData *) window->display->driverdata;
  1037     Display *display = data->videodata->display;
  1038  
  1039     XIconifyWindow(display, data->xwindow, displaydata->screen);
  1040 }
  1041 
  1042 void
  1043 X11_RestoreWindow(_THIS, SDL_Window * window)
  1044 {
  1045     X11_SetWindowMaximized(_this, window, SDL_FALSE);
  1046     X11_ShowWindow(_this, window);
  1047 }
  1048 
  1049 void
  1050 X11_SetWindowGrab(_THIS, SDL_Window * window)
  1051 {
  1052     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1053     Display *display = data->videodata->display;
  1054     SDL_bool oldstyle_fullscreen;
  1055 
  1056     /* ICCCM2.0-compliant window managers can handle fullscreen windows */
  1057     oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);
  1058 
  1059     if (((window->flags & SDL_WINDOW_INPUT_GRABBED) || oldstyle_fullscreen)
  1060         && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1061         /* Try to grab the mouse */
  1062         for (;;) {
  1063             int result =
  1064                 XGrabPointer(display, data->xwindow, True, 0, GrabModeAsync,
  1065                              GrabModeAsync, data->xwindow, None, CurrentTime);
  1066             if (result == GrabSuccess) {
  1067                 break;
  1068             }
  1069             SDL_Delay(100);
  1070         }
  1071 
  1072         /* Raise the window if we grab the mouse */
  1073         XRaiseWindow(display, data->xwindow);
  1074 
  1075         /* Now grab the keyboard */
  1076         XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
  1077                       GrabModeAsync, CurrentTime);
  1078     } else {
  1079         XUngrabPointer(display, CurrentTime);
  1080         XUngrabKeyboard(display, CurrentTime);
  1081     }
  1082 }
  1083 
  1084 void
  1085 X11_DestroyWindow(_THIS, SDL_Window * window)
  1086 {
  1087     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1088     window->driverdata = NULL;
  1089 
  1090     if (data) {
  1091         SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
  1092         Display *display = videodata->display;
  1093         int numwindows = videodata->numwindows;
  1094         SDL_WindowData **windowlist = videodata->windowlist;
  1095         int i;
  1096 
  1097         if (windowlist) {
  1098             for (i = 0; i < numwindows; ++i) {
  1099                 if (windowlist[i] && (windowlist[i]->window == window)) {
  1100                     windowlist[i] = windowlist[numwindows - 1];
  1101                     windowlist[numwindows - 1] = NULL;
  1102                     videodata->numwindows--;
  1103                     break;
  1104                 }
  1105             }
  1106         }
  1107 #ifdef X_HAVE_UTF8_STRING
  1108         if (data->ic) {
  1109             XDestroyIC(data->ic);
  1110         }
  1111 #endif
  1112         if (data->created) {
  1113             XDestroyWindow(display, data->xwindow);
  1114         }
  1115         SDL_free(data);
  1116     }
  1117 }
  1118 
  1119 SDL_bool
  1120 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1121 {
  1122     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1123     Display *display = data->videodata->display;
  1124 
  1125     if (info->version.major == SDL_MAJOR_VERSION &&
  1126         info->version.minor == SDL_MINOR_VERSION) {
  1127         info->subsystem = SDL_SYSWM_X11;
  1128         info->x11.display = display;
  1129         info->x11.window = data->xwindow;
  1130         return SDL_TRUE;
  1131     } else {
  1132         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1133                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1134         return SDL_FALSE;
  1135     }
  1136 }
  1137 
  1138 /* vi: set ts=4 sw=4 expandtab: */