src/video/x11/SDL_x11window.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 29 Jul 2006 21:51:00 +0000
changeset 1956 ba0d62354872
parent 1952 420716272158
child 2143 e906da4414a3
permissions -rw-r--r--
Simplified driver window creation code.
Implemented several Cocoa window functions
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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_syswm.h"
    25 #include "../SDL_sysvideo.h"
    26 #include "../../events/SDL_keyboard_c.h"
    27 
    28 #include "SDL_x11video.h"
    29 
    30 
    31 static int
    32 SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
    33 {
    34     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
    35     SDL_WindowData *data;
    36     int numwindows = videodata->numwindows;
    37     SDL_WindowData **windowlist = videodata->windowlist;
    38 
    39     /* Allocate the window data */
    40     data = (SDL_WindowData *) SDL_malloc(sizeof(*data));
    41     if (!data) {
    42         SDL_OutOfMemory();
    43         return -1;
    44     }
    45     data->windowID = window->id;
    46     data->window = w;
    47 #ifdef X_HAVE_UTF8_STRING
    48     if (SDL_X11_HAVE_UTF8) {
    49         data->ic =
    50             pXCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w,
    51                        XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
    52                        XNResourceName, videodata->classname, XNResourceClass,
    53                        videodata->classname, NULL);
    54     }
    55 #endif
    56     data->created = created;
    57     data->videodata = videodata;
    58 
    59     /* Associate the data with the window */
    60     windowlist =
    61         (SDL_WindowData **) SDL_realloc(windowlist,
    62                                         (numwindows +
    63                                          1) * sizeof(*windowlist));
    64     if (!windowlist) {
    65         SDL_OutOfMemory();
    66         SDL_free(data);
    67         return -1;
    68     }
    69     windowlist[numwindows++] = data;
    70     videodata->numwindows = numwindows;
    71     videodata->windowlist = windowlist;
    72 
    73     /* Fill in the SDL window with the window data */
    74     {
    75         XWindowAttributes attrib;
    76 
    77         XGetWindowAttributes(data->videodata->display, w, &attrib);
    78         window->x = attrib.x;
    79         window->y = attrib.y;
    80         window->w = attrib.width;
    81         window->h = attrib.height;
    82         if (attrib.map_state != IsUnmapped) {
    83             window->flags |= SDL_WINDOW_SHOWN;
    84         } else {
    85             window->flags &= ~SDL_WINDOW_SHOWN;
    86         }
    87     }
    88     /* FIXME: How can I tell?
    89        {
    90        DWORD style = GetWindowLong(hwnd, GWL_STYLE);
    91        if (style & WS_VISIBLE) {
    92        if (style & (WS_BORDER | WS_THICKFRAME)) {
    93        window->flags &= ~SDL_WINDOW_BORDERLESS;
    94        } else {
    95        window->flags |= SDL_WINDOW_BORDERLESS;
    96        }
    97        if (style & WS_THICKFRAME) {
    98        window->flags |= SDL_WINDOW_RESIZABLE;
    99        } else {
   100        window->flags &= ~SDL_WINDOW_RESIZABLE;
   101        }
   102        if (style & WS_MAXIMIZE) {
   103        window->flags |= SDL_WINDOW_MAXIMIZED;
   104        } else {
   105        window->flags &= ~SDL_WINDOW_MAXIMIZED;
   106        }
   107        if (style & WS_MINIMIZE) {
   108        window->flags |= SDL_WINDOW_MINIMIZED;
   109        } else {
   110        window->flags &= ~SDL_WINDOW_MINIMIZED;
   111        }
   112        }
   113        if (GetFocus() == hwnd) {
   114        int index = data->videodata->keyboard;
   115        window->flags |= SDL_WINDOW_INPUT_FOCUS;
   116        SDL_SetKeyboardFocus(index, data->windowID);
   117 
   118        if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   119        RECT rect;
   120        GetClientRect(hwnd, &rect);
   121        ClientToScreen(hwnd, (LPPOINT) & rect);
   122        ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   123        ClipCursor(&rect);
   124        }
   125        }
   126      */
   127 
   128     /* All done! */
   129     window->driverdata = data;
   130     return 0;
   131 }
   132 
   133 int
   134 X11_CreateWindow(_THIS, SDL_Window * window)
   135 {
   136     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   137     SDL_DisplayData *displaydata =
   138         (SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->driverdata;
   139     Visual *visual;
   140     int depth;
   141     XSetWindowAttributes xattr;
   142     int x, y;
   143     Window w;
   144     XSizeHints *sizehints;
   145     XWMHints *wmhints;
   146     XClassHint *classhints;
   147 
   148 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   149 /* FIXME
   150     if ( use_xinerama ) {
   151         x = xinerama_info.x_org;
   152         y = xinerama_info.y_org;
   153     }
   154 */
   155 #endif
   156 #ifdef SDL_VIDEO_OPENGL_GLX
   157     if (window->flags & SDL_WINDOW_OPENGL) {
   158         XVisualInfo *vinfo;
   159 
   160         if (X11_GL_Initialize(_this) < 0) {
   161             return -1;
   162         }
   163         vinfo = X11_GL_GetVisual(_this, data->display, displaydata->screen);
   164         if (!vinfo) {
   165             return -1;
   166         }
   167         visual = vinfo->visual;
   168         depth = vinfo->depth;
   169         XFree(vinfo);
   170     } else
   171 #endif
   172     {
   173         visual = displaydata->visual;
   174         depth = displaydata->depth;
   175     }
   176 
   177     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   178         xattr.override_redirect = True;
   179     } else {
   180         xattr.override_redirect = False;
   181     }
   182     xattr.background_pixel = 0;
   183     xattr.border_pixel = 0;
   184     if (visual->class == PseudoColor || visual->class == DirectColor) {
   185         xattr.colormap =
   186             XCreateColormap(data->display,
   187                             RootWindow(data->display, displaydata->screen),
   188                             visual, AllocAll);
   189     } else {
   190         xattr.colormap =
   191             XCreateColormap(data->display,
   192                             RootWindow(data->display, displaydata->screen),
   193                             visual, AllocNone);
   194     }
   195 
   196     if (window->x == SDL_WINDOWPOS_CENTERED) {
   197         x = (DisplayWidth(data->display, displaydata->screen) -
   198              window->w) / 2;
   199     } else if (window->x == SDL_WINDOWPOS_UNDEFINED) {
   200         x = 0;
   201     } else {
   202         x = window->x;
   203     }
   204     if (window->y == SDL_WINDOWPOS_CENTERED) {
   205         y = (DisplayHeight(data->display, displaydata->screen) -
   206              window->h) / 2;
   207     } else if (window->y == SDL_WINDOWPOS_UNDEFINED) {
   208         y = 0;
   209     } else {
   210         y = window->y;
   211     }
   212 
   213     w = XCreateWindow(data->display,
   214                       RootWindow(data->display, displaydata->screen), x, y,
   215                       window->w, window->h, 0, depth, InputOutput, visual,
   216                       (CWOverrideRedirect | CWBackPixel | CWBorderPixel |
   217                        CWColormap), &xattr);
   218     if (!w) {
   219 #ifdef SDL_VIDEO_OPENGL_GLX
   220         if (window->flags & SDL_WINDOW_OPENGL) {
   221             X11_GL_Shutdown(_this);
   222         }
   223 #endif
   224         SDL_SetError("Couldn't create window");
   225         return -1;
   226     }
   227 
   228     sizehints = XAllocSizeHints();
   229     if (sizehints) {
   230         if (window->flags & SDL_WINDOW_RESIZABLE) {
   231             sizehints->min_width = 32;
   232             sizehints->min_height = 32;
   233             sizehints->max_height = 4096;
   234             sizehints->max_width = 4096;
   235         } else {
   236             sizehints->min_width = sizehints->max_width = window->w;
   237             sizehints->min_height = sizehints->max_height = window->h;
   238         }
   239         sizehints->flags = PMaxSize | PMinSize;
   240         if (!(window->flags & SDL_WINDOW_FULLSCREEN)
   241             && window->x != SDL_WINDOWPOS_UNDEFINED
   242             && window->y != SDL_WINDOWPOS_UNDEFINED) {
   243             sizehints->x = x;
   244             sizehints->y = y;
   245             sizehints->flags |= USPosition;
   246         }
   247         XSetWMNormalHints(data->display, w, sizehints);
   248         XFree(sizehints);
   249     }
   250 
   251     if (window->flags & SDL_WINDOW_BORDERLESS) {
   252         SDL_bool set;
   253         Atom WM_HINTS;
   254 
   255         /* We haven't modified the window manager hints yet */
   256         set = SDL_FALSE;
   257 
   258         /* First try to set MWM hints */
   259         WM_HINTS = XInternAtom(data->display, "_MOTIF_WM_HINTS", True);
   260         if (WM_HINTS != None) {
   261             /* Hints used by Motif compliant window managers */
   262             struct
   263             {
   264                 unsigned long flags;
   265                 unsigned long functions;
   266                 unsigned long decorations;
   267                 long input_mode;
   268                 unsigned long status;
   269             } MWMHints = {
   270             (1L << 1), 0, 0, 0, 0};
   271 
   272             XChangeProperty(data->display, w, WM_HINTS, WM_HINTS, 32,
   273                             PropModeReplace, (unsigned char *) &MWMHints,
   274                             sizeof(MWMHints) / sizeof(long));
   275             set = SDL_TRUE;
   276         }
   277         /* Now try to set KWM hints */
   278         WM_HINTS = XInternAtom(data->display, "KWM_WIN_DECORATION", True);
   279         if (WM_HINTS != None) {
   280             long KWMHints = 0;
   281 
   282             XChangeProperty(data->display, w,
   283                             WM_HINTS, WM_HINTS, 32,
   284                             PropModeReplace,
   285                             (unsigned char *) &KWMHints,
   286                             sizeof(KWMHints) / sizeof(long));
   287             set = SDL_TRUE;
   288         }
   289         /* Now try to set GNOME hints */
   290         WM_HINTS = XInternAtom(data->display, "_WIN_HINTS", True);
   291         if (WM_HINTS != None) {
   292             long GNOMEHints = 0;
   293 
   294             XChangeProperty(data->display, w,
   295                             WM_HINTS, WM_HINTS, 32,
   296                             PropModeReplace,
   297                             (unsigned char *) &GNOMEHints,
   298                             sizeof(GNOMEHints) / sizeof(long));
   299             set = SDL_TRUE;
   300         }
   301         /* Finally set the transient hints if necessary */
   302         if (!set) {
   303             XSetTransientForHint(data->display, w,
   304                                  RootWindow(data->display,
   305                                             displaydata->screen));
   306         }
   307     } else {
   308         SDL_bool set;
   309         Atom WM_HINTS;
   310 
   311         /* We haven't modified the window manager hints yet */
   312         set = SDL_FALSE;
   313 
   314         /* First try to unset MWM hints */
   315         WM_HINTS = XInternAtom(data->display, "_MOTIF_WM_HINTS", True);
   316         if (WM_HINTS != None) {
   317             XDeleteProperty(data->display, w, WM_HINTS);
   318             set = SDL_TRUE;
   319         }
   320         /* Now try to unset KWM hints */
   321         WM_HINTS = XInternAtom(data->display, "KWM_WIN_DECORATION", True);
   322         if (WM_HINTS != None) {
   323             XDeleteProperty(data->display, w, WM_HINTS);
   324             set = SDL_TRUE;
   325         }
   326         /* Now try to unset GNOME hints */
   327         WM_HINTS = XInternAtom(data->display, "_WIN_HINTS", True);
   328         if (WM_HINTS != None) {
   329             XDeleteProperty(data->display, w, WM_HINTS);
   330             set = SDL_TRUE;
   331         }
   332         /* Finally unset the transient hints if necessary */
   333         if (!set) {
   334             /* NOTE: Does this work? */
   335             XSetTransientForHint(data->display, w, None);
   336         }
   337     }
   338 
   339     /* Tell KDE to keep fullscreen windows on top */
   340     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   341         XEvent ev;
   342         long mask;
   343 
   344         SDL_zero(ev);
   345         ev.xclient.type = ClientMessage;
   346         ev.xclient.window = RootWindow(data->display, displaydata->screen);
   347         ev.xclient.message_type =
   348             XInternAtom(data->display, "KWM_KEEP_ON_TOP", False);
   349         ev.xclient.format = 32;
   350         ev.xclient.data.l[0] = w;
   351         ev.xclient.data.l[1] = CurrentTime;
   352         XSendEvent(data->display,
   353                    RootWindow(data->display, displaydata->screen), False,
   354                    SubstructureRedirectMask, &ev);
   355     }
   356 
   357     /* Set the input hints so we get keyboard input */
   358     wmhints = XAllocWMHints();
   359     if (wmhints) {
   360         wmhints->input = True;
   361         wmhints->flags = InputHint;
   362         XSetWMHints(data->display, w, wmhints);
   363         XFree(wmhints);
   364     }
   365 
   366     XSelectInput(data->display, w,
   367                  (FocusChangeMask | EnterWindowMask | LeaveWindowMask |
   368                   ExposureMask | ButtonPressMask | ButtonReleaseMask |
   369                   PointerMotionMask | KeyPressMask | KeyReleaseMask |
   370                   PropertyChangeMask | StructureNotifyMask |
   371                   KeymapStateMask));
   372 
   373     /* Set the class hints so we can get an icon (AfterStep) */
   374     classhints = XAllocClassHint();
   375     if (classhints != NULL) {
   376         classhints->res_name = data->classname;
   377         classhints->res_class = data->classname;
   378         XSetClassHint(data->display, w, classhints);
   379         XFree(classhints);
   380     }
   381 
   382     /* Allow the window to be deleted by the window manager */
   383     XSetWMProtocols(data->display, w, &data->WM_DELETE_WINDOW, 1);
   384 
   385     if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) {
   386 #ifdef SDL_VIDEO_OPENGL_GLX
   387         if (window->flags & SDL_WINDOW_OPENGL) {
   388             X11_GL_Shutdown(_this);
   389         }
   390 #endif
   391         XDestroyWindow(data->display, w);
   392         return -1;
   393     }
   394     return 0;
   395 }
   396 
   397 int
   398 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   399 {
   400     Window w = (Window) data;
   401 
   402     /* FIXME: Query the title from the existing window */
   403 
   404     if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) {
   405         return -1;
   406     }
   407     return 0;
   408 }
   409 
   410 void
   411 X11_SetWindowTitle(_THIS, SDL_Window * window)
   412 {
   413     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   414     Display *display = data->videodata->display;
   415     XTextProperty titleprop, iconprop;
   416     Status status;
   417     const char *title = window->title;
   418     const char *icon = NULL;
   419 
   420 #ifdef X_HAVE_UTF8_STRING
   421     Atom _NET_WM_NAME = 0;
   422     Atom _NET_WM_ICON_NAME = 0;
   423 
   424     /* Look up some useful Atoms */
   425     if (SDL_X11_HAVE_UTF8) {
   426         _NET_WM_NAME = XInternAtom(display, "_NET_WM_NAME", False);
   427         _NET_WM_ICON_NAME = XInternAtom(display, "_NET_WM_ICON_NAME", False);
   428     }
   429 #endif
   430 
   431     if (title != NULL) {
   432         char *title_latin1 = SDL_iconv_utf8_latin1((char *) title);
   433         if (!title_latin1) {
   434             SDL_OutOfMemory();
   435             return;
   436         }
   437         status = XStringListToTextProperty(&title_latin1, 1, &titleprop);
   438         SDL_free(title_latin1);
   439         if (status) {
   440             XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
   441             XFree(titleprop.value);
   442         }
   443 #ifdef X_HAVE_UTF8_STRING
   444         if (SDL_X11_HAVE_UTF8) {
   445             status =
   446                 Xutf8TextListToTextProperty(display, (char **) &title, 1,
   447                                             XUTF8StringStyle, &titleprop);
   448             if (status == Success) {
   449                 XSetTextProperty(display, data->window, &titleprop,
   450                                  _NET_WM_NAME);
   451                 XFree(titleprop.value);
   452             }
   453         }
   454 #endif
   455     }
   456     if (icon != NULL) {
   457         char *icon_latin1 = SDL_iconv_utf8_latin1((char *) icon);
   458         if (!icon_latin1) {
   459             SDL_OutOfMemory();
   460             return;
   461         }
   462         status = XStringListToTextProperty(&icon_latin1, 1, &iconprop);
   463         SDL_free(icon_latin1);
   464         if (status) {
   465             XSetTextProperty(display, data->window, &iconprop,
   466                              XA_WM_ICON_NAME);
   467             XFree(iconprop.value);
   468         }
   469 #ifdef X_HAVE_UTF8_STRING
   470         if (SDL_X11_HAVE_UTF8) {
   471             status =
   472                 Xutf8TextListToTextProperty(display, (char **) &icon, 1,
   473                                             XUTF8StringStyle, &iconprop);
   474             if (status == Success) {
   475                 XSetTextProperty(display, data->window, &iconprop,
   476                                  _NET_WM_ICON_NAME);
   477                 XFree(iconprop.value);
   478             }
   479         }
   480 #endif
   481     }
   482 }
   483 
   484 void
   485 X11_SetWindowPosition(_THIS, SDL_Window * window)
   486 {
   487     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   488     SDL_DisplayData *displaydata =
   489         (SDL_DisplayData *) SDL_GetDisplayFromWindow(window)->driverdata;
   490     Display *display = data->videodata->display;
   491 
   492     XMoveWindow(display, data->window, window->x, window->y);
   493 }
   494 
   495 void
   496 X11_SetWindowSize(_THIS, SDL_Window * window)
   497 {
   498     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   499     Display *display = data->videodata->display;
   500 
   501     XMoveWindow(display, data->window, window->w, window->h);
   502 }
   503 
   504 void
   505 X11_ShowWindow(_THIS, SDL_Window * window)
   506 {
   507     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   508     Display *display = data->videodata->display;
   509 
   510     XMapRaised(display, data->window);
   511 }
   512 
   513 void
   514 X11_HideWindow(_THIS, SDL_Window * window)
   515 {
   516     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   517     Display *display = data->videodata->display;
   518 
   519     XUnmapWindow(display, data->window);
   520 }
   521 
   522 void
   523 X11_RaiseWindow(_THIS, SDL_Window * window)
   524 {
   525     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   526     Display *display = data->videodata->display;
   527 
   528     XRaiseWindow(display, data->window);
   529 }
   530 
   531 void
   532 X11_MaximizeWindow(_THIS, SDL_Window * window)
   533 {
   534     /* FIXME: is this even possible? */
   535 }
   536 
   537 void
   538 X11_MinimizeWindow(_THIS, SDL_Window * window)
   539 {
   540     X11_HideWindow(_this, window);
   541 }
   542 
   543 void
   544 X11_RestoreWindow(_THIS, SDL_Window * window)
   545 {
   546     X11_ShowWindow(_this, window);
   547 }
   548 
   549 void
   550 X11_SetWindowGrab(_THIS, SDL_Window * window)
   551 {
   552     /* FIXME */
   553 }
   554 
   555 void
   556 X11_DestroyWindow(_THIS, SDL_Window * window)
   557 {
   558     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   559 
   560     if (data) {
   561         Display *display = data->videodata->display;
   562 #ifdef SDL_VIDEO_OPENGL_GLX
   563         if (window->flags & SDL_WINDOW_OPENGL) {
   564             X11_GL_Shutdown(_this);
   565         }
   566 #endif
   567 #ifdef X_HAVE_UTF8_STRING
   568         if (data->ic) {
   569             XDestroyIC(data->ic);
   570         }
   571 #endif
   572         if (data->created) {
   573             XDestroyWindow(display, data->window);
   574         }
   575         SDL_free(data);
   576     }
   577 }
   578 
   579 SDL_bool
   580 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   581 {
   582     if (info->version.major <= SDL_MAJOR_VERSION) {
   583         /* FIXME! */
   584         return SDL_TRUE;
   585     } else {
   586         SDL_SetError("Application not compiled with SDL %d.%d\n",
   587                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   588         return SDL_FALSE;
   589     }
   590 }
   591 
   592 /* vi: set ts=4 sw=4 expandtab: */