src/video/x11/SDL_x11window.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 17 Jul 2010 18:44:34 -0700
changeset 4528 f06faa886423
parent 4524 a256e1dadf3f
child 4555 9e73d17638d3
permissions -rw-r--r--
Fixed bug 1022 Fixed the X11 icon on 64-bit systems

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2010 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"

#include "SDL_x11video.h"
#include "SDL_x11mouse.h"
#include "SDL_x11gamma.h"
#include "../Xext/extensions/StdCmap.h"

#ifdef SDL_VIDEO_DRIVER_PANDORA
#include "SDL_x11opengles.h"
#endif

#include "SDL_timer.h"
#include "SDL_syswm.h"

#define _NET_WM_STATE_REMOVE    0l
#define _NET_WM_STATE_ADD       1l
#define _NET_WM_STATE_TOGGLE    2l

static SDL_bool
X11_IsWindowOldFullscreen(_THIS, SDL_Window * window)
{
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;

    /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    if ((window->flags & SDL_WINDOW_FULLSCREEN) && !videodata->net_wm) {
        return SDL_TRUE;
    } else {
        return SDL_FALSE;
    }
}

static SDL_bool
X11_IsWindowMapped(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    XWindowAttributes attr;

    XGetWindowAttributes(videodata->display, data->xwindow, &attr);
    if (attr.map_state != IsUnmapped) {
        return SDL_TRUE;
    } else {
        return SDL_FALSE;
    }
}

static int
X11_GetWMStateProperty(_THIS, SDL_Window * window, Atom atoms[3])
{
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    int count = 0;

    if (window->flags & SDL_WINDOW_FULLSCREEN) {
        atoms[count++] = data->_NET_WM_STATE_FULLSCREEN;
    }
    if (window->flags & SDL_WINDOW_MAXIMIZED) {
        atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_VERT;
        atoms[count++] = data->_NET_WM_STATE_MAXIMIZED_HORZ;
    }
    return count;
}

static void
X11_GetDisplaySize(_THIS, SDL_Window * window, int *w, int *h)
{
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    SDL_DisplayData *displaydata =
        (SDL_DisplayData *) window->display->driverdata;
    XWindowAttributes attr;

    XGetWindowAttributes(data->display, RootWindow(data->display,
                                                   displaydata->screen),
                         &attr);
    if (w) {
        *w = attr.width;
    }
    if (h) {
        *h = attr.height;
    }
}

static int
SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
{
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    SDL_WindowData *data;
    int numwindows = videodata->numwindows;
    int windowlistlength = videodata->windowlistlength;
    SDL_WindowData **windowlist = videodata->windowlist;

    /* Allocate the window data */
    data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
    if (!data) {
        SDL_OutOfMemory();
        return -1;
    }
    data->window = window;
    data->xwindow = w;
#ifdef X_HAVE_UTF8_STRING
    if (SDL_X11_HAVE_UTF8) {
        data->ic =
            pXCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w,
                       XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
                       XNResourceName, videodata->classname, XNResourceClass,
                       videodata->classname, NULL);
    }
