2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 /* X11 based SDL video driver implementation.
25 Note: This implementation does not currently need X11 thread locking,
26 since the event thread uses a separate X connection and any
27 additional locking necessary is handled internally. However,
28 if full locking is neccessary, take a look at XInitThreads().
32 #include <sys/ioctl.h>
35 #include <sys/fcntl.h>
38 #include "SDL_endian.h"
39 #include "SDL_timer.h"
40 #include "SDL_thread.h"
41 #include "SDL_video.h"
42 #include "SDL_mouse.h"
43 #include "../SDL_sysvideo.h"
44 #include "../SDL_pixels_c.h"
45 #include "../../events/SDL_events_c.h"
46 #include "SDL_x11video.h"
47 #include "SDL_x11wm_c.h"
48 #include "SDL_x11mouse_c.h"
49 #include "SDL_x11events_c.h"
50 #include "SDL_x11modes_c.h"
51 #include "SDL_x11image_c.h"
52 #include "SDL_x11yuv_c.h"
53 #include "SDL_x11gl_c.h"
54 #include "SDL_x11gamma_c.h"
55 #include "../blank_cursor.h"
57 #ifdef X_HAVE_UTF8_STRING
61 /* Initialization/Query functions */
62 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat);
63 static SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
64 static int X11_ToggleFullScreen(_THIS, int on);
65 static void X11_UpdateMouse(_THIS);
66 static int X11_SetColors(_THIS, int firstcolor, int ncolors,
68 static int X11_SetGammaRamp(_THIS, Uint16 *ramp);
69 static void X11_VideoQuit(_THIS);
72 /* X11 driver bootstrap functions */
74 static int X11_Available(void)
76 Display *display = NULL;
77 if ( SDL_X11_LoadSymbols() ) {
78 display = XOpenDisplay(NULL);
79 if ( display != NULL ) {
80 XCloseDisplay(display);
82 SDL_X11_UnloadSymbols();
84 return(display != NULL);
87 static void X11_DeleteDevice(SDL_VideoDevice *device)
90 if ( device->hidden ) {
91 SDL_free(device->hidden);
93 if ( device->gl_data ) {
94 SDL_free(device->gl_data);
97 SDL_X11_UnloadSymbols();
101 static SDL_VideoDevice *X11_CreateDevice(int devindex)
103 SDL_VideoDevice *device = NULL;
105 if ( SDL_X11_LoadSymbols() ) {
106 /* Initialize all variables that we clean on shutdown */
107 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
109 SDL_memset(device, 0, (sizeof *device));
110 device->hidden = (struct SDL_PrivateVideoData *)
111 SDL_malloc((sizeof *device->hidden));
112 device->gl_data = (struct SDL_PrivateGLData *)
113 SDL_malloc((sizeof *device->gl_data));
115 if ( (device == NULL) || (device->hidden == NULL) ||
116 (device->gl_data == NULL) ) {
118 X11_DeleteDevice(device); /* calls SDL_X11_UnloadSymbols(). */
121 SDL_memset(device->hidden, 0, (sizeof *device->hidden));
122 SDL_memset(device->gl_data, 0, (sizeof *device->gl_data));
124 #if SDL_VIDEO_OPENGL_GLX
125 device->gl_data->swap_interval = -1;
128 /* Set the driver flags */
129 device->handles_any_size = 1;
131 /* Set the function pointers */
132 device->VideoInit = X11_VideoInit;
133 device->ListModes = X11_ListModes;
134 device->SetVideoMode = X11_SetVideoMode;
135 device->ToggleFullScreen = X11_ToggleFullScreen;
136 device->UpdateMouse = X11_UpdateMouse;
137 #if SDL_VIDEO_DRIVER_X11_XV
138 device->CreateYUVOverlay = X11_CreateYUVOverlay;
140 device->SetColors = X11_SetColors;
141 device->UpdateRects = NULL;
142 device->VideoQuit = X11_VideoQuit;
143 device->AllocHWSurface = X11_AllocHWSurface;
144 device->CheckHWBlit = NULL;
145 device->FillHWRect = NULL;
146 device->SetHWColorKey = NULL;
147 device->SetHWAlpha = NULL;
148 device->LockHWSurface = X11_LockHWSurface;
149 device->UnlockHWSurface = X11_UnlockHWSurface;
150 device->FlipHWSurface = X11_FlipHWSurface;
151 device->FreeHWSurface = X11_FreeHWSurface;
152 device->SetGamma = X11_SetVidModeGamma;
153 device->GetGamma = X11_GetVidModeGamma;
154 device->SetGammaRamp = X11_SetGammaRamp;
155 device->GetGammaRamp = NULL;
156 #if SDL_VIDEO_OPENGL_GLX
157 device->GL_LoadLibrary = X11_GL_LoadLibrary;
158 device->GL_GetProcAddress = X11_GL_GetProcAddress;
159 device->GL_GetAttribute = X11_GL_GetAttribute;
160 device->GL_MakeCurrent = X11_GL_MakeCurrent;
161 device->GL_SwapBuffers = X11_GL_SwapBuffers;
163 device->SetCaption = X11_SetCaption;
164 device->SetIcon = X11_SetIcon;
165 device->IconifyWindow = X11_IconifyWindow;
166 device->GrabInput = X11_GrabInput;
167 device->GetWMInfo = X11_GetWMInfo;
168 device->FreeWMCursor = X11_FreeWMCursor;
169 device->CreateWMCursor = X11_CreateWMCursor;
170 device->ShowWMCursor = X11_ShowWMCursor;
171 device->WarpWMCursor = X11_WarpWMCursor;
172 device->CheckMouseMode = X11_CheckMouseMode;
173 device->InitOSKeymap = X11_InitOSKeymap;
174 device->PumpEvents = X11_PumpEvents;
176 device->free = X11_DeleteDevice;
182 VideoBootStrap X11_bootstrap = {
183 "x11", "X Window System",
184 X11_Available, X11_CreateDevice
187 /* Normal X11 error handler routine */
188 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
189 static int x_errhandler(Display *d, XErrorEvent *e)
191 #if SDL_VIDEO_DRIVER_X11_VIDMODE
194 #if SDL_VIDEO_DRIVER_X11_DGAMOUSE
195 extern int dga_error;
198 #if SDL_VIDEO_DRIVER_X11_VIDMODE
199 /* VidMode errors are non-fatal. :) */
200 /* Are the errors offset by one from the error base?
201 e.g. the error base is 143, the code is 148, and the
202 actual error is XF86VidModeExtensionDisabled (4) ?
204 if ( (vm_error >= 0) &&
205 (((e->error_code == BadRequest)&&(e->request_code == vm_error)) ||
206 ((e->error_code > vm_error) &&
207 (e->error_code <= (vm_error+XF86VidModeNumberErrors)))) ) {
210 XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
211 printf("VidMode error: %s\n", errmsg);
216 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
218 #if SDL_VIDEO_DRIVER_X11_DGAMOUSE
219 /* DGA errors can be non-fatal. :) */
220 if ( (dga_error >= 0) &&
221 ((e->error_code > dga_error) &&
222 (e->error_code <= (dga_error+XF86DGANumberErrors))) ) {
225 XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
226 printf("DGA error: %s\n", errmsg);
231 #endif /* SDL_VIDEO_DRIVER_X11_DGAMOUSE */
233 return(X_handler(d,e));
236 /* X11 I/O error handler routine */
237 static int (*XIO_handler)(Display *) = NULL;
238 static int xio_errhandler(Display *d)
240 /* Ack! Lost X11 connection! */
242 /* We will crash if we try to clean up our display */
243 if ( SDL_VideoSurface && current_video->hidden->Ximage ) {
244 SDL_VideoSurface->pixels = NULL;
246 current_video->hidden->X11_Display = NULL;
248 /* Continue with the standard X11 error handler */
249 return(XIO_handler(d));
252 static int (*Xext_handler)(Display *, _Xconst char *, _Xconst char *) = NULL;
253 static int xext_errhandler(Display *d, _Xconst char *ext, _Xconst char *reason)
256 printf("Xext error inside SDL (may be harmless):\n");
257 printf(" Extension \"%s\" %s on display \"%s\".\n",
258 ext, reason, XDisplayString(d));
261 if (SDL_strcmp(reason, "missing") == 0) {
263 * Since the query itself, elsewhere, can handle a missing extension
264 * and the default behaviour in Xlib is to write to stderr, which
265 * generates unnecessary bug reports, we just ignore these.
270 /* Everything else goes to the default handler... */
271 return Xext_handler(d, ext, reason);
274 /* Find out what class name we should use */
275 static char *get_classname(char *classname, int maxlen)
278 #if defined(__LINUX__) || defined(__FREEBSD__)
284 /* First allow environment variable override */
285 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
287 SDL_strlcpy(classname, spot, maxlen);
291 /* Next look at the application's executable name */
292 #if defined(__LINUX__) || defined(__FREEBSD__)
293 #if defined(__LINUX__)
294 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
295 #elif defined(__FREEBSD__)
296 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", getpid());
298 #error Where can we find the executable name?
300 linksize = readlink(procfile, linkfile, sizeof(linkfile)-1);
301 if ( linksize > 0 ) {
302 linkfile[linksize] = '\0';
303 spot = SDL_strrchr(linkfile, '/');
305 SDL_strlcpy(classname, spot+1, maxlen);
307 SDL_strlcpy(classname, linkfile, maxlen);
311 #endif /* __LINUX__ */
313 /* Finally use the default we've used forever */
314 SDL_strlcpy(classname, "SDL_App", maxlen);
318 /* Create auxiliary (toplevel) windows with the current visual */
319 static void create_aux_windows(_THIS)
322 char classname[1024];
323 XSetWindowAttributes xattr;
325 unsigned long app_event_mask;
326 int def_vis = (SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen));
328 /* Look up some useful Atoms */
329 WM_DELETE_WINDOW = XInternAtom(SDL_Display, "WM_DELETE_WINDOW", False);
331 /* Don't create any extra windows if we are being managed */
332 if ( SDL_windowid ) {
334 WMwindow = SDL_strtol(SDL_windowid, NULL, 0);
339 XDestroyWindow(SDL_Display, FSwindow);
341 #if SDL_VIDEO_DRIVER_X11_XINERAMA
342 if ( use_xinerama ) {
343 x = xinerama_info.x_org;
344 y = xinerama_info.y_org;
347 xattr.override_redirect = True;
348 xattr.background_pixel = def_vis ? BlackPixel(SDL_Display, SDL_Screen) : 0;
349 xattr.border_pixel = 0;
350 xattr.colormap = SDL_XColorMap;
352 FSwindow = XCreateWindow(SDL_Display, SDL_Root,
354 this->hidden->depth, InputOutput, SDL_Visual,
355 CWOverrideRedirect | CWBackPixel | CWBorderPixel
359 XSelectInput(SDL_Display, FSwindow, StructureNotifyMask);
361 /* Tell KDE to keep the fullscreen window on top */
366 SDL_memset(&ev, 0, sizeof(ev));
367 ev.xclient.type = ClientMessage;
368 ev.xclient.window = SDL_Root;
369 ev.xclient.message_type = XInternAtom(SDL_Display,
370 "KWM_KEEP_ON_TOP", False);
371 ev.xclient.format = 32;
372 ev.xclient.data.l[0] = FSwindow;
373 ev.xclient.data.l[1] = CurrentTime;
374 mask = SubstructureRedirectMask;
375 XSendEvent(SDL_Display, SDL_Root, False, mask, &ev);
380 /* All window attributes must survive the recreation */
381 hints = XGetWMHints(SDL_Display, WMwindow);
382 XDestroyWindow(SDL_Display, WMwindow);
385 /* Create the window for windowed management */
386 /* (reusing the xattr structure above) */
387 WMwindow = XCreateWindow(SDL_Display, SDL_Root,
389 this->hidden->depth, InputOutput, SDL_Visual,
390 CWBackPixel | CWBorderPixel | CWColormap,
393 /* Set the input hints so we get keyboard input */
395 hints = XAllocWMHints();
397 hints->flags = InputHint;
399 XSetWMHints(SDL_Display, WMwindow, hints);
401 X11_SetCaptionNoLock(this, this->wm_title, this->wm_icon);
403 app_event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
404 | PropertyChangeMask | StructureNotifyMask | KeymapStateMask;
405 XSelectInput(SDL_Display, WMwindow, app_event_mask);
407 /* Set the class hints so we can get an icon (AfterStep) */
408 get_classname(classname, sizeof(classname));
410 XClassHint *classhints;
411 classhints = XAllocClassHint();
412 if(classhints != NULL) {
413 classhints->res_name = classname;
414 classhints->res_class = classname;
415 XSetClassHint(SDL_Display, WMwindow, classhints);
421 pid_t pid = getpid();
424 if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1) {
425 Atom _NET_WM_PID = XInternAtom(SDL_Display, "_NET_WM_PID", False);
426 Atom WM_CLIENT_MACHINE = XInternAtom(SDL_Display, "WM_CLIENT_MACHINE", False);
428 hostname[sizeof(hostname)-1] = '\0';
429 XChangeProperty(SDL_Display, WMwindow, _NET_WM_PID, XA_CARDINAL, 32,
430 PropModeReplace, (unsigned char *)&pid, 1);
431 XChangeProperty(SDL_Display, WMwindow, WM_CLIENT_MACHINE, XA_STRING, 8,
432 PropModeReplace, (unsigned char *)hostname, SDL_strlen(hostname));
436 /* Setup the communication with the IM server */
437 /* create_aux_windows may be called several times against the same
438 Display. We should reuse the SDL_IM if one has been opened for
439 the Display, so we should not simply reset SDL_IM here. */
441 #ifdef X_HAVE_UTF8_STRING
442 if (SDL_X11_HAVE_UTF8) {
443 /* Discard obsolete resources if any. */
444 if (SDL_IM != NULL && SDL_Display != XDisplayOfIM(SDL_IM)) {
445 /* Just a double check. I don't think this
446 code is ever executed. */
447 SDL_SetError("display has changed while an IM is kept");
449 XUnsetICFocus(SDL_IC);
457 /* Open an input method. */
458 if (SDL_IM == NULL) {
459 char *old_locale = NULL, *old_modifiers = NULL;
462 /* I'm not comfortable to do locale setup
463 here. However, we need C library locale
464 (and xlib modifiers) to be set based on the
465 user's preference to use XIM, and many
466 existing game programs doesn't take care of
467 users' locale preferences, so someone other
468 than the game program should do it.
469 Moreover, ones say that some game programs
470 heavily rely on the C locale behaviour,
471 e.g., strcol()'s, and we can't change the C
472 library locale. Given the situation, I
473 couldn't find better place to do the
476 /* Save the current (application program's)
478 p = setlocale(LC_ALL, NULL);
481 old_locale = SDL_stack_alloc(char, n);
483 SDL_strlcpy(old_locale, p, n);
486 p = XSetLocaleModifiers(NULL);
489 old_modifiers = SDL_stack_alloc(char, n);
490 if ( old_modifiers ) {
491 SDL_strlcpy(old_modifiers, p, n);
495 /* Fetch the user's preferences and open the
496 input method with them. */
497 setlocale(LC_ALL, "");
498 XSetLocaleModifiers("");
499 SDL_IM = XOpenIM(SDL_Display, NULL, classname, classname);
501 /* Restore the application's locale settings
502 so that we don't break the application's
503 expected behaviour. */
505 /* We need to restore the C library
506 locale first, since the
507 interpretation of the X modifier
509 setlocale(LC_ALL, old_locale);
510 SDL_stack_free(old_locale);
512 if ( old_modifiers ) {
513 XSetLocaleModifiers(old_modifiers);
514 SDL_stack_free(old_modifiers);
518 /* Create a new input context for the new window just created. */
519 if (SDL_IM == NULL) {
520 SDL_SetError("no input method could be opened");
522 if (SDL_IC != NULL) {
523 /* Discard the old IC before creating new one. */
524 XUnsetICFocus(SDL_IC);
527 /* Theoretically we should check the current IM supports
528 PreeditNothing+StatusNothing style (i.e., root window method)
529 before creating the IC. However, it is the bottom line method,
530 and we supports any other options. If the IM didn't support
531 root window method, the following call fails, and SDL falls
532 back to pre-XIM keyboard handling. */
533 SDL_IC = pXCreateIC(SDL_IM,
534 XNClientWindow, WMwindow,
535 XNFocusWindow, WMwindow,
536 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
537 XNResourceName, classname,
538 XNResourceClass, classname,
541 if (SDL_IC == NULL) {
542 SDL_SetError("no input context could be created");
546 /* We need to receive X events that an IM wants and to pass
547 them to the IM through XFilterEvent. The set of events may
548 vary depending on the IM implementation and the options
549 specified through various routes. Although unlikely, the
550 xlib specification allows IM to change the event requirement
551 with its own circumstances, it is safe to call SelectInput
552 whenever we re-create an IC. */
553 unsigned long mask = 0;
554 char *ret = pXGetICValues(SDL_IC, XNFilterEvents, &mask, NULL);
556 XUnsetICFocus(SDL_IC);
559 SDL_SetError("no input context could be created");
563 XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
571 /* Allow the window to be deleted by the window manager */
572 XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
575 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
581 /* Open the X11 display */
582 display = NULL; /* Get it from DISPLAY environment variable */
584 if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
585 (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
590 SDL_Display = XOpenDisplay(display);
591 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
592 /* On Tru64 if linking without -lX11, it fails and you get following message.
593 * Xlib: connection to ":0.0" refused by server
594 * Xlib: XDM authorization key matches an existing client!
596 * It succeeds if retrying 1 second later
597 * or if running xhost +localhost on shell.
600 if ( SDL_Display == NULL ) {
602 SDL_Display = XOpenDisplay(display);
605 if ( SDL_Display == NULL ) {
606 SDL_SetError("Couldn't open X11 display");
610 XSynchronize(SDL_Display, True);
613 /* Create an alternate X display for graphics updates -- allows us
614 to do graphics updates in a separate thread from event handling.
615 Thread-safe X11 doesn't seem to exist.
617 GFX_Display = XOpenDisplay(display);
618 if ( GFX_Display == NULL ) {
619 XCloseDisplay(SDL_Display);
621 SDL_SetError("Couldn't open X11 display");
625 /* Set the normal X error handler */
626 X_handler = XSetErrorHandler(x_errhandler);
628 /* Set the error handler if we lose the X display */
629 XIO_handler = XSetIOErrorHandler(xio_errhandler);
631 /* Set the X extension error handler */
632 Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
634 /* use default screen (from $DISPLAY) */
635 SDL_Screen = DefaultScreen(SDL_Display);
637 #ifndef NO_SHARED_MEMORY
638 /* Check for MIT shared memory extension */
641 use_mitshm = XShmQueryExtension(SDL_Display);
643 #endif /* NO_SHARED_MEMORY */
645 /* Get the available video modes */
646 if(X11_GetVideoModes(this) < 0) {
647 XCloseDisplay(GFX_Display);
649 XCloseDisplay(SDL_Display);
654 /* Determine the current screen size */
655 this->info.current_w = DisplayWidth(SDL_Display, SDL_Screen);
656 this->info.current_h = DisplayHeight(SDL_Display, SDL_Screen);
658 /* Determine the default screen depth:
659 Use the default visual (or at least one with the same depth) */
660 SDL_DisplayColormap = DefaultColormap(SDL_Display, SDL_Screen);
661 for(i = 0; i < this->hidden->nvisuals; i++)
662 if(this->hidden->visuals[i].depth == DefaultDepth(SDL_Display,
665 if(i == this->hidden->nvisuals) {
666 /* default visual was useless, take the deepest one instead */
669 SDL_Visual = this->hidden->visuals[i].visual;
670 if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
671 SDL_XColorMap = SDL_DisplayColormap;
673 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
674 SDL_Visual, AllocNone);
676 this->hidden->depth = this->hidden->visuals[i].depth;
677 vformat->BitsPerPixel = this->hidden->visuals[i].bpp;
678 if ( vformat->BitsPerPixel > 8 ) {
679 vformat->Rmask = SDL_Visual->red_mask;
680 vformat->Gmask = SDL_Visual->green_mask;
681 vformat->Bmask = SDL_Visual->blue_mask;
683 if ( this->hidden->depth == 32 ) {
684 vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
686 X11_SaveVidModeGamma(this);
688 /* Allow environment override of screensaver disable. */
689 env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
691 allow_screensaver = SDL_atoi(env);
693 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
694 allow_screensaver = 0;
696 allow_screensaver = 1;
700 /* See if we have been passed a window to use */
701 SDL_windowid = SDL_getenv("SDL_WINDOWID");
703 /* Create the fullscreen and managed windows */
704 create_aux_windows(this);
706 /* Create the blank cursor */
707 SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask,
708 BLANK_CWIDTH, BLANK_CHEIGHT,
709 BLANK_CHOTX, BLANK_CHOTY);
711 /* Fill in some window manager capabilities */
712 this->info.wm_available = 1;
719 static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
721 /* Clean up OpenGL */
723 screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
725 X11_GL_Shutdown(this);
727 if ( ! SDL_windowid ) {
728 /* Hide the managed window */
730 XUnmapWindow(SDL_Display, WMwindow);
732 if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
733 screen->flags &= ~SDL_FULLSCREEN;
734 X11_LeaveFullScreen(this);
737 /* Destroy the output window */
739 XDestroyWindow(SDL_Display, SDL_Window);
742 /* Free the colormap entries */
746 numcolors = SDL_Visual->map_entries;
747 for ( pixel=0; pixel<numcolors; ++pixel ) {
748 while ( SDL_XPixels[pixel] > 0 ) {
749 XFreeColors(GFX_Display,
750 SDL_DisplayColormap,&pixel,1,0);
751 --SDL_XPixels[pixel];
754 SDL_free(SDL_XPixels);
758 /* Free the graphics context */
760 XFreeGC(SDL_Display, SDL_GC);
766 static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
768 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
769 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
771 if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
774 if ( SDL_strcmp(window, "center") == 0 ) {
779 *x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
780 *y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
786 static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
790 hints = XAllocSizeHints();
792 if (!(flags & SDL_RESIZABLE)) {
793 hints->min_width = hints->max_width = w;
794 hints->min_height = hints->max_height = h;
795 hints->flags = PMaxSize | PMinSize;
797 if ( flags & SDL_FULLSCREEN ) {
800 hints->flags |= USPosition;
802 /* Center it, if desired */
803 if ( X11_WindowPosition(this, &hints->x, &hints->y, w, h) ) {
804 hints->flags |= USPosition;
806 /* Hints must be set before moving the window, otherwise an
807 unwanted ConfigureNotify event will be issued */
808 XSetWMNormalHints(SDL_Display, WMwindow, hints);
810 XMoveWindow(SDL_Display, WMwindow, hints->x, hints->y);
812 /* Flush the resize event so we don't catch it later */
813 XSync(SDL_Display, True);
815 XSetWMNormalHints(SDL_Display, WMwindow, hints);
819 /* Respect the window caption style */
820 if ( flags & SDL_NOFRAME ) {
824 /* We haven't modified the window manager hints yet */
827 /* First try to set MWM hints */
828 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
829 if ( WM_HINTS != None ) {
830 /* Hints used by Motif compliant window managers */
833 unsigned long functions;
834 unsigned long decorations;
836 unsigned long status;
837 } MWMHints = { (1L << 1), 0, 0, 0, 0 };
839 XChangeProperty(SDL_Display, WMwindow,
840 WM_HINTS, WM_HINTS, 32,
842 (unsigned char *)&MWMHints,
843 sizeof(MWMHints)/sizeof(long));
846 /* Now try to set KWM hints */
847 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
848 if ( WM_HINTS != None ) {
851 XChangeProperty(SDL_Display, WMwindow,
852 WM_HINTS, WM_HINTS, 32,
854 (unsigned char *)&KWMHints,
855 sizeof(KWMHints)/sizeof(long));
858 /* Now try to set GNOME hints */
859 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
860 if ( WM_HINTS != None ) {
863 XChangeProperty(SDL_Display, WMwindow,
864 WM_HINTS, WM_HINTS, 32,
866 (unsigned char *)&GNOMEHints,
867 sizeof(GNOMEHints)/sizeof(long));
870 /* Finally set the transient hints if necessary */
872 XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
878 /* We haven't modified the window manager hints yet */
881 /* First try to unset MWM hints */
882 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
883 if ( WM_HINTS != None ) {
884 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
887 /* Now try to unset KWM hints */
888 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
889 if ( WM_HINTS != None ) {
890 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
893 /* Now try to unset GNOME hints */
894 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
895 if ( WM_HINTS != None ) {
896 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
899 /* Finally unset the transient hints if necessary */
901 XDeleteProperty(SDL_Display, WMwindow, XA_WM_TRANSIENT_FOR);
906 static int X11_CreateWindow(_THIS, SDL_Surface *screen,
907 int w, int h, int bpp, Uint32 flags)
914 /* If a window is already present, destroy it and start fresh */
916 X11_DestroyWindow(this, screen);
917 switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
920 /* See if we have been given a window id */
921 if ( SDL_windowid ) {
922 SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
927 /* find out which visual we are going to use */
928 if ( flags & SDL_OPENGL ) {
931 vi = X11_GL_GetVisual(this);
937 } else if ( SDL_windowid ) {
940 XGetWindowAttributes(SDL_Display, SDL_Window, &a);
944 for ( i = 0; i < this->hidden->nvisuals; i++ ) {
945 if ( this->hidden->visuals[i].bpp == bpp )
948 if ( i == this->hidden->nvisuals ) {
949 SDL_SetError("No matching visual for requested depth");
950 return -1; /* should never happen */
952 vis = this->hidden->visuals[i].visual;
953 depth = this->hidden->visuals[i].depth;
956 printf("Choosing %s visual at %d bpp - %d colormap entries\n", vis->class == PseudoColor ? "PseudoColor" : (vis->class == TrueColor ? "TrueColor" : (vis->class == DirectColor ? "DirectColor" : "Unknown")), depth, vis->map_entries);
958 vis_change = (vis != SDL_Visual);
960 this->hidden->depth = depth;
962 /* Allocate the new pixel format for this video mode */
963 if ( this->hidden->depth == 32 ) {
964 Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask));
968 if ( ! SDL_ReallocFormat(screen, bpp,
969 vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
973 /* Create the appropriate colormap */
974 if ( SDL_XColorMap != SDL_DisplayColormap ) {
975 XFreeColormap(SDL_Display, SDL_XColorMap);
977 if ( SDL_Visual->class == PseudoColor ) {
980 /* Allocate the pixel flags */
981 ncolors = SDL_Visual->map_entries;
982 SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
983 if(SDL_XPixels == NULL) {
987 SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
989 /* always allocate a private colormap on non-default visuals */
990 if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
991 flags |= SDL_HWPALETTE;
993 if ( flags & SDL_HWPALETTE ) {
994 screen->flags |= SDL_HWPALETTE;
995 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
996 SDL_Visual, AllocAll);
998 SDL_XColorMap = SDL_DisplayColormap;
1000 } else if ( SDL_Visual->class == DirectColor ) {
1002 /* Create a colormap which we can manipulate for gamma */
1003 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1004 SDL_Visual, AllocAll);
1005 XSync(SDL_Display, False);
1007 /* Initialize the colormap to the identity mapping */
1008 SDL_GetGammaRamp(0, 0, 0);
1009 this->screen = screen;
1010 X11_SetGammaRamp(this, this->gamma);
1011 this->screen = NULL;
1013 /* Create a read-only colormap for our window */
1014 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1015 SDL_Visual, AllocNone);
1018 /* Recreate the auxiliary windows, if needed (required for GL) */
1020 create_aux_windows(this);
1022 if(screen->flags & SDL_HWPALETTE) {
1023 /* Since the full-screen window might have got a nonzero background
1024 colour (0 is white on some displays), we should reset the
1025 background to 0 here since that is what the user expects
1026 with a private colormap */
1027 XSetWindowBackground(SDL_Display, FSwindow, 0);
1028 XClearWindow(SDL_Display, FSwindow);
1031 /* resize the (possibly new) window manager window */
1032 if( !SDL_windowid ) {
1033 X11_SetSizeHints(this, w, h, flags);
1036 XResizeWindow(SDL_Display, WMwindow, w, h);
1039 /* Create (or use) the X11 display window */
1040 if ( !SDL_windowid ) {
1041 if ( flags & SDL_OPENGL ) {
1042 if ( X11_GL_CreateWindow(this, w, h) < 0 ) {
1046 XSetWindowAttributes swa;
1048 swa.background_pixel = 0;
1049 swa.border_pixel = 0;
1050 swa.colormap = SDL_XColorMap;
1051 SDL_Window = XCreateWindow(SDL_Display, WMwindow,
1052 0, 0, w, h, 0, depth,
1053 InputOutput, SDL_Visual,
1054 CWBackPixel | CWBorderPixel
1055 | CWColormap, &swa);
1057 /* Only manage our input if we own the window */
1058 XSelectInput(SDL_Display, SDL_Window,
1059 ( EnterWindowMask | LeaveWindowMask
1060 | ButtonPressMask | ButtonReleaseMask
1061 | PointerMotionMask | ExposureMask ));
1063 /* Create the graphics context here, once we have a window */
1064 if ( flags & SDL_OPENGL ) {
1065 if ( X11_GL_CreateContext(this) < 0 ) {
1068 screen->flags |= SDL_OPENGL;
1073 gcv.graphics_exposures = False;
1074 SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1075 GCGraphicsExposures, &gcv);
1077 SDL_SetError("Couldn't create graphics context");
1082 /* Set our colormaps when not setting a GL mode */
1083 if ( ! (flags & SDL_OPENGL) ) {
1084 XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
1085 if( !SDL_windowid ) {
1086 XSetWindowColormap(SDL_Display, FSwindow, SDL_XColorMap);
1087 XSetWindowColormap(SDL_Display, WMwindow, SDL_XColorMap);
1091 #if 0 /* This is an experiment - are the graphics faster now? - nope. */
1092 if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1094 /* Cache the window in the server, when possible */
1097 XSetWindowAttributes a;
1099 xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen);
1100 a.backing_store = DoesBackingStore(xscreen);
1101 if ( a.backing_store != NotUseful ) {
1102 XChangeWindowAttributes(SDL_Display, SDL_Window,
1103 CWBackingStore, &a);
1107 /* Map them both and go fullscreen, if requested */
1108 if ( ! SDL_windowid ) {
1109 XMapWindow(SDL_Display, SDL_Window);
1110 XMapWindow(SDL_Display, WMwindow);
1111 X11_WaitMapped(this, WMwindow);
1112 if ( flags & SDL_FULLSCREEN ) {
1113 screen->flags |= SDL_FULLSCREEN;
1114 X11_EnterFullScreen(this);
1116 screen->flags &= ~SDL_FULLSCREEN;
1123 static int X11_ResizeWindow(_THIS,
1124 SDL_Surface *screen, int w, int h, Uint32 flags)
1126 if ( ! SDL_windowid ) {
1127 /* Resize the window manager window */
1128 X11_SetSizeHints(this, w, h, flags);
1131 XResizeWindow(SDL_Display, WMwindow, w, h);
1133 /* Resize the fullscreen and display windows */
1134 if ( flags & SDL_FULLSCREEN ) {
1135 if ( screen->flags & SDL_FULLSCREEN ) {
1136 X11_ResizeFullScreen(this);
1138 screen->flags |= SDL_FULLSCREEN;
1139 X11_EnterFullScreen(this);
1142 if ( screen->flags & SDL_FULLSCREEN ) {
1143 screen->flags &= ~SDL_FULLSCREEN;
1144 X11_LeaveFullScreen(this);
1147 XResizeWindow(SDL_Display, SDL_Window, w, h);
1152 SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1153 int width, int height, int bpp, Uint32 flags)
1157 /* Lock the event thread, in multi-threading environments */
1158 SDL_Lock_EventThread();
1160 /* Check the combination of flags we were passed */
1161 if ( flags & SDL_FULLSCREEN ) {
1162 /* Clear fullscreen flag if not supported */
1163 if ( SDL_windowid ) {
1164 flags &= ~SDL_FULLSCREEN;
1168 /* Flush any delayed updates */
1169 XSync(GFX_Display, False);
1171 /* Set up the X11 window */
1172 saved_flags = current->flags;
1173 if ( (SDL_Window) && ((saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL))
1174 && (bpp == current->format->BitsPerPixel)
1175 && ((saved_flags&SDL_NOFRAME) == (flags&SDL_NOFRAME)) ) {
1176 if (X11_ResizeWindow(this, current, width, height, flags) < 0) {
1180 X11_PendingConfigureNotifyWidth = width;
1181 X11_PendingConfigureNotifyHeight = height;
1183 if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1189 /* Update the internal keyboard state */
1190 X11_SetKeyboardState(SDL_Display, NULL);
1192 /* When the window is first mapped, ignore non-modifier keys */
1193 if ( !current->w && !current->h ) {
1194 Uint8 *keys = SDL_GetKeyState(NULL);
1196 for ( i = 0; i < SDLK_LAST; ++i ) {
1211 keys[i] = SDL_RELEASED;
1217 /* Set up the new mode framebuffer */
1218 if ( ((current->w != width) || (current->h != height)) ||
1219 ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1221 current->h = height;
1222 current->pitch = SDL_CalculatePitch(current);
1223 if (X11_ResizeImage(this, current, flags) < 0) {
1229 /* Clear these flags and set them only if they are in the new set. */
1230 current->flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
1231 current->flags |= (flags&(SDL_RESIZABLE|SDL_NOFRAME));
1234 /* Release the event thread */
1235 XSync(SDL_Display, False);
1236 SDL_Unlock_EventThread();
1242 static int X11_ToggleFullScreen(_THIS, int on)
1244 Uint32 event_thread;
1246 /* Don't switch if we don't own the window */
1247 if ( SDL_windowid ) {
1251 /* Don't lock if we are the event thread */
1252 event_thread = SDL_EventThreadID();
1253 if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1256 if ( event_thread ) {
1257 SDL_Lock_EventThread();
1260 this->screen->flags |= SDL_FULLSCREEN;
1261 X11_EnterFullScreen(this);
1263 this->screen->flags &= ~SDL_FULLSCREEN;
1264 X11_LeaveFullScreen(this);
1266 X11_RefreshDisplay(this);
1267 if ( event_thread ) {
1268 SDL_Unlock_EventThread();
1270 SDL_ResetKeyboard();
1274 /* Update the current mouse state and position */
1275 static void X11_UpdateMouse(_THIS)
1282 /* Lock the event thread, in multi-threading environments */
1283 SDL_Lock_EventThread();
1284 if ( XQueryPointer(SDL_Display, SDL_Window, &u1, ¤t_win,
1285 &u2, &u2, &x, &y, &mask) ) {
1286 if ( (x >= 0) && (x < SDL_VideoSurface->w) &&
1287 (y >= 0) && (y < SDL_VideoSurface->h) ) {
1288 SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
1289 SDL_PrivateMouseMotion(0, 0, x, y);
1291 SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1294 SDL_Unlock_EventThread();
1297 /* simple colour distance metric. Supposed to be better than a plain
1298 Euclidian distance anyway. */
1299 #define COLOUR_FACTOR 3
1300 #define LIGHT_FACTOR 1
1301 #define COLOUR_DIST(r1, g1, b1, r2, g2, b2) \
1302 (COLOUR_FACTOR * (abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)) \
1303 + LIGHT_FACTOR * abs(r1 + g1 + b1 - (r2 + g2 + b2)))
1305 static void allocate_nearest(_THIS, SDL_Color *colors,
1306 SDL_Color *want, int nwant)
1309 * There is no way to know which ones to choose from, so we retrieve
1310 * the entire colormap and try the nearest possible, until we find one
1315 for(i = 0; i < 256; i++)
1318 * XQueryColors sets the flags in the XColor struct, so we use
1319 * that to keep track of which colours are available
1321 XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1323 for(i = 0; i < nwant; i++) {
1327 int mindist = 0x7fffffff;
1331 for(j = 0; j < 256; j++) {
1334 continue; /* unavailable colour cell */
1335 rj = all[j].red >> 8;
1336 gj = all[j].green >> 8;
1337 bj = all[j].blue >> 8;
1338 d2 = COLOUR_DIST(ri, gi, bi, rj, gj, bj);
1344 if(SDL_XPixels[best])
1345 continue; /* already allocated, waste no more time */
1347 if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1349 colors[c->pixel].r = c->red >> 8;
1350 colors[c->pixel].g = c->green >> 8;
1351 colors[c->pixel].b = c->blue >> 8;
1352 ++SDL_XPixels[c->pixel];
1355 * The colour couldn't be allocated, probably being
1356 * owned as a r/w cell by another client. Flag it as
1357 * unavailable and try again. The termination of the
1358 * loop is guaranteed since at least black and white
1367 int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1371 /* Check to make sure we have a colormap allocated */
1372 if ( SDL_XPixels == NULL ) {
1375 if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1376 /* private writable colormap: just set the colours we need */
1379 xcmap = SDL_stack_alloc(XColor, ncolors);
1382 for ( i=0; i<ncolors; ++i ) {
1383 xcmap[i].pixel = i + firstcolor;
1384 xcmap[i].red = (colors[i].r<<8)|colors[i].r;
1385 xcmap[i].green = (colors[i].g<<8)|colors[i].g;
1386 xcmap[i].blue = (colors[i].b<<8)|colors[i].b;
1387 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1389 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1390 XSync(GFX_Display, False);
1391 SDL_stack_free(xcmap);
1394 * Shared colormap: We only allocate read-only cells, which
1395 * increases the likelyhood of colour sharing with other
1396 * clients. The pixel values will almost certainly be
1397 * different from the requested ones, so the user has to
1398 * walk the colormap and see which index got what colour.
1400 * We can work directly with the logical palette since it
1401 * has already been set when we get here.
1403 SDL_Color *want, *reject;
1404 unsigned long *freelist;
1407 int nc = this->screen->format->palette->ncolors;
1408 colors = this->screen->format->palette->colors;
1409 freelist = SDL_stack_alloc(unsigned long, nc);
1410 /* make sure multiple allocations of the same cell are freed */
1411 for(i = 0; i < ncolors; i++) {
1412 int pixel = firstcolor + i;
1413 while(SDL_XPixels[pixel]) {
1414 freelist[nfree++] = pixel;
1415 --SDL_XPixels[pixel];
1418 XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1419 SDL_stack_free(freelist);
1421 want = SDL_stack_alloc(SDL_Color, ncolors);
1422 reject = SDL_stack_alloc(SDL_Color, ncolors);
1423 SDL_memcpy(want, colors + firstcolor, ncolors * sizeof(SDL_Color));
1424 /* make sure the user isn't fooled by her own wishes
1425 (black is safe, always available in the default colormap) */
1426 SDL_memset(colors + firstcolor, 0, ncolors * sizeof(SDL_Color));
1428 /* now try to allocate the colours */
1429 for(i = 0; i < ncolors; i++) {
1431 col.red = want[i].r << 8;
1432 col.green = want[i].g << 8;
1433 col.blue = want[i].b << 8;
1434 col.flags = DoRed | DoGreen | DoBlue;
1435 if(XAllocColor(GFX_Display, SDL_XColorMap, &col)) {
1436 /* We got the colour, or at least the nearest
1437 the hardware could get. */
1438 colors[col.pixel].r = col.red >> 8;
1439 colors[col.pixel].g = col.green >> 8;
1440 colors[col.pixel].b = col.blue >> 8;
1441 ++SDL_XPixels[col.pixel];
1444 * no more free cells, add it to the list
1445 * of rejected colours
1447 reject[nrej++] = want[i];
1451 allocate_nearest(this, colors, reject, nrej);
1452 SDL_stack_free(reject);
1453 SDL_stack_free(want);
1458 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1463 /* See if actually setting the gamma is supported */
1464 if ( SDL_Visual->class != DirectColor ) {
1465 SDL_SetError("Gamma correction not supported on this visual");
1469 /* Calculate the appropriate palette for the given gamma ramp */
1470 ncolors = SDL_Visual->map_entries;
1471 for ( i=0; i<ncolors; ++i ) {
1472 Uint8 c = (256 * i / ncolors);
1473 xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
1474 xcmap[i].red = ramp[0*256+c];
1475 xcmap[i].green = ramp[1*256+c];
1476 xcmap[i].blue = ramp[2*256+c];
1477 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1479 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1480 XSync(GFX_Display, False);
1484 /* Note: If we are terminated, this could be called in the middle of
1485 another SDL video routine -- notably UpdateRects.
1487 void X11_VideoQuit(_THIS)
1489 /* Shutdown everything that's still up */
1490 /* The event thread should be done, so we can touch SDL_Display */
1491 if ( SDL_Display != NULL ) {
1492 /* Flush any delayed updates */
1493 XSync(GFX_Display, False);
1495 /* Close the connection with the IM server */
1496 #ifdef X_HAVE_UTF8_STRING
1497 if (SDL_IC != NULL) {
1498 XUnsetICFocus(SDL_IC);
1502 if (SDL_IM != NULL) {
1508 /* Start shutting down the windows */
1509 X11_DestroyImage(this, this->screen);
1510 X11_DestroyWindow(this, this->screen);
1511 X11_FreeVideoModes(this);
1512 if ( SDL_XColorMap != SDL_DisplayColormap ) {
1513 XFreeColormap(SDL_Display, SDL_XColorMap);
1515 if ( SDL_iconcolors ) {
1516 unsigned long pixel;
1517 Colormap dcmap = DefaultColormap(SDL_Display,
1519 for(pixel = 0; pixel < 256; ++pixel) {
1520 while(SDL_iconcolors[pixel] > 0) {
1521 XFreeColors(GFX_Display,
1522 dcmap, &pixel, 1, 0);
1523 --SDL_iconcolors[pixel];
1526 SDL_free(SDL_iconcolors);
1527 SDL_iconcolors = NULL;
1530 /* Restore gamma settings if they've changed */
1531 if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1532 X11_SwapVidModeGamma(this);
1535 /* Free that blank cursor */
1536 if ( SDL_BlankCursor != NULL ) {
1537 this->FreeWMCursor(this, SDL_BlankCursor);
1538 SDL_BlankCursor = NULL;
1541 /* Close the X11 graphics connection */
1542 if ( GFX_Display != NULL ) {
1543 XCloseDisplay(GFX_Display);
1547 /* Close the X11 display connection */
1548 XCloseDisplay(SDL_Display);
1551 /* Reset the X11 error handlers */
1552 if ( XIO_handler ) {
1553 XSetIOErrorHandler(XIO_handler);
1556 XSetErrorHandler(X_handler);
1559 /* Unload GL library after X11 shuts down */
1560 X11_GL_UnloadLibrary(this);
1562 if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1563 /* Direct screen access, no memory buffer */
1564 this->screen->pixels = NULL;
1567 #if SDL_VIDEO_DRIVER_X11_XME