#endif
    data->created = created;
    data->videodata = videodata;

    /* Associate the data with the window */

    if (numwindows < windowlistlength) {
        windowlist[numwindows] = data;
        videodata->numwindows++;
    } else {
        windowlist =
            (SDL_WindowData **) SDL_realloc(windowlist,
                                            (numwindows +
                                             1) * sizeof(*windowlist));
        if (!windowlist) {
            SDL_OutOfMemory();
            SDL_free(data);
            return -1;
        }
        windowlist[numwindows] = data;
        videodata->numwindows++;
        videodata->windowlistlength++;
        videodata->windowlist = windowlist;
    }

    /* Fill in the SDL window with the window data */
    {
        XWindowAttributes attrib;

        XGetWindowAttributes(data->videodata->display, w, &attrib);
        window->x = attrib.x;
        window->y = attrib.y;
        window->w = attrib.width;
        window->h = attrib.height;
        if (attrib.map_state != IsUnmapped) {
            window->flags |= SDL_WINDOW_SHOWN;
        } else {
            window->flags &= ~SDL_WINDOW_SHOWN;
        }
    }

    {
        Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
        Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
        Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
        Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;
        Atom actualType;
        int actualFormat;
        unsigned long i, numItems, bytesAfter;
        unsigned char *propertyValue = NULL;
        long maxLength = 1024;

        if (XGetWindowProperty(data->videodata->display, w, _NET_WM_STATE,
                               0l, maxLength, False, XA_ATOM, &actualType,
                               &actualFormat, &numItems, &bytesAfter,
                               &propertyValue) == Success) {
            Atom *atoms = (Atom *) propertyValue;
            int maximized = 0;
            int fullscreen = 0;

            for (i = 0; i < numItems; ++i) {
                if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) {
                    maximized |= 1;
                } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
                    maximized |= 2;
                } else if ( atoms[i] == _NET_WM_STATE_FULLSCREEN) {
                    fullscreen = 1;
                }
            }
            if (maximized == 3) {
                window->flags |= SDL_WINDOW_MAXIMIZED;
            }  else if (fullscreen == 1) {
                window->flags |= SDL_WINDOW_FULLSCREEN;
            }
            XFree(propertyValue);
        }
    }

    /* FIXME: How can I tell?
       {
       DWORD style = GetWindowLong(hwnd, GWL_STYLE);
       if (style & WS_VISIBLE) {
       if (style & (WS_BORDER | WS_THICKFRAME)) {
       window->flags &= ~SDL_WINDOW_BORDERLESS;
       } else {
       window->flags |= SDL_WINDOW_BORDERLESS;
       }
       if (style & WS_THICKFRAME) {
       window->flags |= SDL_WINDOW_RESIZABLE;
       } else {
       window->flags &= ~SDL_WINDOW_RESIZABLE;
       }
       if (style & WS_MINIMIZE) {
       window->flags |= SDL_WINDOW_MINIMIZED;
       } else {
       window->flags &= ~SDL_WINDOW_MINIMIZED;
       }
       }
       if (GetFocus() == hwnd) {
       int index = data->videodata->keyboard;
       window->flags |= SDL_WINDOW_INPUT_FOCUS;
       SDL_SetKeyboardFocus(index, data->window);

       if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
       RECT rect;
       GetClientRect(hwnd, &rect);
       ClientToScreen(hwnd, (LPPOINT) & rect);
       ClientToScreen(hwnd, (LPPOINT) & rect + 1);
       ClipCursor(&rect);
       }
       }
     */

    /* All done! */
    window->driverdata = data;
    return 0;
}

int
X11_CreateWindow(_THIS, SDL_Window * window)
{
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    SDL_DisplayData *displaydata =
        (SDL_DisplayData *) window->display->driverdata;
    Display *display = data->display;
    int screen = displaydata->screen;
    Visual *visual;
    int depth;
    XSetWindowAttributes xattr;
    int x, y;
    Window w;
    XSizeHints *sizehints;
    XWMHints *wmhints;
    XClassHint *classhints;
    SDL_bool oldstyle_fullscreen;
    Atom _NET_WM_WINDOW_TYPE;
    Atom _NET_WM_WINDOW_TYPE_NORMAL;
    int wmstate_count;
    Atom wmstate_atoms[3];

    /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);

#if SDL_VIDEO_DRIVER_X11_XINERAMA
/* FIXME
    if ( use_xinerama ) {
        x = xinerama_info.x_org;
        y = xinerama_info.y_org;
    }
*/
#endif
#ifdef SDL_VIDEO_OPENGL_GLX
    if (window->flags & SDL_WINDOW_OPENGL) {
        XVisualInfo *vinfo;

        vinfo = X11_GL_GetVisual(_this, display, screen);
        if (!vinfo) {
            return -1;
        }
        visual = vinfo->visual;
        depth = vinfo->depth;
        XFree(vinfo);
    } else
#endif
#ifdef SDL_VIDEO_DRIVER_PANDORA
    if (window->flags & SDL_WINDOW_OPENGL) {
        XVisualInfo *vinfo;

        vinfo = X11_GLES_GetVisual(_this, display, screen);
        if (!vinfo) {
            return -1;
        }
        visual = vinfo->visual;
        depth = vinfo->depth;
        XFree(vinfo);
    } else
#endif
    {
        visual = displaydata->visual;
        depth = displaydata->depth;
    }

    if (oldstyle_fullscreen) {
        xattr.override_redirect = True;
    } else {
        xattr.override_redirect = False;
    }
    xattr.background_pixel = 0;
    xattr.border_pixel = 0;

    if (visual->class == PseudoColor) {
        printf("asking for PseudoColor\n");

/*      Status status; */
        XColor *colorcells;
        Colormap colormap;
        Sint32 pix;
        Sint32 ncolors;
        Sint32 nbits;
        Sint32 rmax, gmax, bmax;
        Sint32 rwidth, gwidth, bwidth;
        Sint32 rmask, gmask, bmask;
        Sint32 rshift, gshift, bshift;
        Sint32 r, g, b;

        /* Is the colormap we need already registered in SDL? */
        if ((colormap =
            X11_LookupColormap(display, screen, visual->visualid))) {
            xattr.colormap = colormap;
/*             printf("found existing colormap\n"); */
        } else {
            /* The colormap is not known to SDL so we will create it */
            colormap = XCreateColormap(display, RootWindow(display, screen),
                                       visual, AllocAll);
/*             printf("colormap = %x\n", colormap); */

            /* If we can't create a colormap, then we must die */
            if (!colormap) {
                SDL_SetError
                    ("Couldn't create window: Could not create writable colormap");
                return -1;
            }

            /* OK, we got a colormap, now fill it in as best as we can */

            colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
            if (NULL == colorcells) {
                SDL_SetError("out of memory in X11_CreateWindow");
                return -1;
            }

            ncolors = visual->map_entries;
            nbits = visual->bits_per_rgb;

/* 	    printf("ncolors = %d nbits = %d\n", ncolors, nbits); */

            /* what if ncolors != (1 << nbits)? That can happen on a
               true PseudoColor display.  I'm assuming that we will
               always have ncolors == (1 << nbits) */

            /* I'm making a lot of assumptions here. */

            /* Compute the width of each field. If there is one extra
               bit, give it to green. If there are two extra bits give
               them to red and greed.  We can get extra bits when the
               number of bits per pixel is not a multiple of 3. For
               example when we have 16 bits per pixel and need a 5/6/5
               layout for the RGB fields */

            rwidth = (nbits / 3) + (((nbits % 3) == 2) ? 1 : 0);
            gwidth = (nbits / 3) + (((nbits % 3) >= 1) ? 1 : 0);
            bwidth = (nbits / 3);

            rshift = gwidth + bwidth;
            gshift = bwidth;
            bshift = 0;

            rmax = 1 << rwidth;
            gmax = 1 << gwidth;
            bmax = 1 << bwidth;

            rmask = rmax - 1;
            gmask = gmax - 1;
            bmask = bmax - 1;

/*             printf("red   mask = %4x shift = %4d width = %d\n", rmask, rshift, rwidth); */
/*             printf("green mask = %4x shift = %4d width = %d\n", gmask, gshift, gwidth); */
/*             printf("blue  mask = %4x shift = %4d width = %d\n", bmask, bshift, bwidth); */

            /* build the color table pixel values */
            pix = 0;
            for (r = 0; r < rmax; r++) {
                for (g = 0; g < gmax; g++) {
                    for (b = 0; b < bmax; b++) {
                        colorcells[pix].pixel =
                            (r << rshift) | (g << gshift) | (b << bshift);
                        colorcells[pix].red = (0xffff * r) / rmask;
                        colorcells[pix].green = (0xffff * g) / gmask;
                        colorcells[pix].blue = (0xffff * b) / bmask;
/* 		  printf("%4x:%4x [%4x %4x %4x]\n",  */
/* 			 pix,  */
/* 			 colorcells[pix].pixel, */
/* 			 colorcells[pix].red, */
/* 			 colorcells[pix].green, */
/* 			 colorcells[pix].blue); */
                        pix++;
                    }
                }
            }

/*             status = */
/*                 XStoreColors(display, colormap, colorcells, ncolors); */

            xattr.colormap = colormap;
            X11_TrackColormap(display, screen, colormap, visual, NULL);

            SDL_free(colorcells);
        }
    } else if (visual->class == DirectColor) {
        Status status;
        XColor *colorcells;
        Colormap colormap;
        int i;
        int ncolors;
        int rmax, gmax, bmax;
        int rmask, gmask, bmask;
        int rshift, gshift, bshift;

        /* Is the colormap we need already registered in SDL? */
        if ((colormap =
             X11_LookupColormap(display, screen, visual->visualid))) {
            xattr.colormap = colormap;
/*             printf("found existing colormap\n"); */
        } else {
            /* The colormap is not known to SDL so we will create it */
            colormap = XCreateColormap(display, RootWindow(display, screen),
                                       visual, AllocAll);
/*             printf("colormap = %x\n", colormap); */

            /* If we can't create a colormap, then we must die */
            if (!colormap) {
                SDL_SetError
                    ("Couldn't create window: Could not create writable colormap");
                return -1;
            }

            /* OK, we got a colormap, now fill it in as best as we can */
            colorcells = SDL_malloc(visual->map_entries * sizeof(XColor));
            if (NULL == colorcells) {
                SDL_SetError("out of memory in X11_CreateWindow");
                return -1;
            }
            ncolors = visual->map_entries;
            rmax = 0xffff;
            gmax = 0xffff;
            bmax = 0xffff;

            rshift = 0;
            rmask = visual->red_mask;
            while (0 == (rmask & 1)) {
                rshift++;
                rmask >>= 1;
            }

/*             printf("rmask = %4x rshift = %4d\n", rmask, rshift); */

            gshift = 0;
            gmask = visual->green_mask;
            while (0 == (gmask & 1)) {
                gshift++;
                gmask >>= 1;
            }

/*             printf("gmask = %4x gshift = %4d\n", gmask, gshift); */

            bshift = 0;
            bmask = visual->blue_mask;
            while (0 == (bmask & 1)) {
                bshift++;
                bmask >>= 1;
            }

/*             printf("bmask = %4x bshift = %4d\n", bmask, bshift); */

            /* build the color table pixel values */
            for (i = 0; i < ncolors; i++) {
                Uint32 red = (rmax * i) / (ncolors - 1);
                Uint32 green = (gmax * i) / (ncolors - 1);
                Uint32 blue = (bmax * i) / (ncolors - 1);

                Uint32 rbits = (rmask * i) / (ncolors - 1);
                Uint32 gbits = (gmask * i) / (ncolors - 1);
                Uint32 bbits = (bmask * i) / (ncolors - 1);

                Uint32 pix =
                    (rbits << rshift) | (gbits << gshift) | (bbits << bshift);

                colorcells[i].pixel = pix;

                colorcells[i].red = red;
                colorcells[i].green = green;
                colorcells[i].blue = blue;

                colorcells[i].flags = DoRed | DoGreen | DoBlue;
/* 		printf("%2d:%4x [%4x %4x %4x]\n", i, pix, red, green, blue); */
            }

            status =
                XStoreColors(display, colormap, colorcells, ncolors);

            xattr.colormap = colormap;
            X11_TrackColormap(display, screen, colormap, visual, colorcells);

            SDL_free(colorcells);
        }
    } else {
        xattr.colormap =
            XCreateColormap(display, RootWindow(display, screen),
                            visual, AllocNone);
    }

    if (oldstyle_fullscreen
        || window->x == SDL_WINDOWPOS_CENTERED) {
        X11_GetDisplaySize(_this, window, &x, NULL);
        x = (x - window->w) / 2;
    } else if (window->x == SDL_WINDOWPOS_UNDEFINED) {
        x = 0;
    } else {
        x = window->x;
    }
    if (oldstyle_fullscreen
        || window->y == SDL_WINDOWPOS_CENTERED) {
        X11_GetDisplaySize(_this, window, NULL, &y);
        y = (y - window->h) / 2;
    } else if (window->y == SDL_WINDOWPOS_UNDEFINED) {
        y = 0;
    } else {
        y = window->y;
    }

    w = XCreateWindow(display, RootWindow(display, screen), x, y,
                      window->w, window->h, 0, depth, InputOutput, visual,
                      (CWOverrideRedirect | CWBackPixel | CWBorderPixel |
                       CWColormap), &xattr);
    if (!w) {
        SDL_SetError("Couldn't create window");
        return -1;
    }
#if SDL_VIDEO_DRIVER_PANDORA
    /* Create the GLES window surface */
    _this->gles_data->egl_surface =
        _this->gles_data->eglCreateWindowSurface(_this->gles_data->
                                                 egl_display,
                                                 _this->gles_data->egl_config,
                                                 (NativeWindowType) w, NULL);

    if (_this->gles_data->egl_surface == EGL_NO_SURFACE) {
        SDL_SetError("Could not create GLES window surface");
        return -1;
    }
#endif

    sizehints = XAllocSizeHints();
    if (sizehints) {
        if (!(window->flags & SDL_WINDOW_RESIZABLE)
            || oldstyle_fullscreen) {
            sizehints->min_width = sizehints->max_width = window->w;
            sizehints->min_height = sizehints->max_height = window->h;
            sizehints->flags = PMaxSize | PMinSize;
        }
        if (!oldstyle_fullscreen
            && window->x != SDL_WINDOWPOS_UNDEFINED
            && window->y != SDL_WINDOWPOS_UNDEFINED) {
            sizehints->x = x;
            sizehints->y = y;
            sizehints->flags |= USPosition;
        }
        XSetWMNormalHints(display, w, sizehints);
        XFree(sizehints);
    }

    if ((window->flags & SDL_WINDOW_BORDERLESS) || oldstyle_fullscreen) {
        SDL_bool set;
        Atom WM_HINTS;

        /* We haven't modified the window manager hints yet */
        set = SDL_FALSE;

        /* First try to set MWM hints */
        WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
        if (WM_HINTS != None) {
            /* Hints used by Motif compliant window managers */
            struct
            {
                unsigned long flags;
                unsigned long functions;
                unsigned long decorations;
                long input_mode;
                unsigned long status;
            } MWMHints = {
            (1L << 1), 0, 0, 0, 0};

            XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
                            PropModeReplace, (unsigned char *) &MWMHints,
                            sizeof(MWMHints) / 4);
            set = SDL_TRUE;
        }
        /* Now try to set KWM hints */
        WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
        if (WM_HINTS != None) {
            long KWMHints = 0;

            XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
                            PropModeReplace,
                            (unsigned char *) &KWMHints,
                            sizeof(KWMHints) / 4);
            set = SDL_TRUE;
        }
        /* Now try to set GNOME hints */
        WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
        if (WM_HINTS != None) {
            long GNOMEHints = 0;

            XChangeProperty(display, w, WM_HINTS, WM_HINTS, 32,
                            PropModeReplace,
                            (unsigned char *) &GNOMEHints,
                            sizeof(GNOMEHints) / 4);
            set = SDL_TRUE;
        }
        /* Finally set the transient hints if necessary */
        if (!set) {
            XSetTransientForHint(display, w, RootWindow(display, screen));
        }
    } else {
        SDL_bool set;
        Atom WM_HINTS;

        /* We haven't modified the window manager hints yet */
        set = SDL_FALSE;

        /* First try to unset MWM hints */
        WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", True);
        if (WM_HINTS != None) {
            XDeleteProperty(display, w, WM_HINTS);
            set = SDL_TRUE;
        }
        /* Now try to unset KWM hints */
        WM_HINTS = XInternAtom(display, "KWM_WIN_DECORATION", True);
        if (WM_HINTS != None) {
            XDeleteProperty(display, w, WM_HINTS);
            set = SDL_TRUE;
        }
        /* Now try to unset GNOME hints */
        WM_HINTS = XInternAtom(display, "_WIN_HINTS", True);
        if (WM_HINTS != None) {
            XDeleteProperty(display, w, WM_HINTS);
            set = SDL_TRUE;
        }
        /* Finally unset the transient hints if necessary */
        if (!set) {
            /* NOTE: Does this work? */
            XSetTransientForHint(display, w, None);
        }
    }

    /* Set the input hints so we get keyboard input */
    wmhints = XAllocWMHints();
    if (wmhints) {
        wmhints->input = True;
        wmhints->flags = InputHint;
        XSetWMHints(display, w, wmhints);
        XFree(wmhints);
    }

    /* Set the class hints so we can get an icon (AfterStep) */
    classhints = XAllocClassHint();
    if (classhints != NULL) {
        classhints->res_name = data->classname;
        classhints->res_class = data->classname;
        XSetClassHint(display, w, classhints);
        XFree(classhints);
    }

    /* Set the window manager state */
    wmstate_count = X11_GetWMStateProperty(_this, window, wmstate_atoms);
    if (wmstate_count > 0) {
        XChangeProperty(display, w, data->_NET_WM_STATE, XA_ATOM, 32,
                        PropModeReplace,
                        (unsigned char *)wmstate_atoms, wmstate_count);
    } else {
        XDeleteProperty(display, w, data->_NET_WM_STATE);
    }

    /* Let the window manager know we're a "normal" window */
    _NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
    _NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
    XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
                    PropModeReplace,
                    (unsigned char *)&_NET_WM_WINDOW_TYPE_NORMAL, 1);

    /* Allow the window to be deleted by the window manager */
    XSetWMProtocols(display, w, &data->WM_DELETE_WINDOW, 1);

    if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
        XDestroyWindow(display, w);
        return -1;
    }
#ifdef X_HAVE_UTF8_STRING
    {
        Uint32 fevent = 0;
        pXGetICValues(((SDL_WindowData *) window->driverdata)->ic,
                      XNFilterEvents, &fevent, NULL);
        XSelectInput(display, w,
                     (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
                      ExposureMask | ButtonPressMask | ButtonReleaseMask |
                      PointerMotionMask | KeyPressMask | KeyReleaseMask |
                      PropertyChangeMask | StructureNotifyMask |
                      KeymapStateMask | fevent));
    }
#else
    {
        XSelectInput(display, w,
                     (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
                      ExposureMask | ButtonPressMask | ButtonReleaseMask |
                      PointerMotionMask | KeyPressMask | KeyReleaseMask |
                      PropertyChangeMask | StructureNotifyMask |
                      KeymapStateMask));
    }
#endif

    return 0;
}

int
X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
{
    Window w = (Window) data;

    window->title = X11_GetWindowTitle(_this, w);

    if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
        return -1;
    }
    return 0;
}

char *
X11_GetWindowTitle(_THIS, Window xwindow)
{
    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    Display *display = data->display;
    int status, real_format;
    Atom real_type;
    unsigned long items_read, items_left;
    unsigned char *propdata;
    char *title = NULL;

    status = XGetWindowProperty(display, xwindow, data->_NET_WM_NAME,
                0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format,
                &items_read, &items_left, &propdata);
    if (status == Success) {
        title = SDL_strdup(SDL_static_cast(char*, propdata));
        XFree(propdata);
    } else {
        status = XGetWindowProperty(display, xwindow, XA_WM_NAME,
                    0L, 8192L, False, XA_STRING, &real_type, &real_format,
                    &items_read, &items_left, &propdata);
        if (status == Success) {
            title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1);
        } else {
            title = SDL_strdup("");
        }
    }
    return title;
}

void
X11_SetWindowTitle(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;
    XTextProperty titleprop, iconprop;
    Status status;
    const char *title = window->title;
    const char *icon = NULL;

#ifdef X_HAVE_UTF8_STRING
    Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME;
    Atom _NET_WM_ICON_NAME = data->videodata->_NET_WM_ICON_NAME;
#endif

    if (title != NULL) {
        char *title_locale = SDL_iconv_utf8_locale(title);
        if (!title_locale) {
            SDL_OutOfMemory();
            return;
        }
        status = XStringListToTextProperty(&title_locale, 1, &titleprop);
        SDL_free(title_locale);
        if (status) {
            XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME);
            XFree(titleprop.value);
        }
#ifdef X_HAVE_UTF8_STRING
        if (SDL_X11_HAVE_UTF8) {
            status =
                Xutf8TextListToTextProperty(display, (char **) &title, 1,
                                            XUTF8StringStyle, &titleprop);
            if (status == Success) {
                XSetTextProperty(display, data->xwindow, &titleprop,
                                 _NET_WM_NAME);
                XFree(titleprop.value);
            }
        }
#endif
    }
    if (icon != NULL) {
        char *icon_locale = SDL_iconv_utf8_locale(icon);
        if (!icon_locale) {
            SDL_OutOfMemory();
            return;
        }
        status = XStringListToTextProperty(&icon_locale, 1, &iconprop);
        SDL_free(icon_locale);
        if (status) {
            XSetTextProperty(display, data->xwindow, &iconprop,
                             XA_WM_ICON_NAME);
            XFree(iconprop.value);
        }
#ifdef X_HAVE_UTF8_STRING
        if (SDL_X11_HAVE_UTF8) {
            status =
                Xutf8TextListToTextProperty(display, (char **) &icon, 1,
                                            XUTF8StringStyle, &iconprop);
            if (status == Success) {
                XSetTextProperty(display, data->xwindow, &iconprop,
                                 _NET_WM_ICON_NAME);
                XFree(iconprop.value);
            }
        }
#endif
    }
}

void
X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;
    Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON;

    if (icon) {
        SDL_PixelFormat format;
        SDL_Surface *surface;
        int propsize;
        long *propdata;

        /* Convert the icon to ARGB for modern window managers */
        SDL_InitFormat(&format, 32, 0x00FF0000, 0x0000FF00, 0x000000FF,
                       0xFF000000);
        surface = SDL_ConvertSurface(icon, &format, 0);
        if (!surface) {
            return;
        }

        /* Set the _NET_WM_ICON property */
        propsize = 2 + (icon->w * icon->h);
        propdata = SDL_malloc(propsize * sizeof(Uint32));
        if (propdata) {
            int x, y;
            Uint32 *src;
            long *dst;

            propdata[0] = icon->w;
            propdata[1] = icon->h;
            dst = &propdata[2];
            for (y = 0; y < icon->h; ++y) {
                src = (Uint32*)((Uint8*)surface->pixels + y * surface->pitch);
                for (x = 0; x < icon->w; ++x) {
                    *dst++ = *src++;
                }
            }
            XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL,
                            32, PropModeReplace, (unsigned char *) propdata,
                            propsize);
        }
        SDL_FreeSurface(surface);
    } else {
        XDeleteProperty(display, data->xwindow, _NET_WM_ICON);
    }
}

void
X11_SetWindowPosition(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;
    SDL_bool oldstyle_fullscreen;
    int x, y;

    /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);

    if (oldstyle_fullscreen
        || window->x == SDL_WINDOWPOS_CENTERED) {
        X11_GetDisplaySize(_this, window, &x, NULL);
        x = (x - window->w) / 2;
    } else {
        x = window->x;
    }
    if (oldstyle_fullscreen
        || window->y == SDL_WINDOWPOS_CENTERED) {
        X11_GetDisplaySize(_this, window, NULL, &y);
        y = (y - window->h) / 2;
    } else {
        y = window->y;
    }
    XMoveWindow(display, data->xwindow, x, y);
}

void
X11_SetWindowSize(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;

    XResizeWindow(display, data->xwindow, window->w, window->h);
}

void
X11_ShowWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;

    XMapRaised(display, data->xwindow);
}

void
X11_HideWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;

    XUnmapWindow(display, data->xwindow);
}

void
X11_RaiseWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;

    XRaiseWindow(display, data->xwindow);
}

static void
X11_SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    SDL_DisplayData *displaydata =
        (SDL_DisplayData *) window->display->driverdata;
    Display *display = data->videodata->display;
    Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE;
    Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT;
    Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ;
    Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN;

    if (X11_IsWindowMapped(_this, window)) {
        XEvent e;

        SDL_zero(e);
        e.xany.type = ClientMessage;
        e.xclient.message_type = _NET_WM_STATE;
        e.xclient.format = 32;
        e.xclient.window = data->xwindow;
        e.xclient.data.l[0] =
            maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
        e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT;
        e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ;
        e.xclient.data.l[3] = 0l;

        XSendEvent(display, RootWindow(display, displaydata->screen), 0,
                   SubstructureNotifyMask | SubstructureRedirectMask, &e);
    } else {
        int count = 0;
        Atom atoms[3];

        if (window->flags & SDL_WINDOW_FULLSCREEN) {
            atoms[count++] = _NET_WM_STATE_FULLSCREEN;
        }
        if (maximized) {
            atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT;
            atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ;
        }
        if (count > 0) {
            XChangeProperty(display, data->xwindow, _NET_WM_STATE, XA_ATOM, 32,
                            PropModeReplace, (unsigned char *)atoms, count);
        } else {
            XDeleteProperty(display, data->xwindow, _NET_WM_STATE);
        }
    }
}

void
X11_MaximizeWindow(_THIS, SDL_Window * window)
{
    X11_SetWindowMaximized(_this, window, SDL_TRUE);
}

void
X11_MinimizeWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    SDL_DisplayData *displaydata =
        (SDL_DisplayData *) window->display->driverdata;
    Display *display = data->videodata->display;
 
    XIconifyWindow(display, data->xwindow, displaydata->screen);
}

void
X11_RestoreWindow(_THIS, SDL_Window * window)
{
    X11_SetWindowMaximized(_this, window, SDL_FALSE);
    X11_ShowWindow(_this, window);
}

void
X11_SetWindowGrab(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;
    SDL_bool oldstyle_fullscreen;

    /* ICCCM2.0-compliant window managers can handle fullscreen windows */
    oldstyle_fullscreen = X11_IsWindowOldFullscreen(_this, window);

    if (((window->flags & SDL_WINDOW_INPUT_GRABBED) || oldstyle_fullscreen)
        && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
        /* Try to grab the mouse */
        for (;;) {
            int result =
                XGrabPointer(display, data->xwindow, True, 0, GrabModeAsync,
                             GrabModeAsync, data->xwindow, None, CurrentTime);
            if (result == GrabSuccess) {
                break;
            }
            SDL_Delay(100);
        }

        /* Raise the window if we grab the mouse */
        XRaiseWindow(display, data->xwindow);

        /* Now grab the keyboard */
        XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
                      GrabModeAsync, CurrentTime);
    } else {
        XUngrabPointer(display, CurrentTime);
        XUngrabKeyboard(display, CurrentTime);
    }
}

void
X11_DestroyWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    window->driverdata = NULL;

    if (data) {
        SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
        Display *display = videodata->display;
        int numwindows = videodata->numwindows;
        SDL_WindowData **windowlist = videodata->windowlist;
        int i;

        if (windowlist) {
            for (i = 0; i < numwindows; ++i) {
                if (windowlist[i] && (windowlist[i]->window == window)) {
                    windowlist[i] = windowlist[numwindows - 1];
                    windowlist[numwindows - 1] = NULL;
                    videodata->numwindows--;
                    break;
                }
            }
        }
#ifdef X_HAVE_UTF8_STRING
        if (data->ic) {
            XDestroyIC(data->ic);
        }
#endif
        if (data->created) {
            XDestroyWindow(display, data->xwindow);
        }
        SDL_free(data);
    }
}

SDL_bool
X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    Display *display = data->videodata->display;

    if (info->version.major == SDL_MAJOR_VERSION &&
        info->version.minor == SDL_MINOR_VERSION) {
        info->subsystem = SDL_SYSWM_X11;
        info->info.x11.display = display;
        info->info.x11.window = data->xwindow;
        return SDL_TRUE;
    } else {
        SDL_SetError("Application not compiled with SDL %d.%d\n",
                     SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
        return SDL_FALSE;
    }
}

/* vi: set ts=4 sw=4 expandtab: */