Make SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL) work with GLX_SGI_swap_control.
Fixes Bugzilla #697.
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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 device->gl_data->sgi_swap_interval = 1;
126 /* Set the driver flags */
127 device->handles_any_size = 1;
129 /* Set the function pointers */
130 device->VideoInit = X11_VideoInit;
131 device->ListModes = X11_ListModes;
132 device->SetVideoMode = X11_SetVideoMode;
133 device->ToggleFullScreen = X11_ToggleFullScreen;
134 device->UpdateMouse = X11_UpdateMouse;
135 #if SDL_VIDEO_DRIVER_X11_XV
136 device->CreateYUVOverlay = X11_CreateYUVOverlay;
138 device->SetColors = X11_SetColors;
139 device->UpdateRects = NULL;
140 device->VideoQuit = X11_VideoQuit;
141 device->AllocHWSurface = X11_AllocHWSurface;
142 device->CheckHWBlit = NULL;
143 device->FillHWRect = NULL;
144 device->SetHWColorKey = NULL;
145 device->SetHWAlpha = NULL;
146 device->LockHWSurface = X11_LockHWSurface;
147 device->UnlockHWSurface = X11_UnlockHWSurface;
148 device->FlipHWSurface = X11_FlipHWSurface;
149 device->FreeHWSurface = X11_FreeHWSurface;
150 device->SetGamma = X11_SetVidModeGamma;
151 device->GetGamma = X11_GetVidModeGamma;
152 device->SetGammaRamp = X11_SetGammaRamp;
153 device->GetGammaRamp = NULL;
154 #if SDL_VIDEO_OPENGL_GLX
155 device->GL_LoadLibrary = X11_GL_LoadLibrary;
156 device->GL_GetProcAddress = X11_GL_GetProcAddress;
157 device->GL_GetAttribute = X11_GL_GetAttribute;
158 device->GL_MakeCurrent = X11_GL_MakeCurrent;
159 device->GL_SwapBuffers = X11_GL_SwapBuffers;
161 device->SetCaption = X11_SetCaption;
162 device->SetIcon = X11_SetIcon;
163 device->IconifyWindow = X11_IconifyWindow;
164 device->GrabInput = X11_GrabInput;
165 device->GetWMInfo = X11_GetWMInfo;
166 device->FreeWMCursor = X11_FreeWMCursor;
167 device->CreateWMCursor = X11_CreateWMCursor;
168 device->ShowWMCursor = X11_ShowWMCursor;
169 device->WarpWMCursor = X11_WarpWMCursor;
170 device->CheckMouseMode = X11_CheckMouseMode;
171 device->InitOSKeymap = X11_InitOSKeymap;
172 device->PumpEvents = X11_PumpEvents;
174 device->free = X11_DeleteDevice;
180 VideoBootStrap X11_bootstrap = {
181 "x11", "X Window System",
182 X11_Available, X11_CreateDevice
185 /* Normal X11 error handler routine */
186 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
187 static int x_errhandler(Display *d, XErrorEvent *e)
189 #if SDL_VIDEO_DRIVER_X11_VIDMODE
192 #if SDL_VIDEO_DRIVER_X11_DGAMOUSE
193 extern int dga_error;
196 #if SDL_VIDEO_DRIVER_X11_VIDMODE
197 /* VidMode errors are non-fatal. :) */
198 /* Are the errors offset by one from the error base?
199 e.g. the error base is 143, the code is 148, and the
200 actual error is XF86VidModeExtensionDisabled (4) ?
202 if ( (vm_error >= 0) &&
203 (((e->error_code == BadRequest)&&(e->request_code == vm_error)) ||
204 ((e->error_code > vm_error) &&
205 (e->error_code <= (vm_error+XF86VidModeNumberErrors)))) ) {
208 XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
209 printf("VidMode error: %s\n", errmsg);
214 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
216 #if SDL_VIDEO_DRIVER_X11_DGAMOUSE
217 /* DGA errors can be non-fatal. :) */
218 if ( (dga_error >= 0) &&
219 ((e->error_code > dga_error) &&
220 (e->error_code <= (dga_error+XF86DGANumberErrors))) ) {
223 XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
224 printf("DGA error: %s\n", errmsg);
229 #endif /* SDL_VIDEO_DRIVER_X11_DGAMOUSE */
231 return(X_handler(d,e));
234 /* X11 I/O error handler routine */
235 static int (*XIO_handler)(Display *) = NULL;
236 static int xio_errhandler(Display *d)
238 /* Ack! Lost X11 connection! */
240 /* We will crash if we try to clean up our display */
241 if ( SDL_VideoSurface && current_video->hidden->Ximage ) {
242 SDL_VideoSurface->pixels = NULL;
244 current_video->hidden->X11_Display = NULL;
246 /* Continue with the standard X11 error handler */
247 return(XIO_handler(d));
250 static int (*Xext_handler)(Display *, _Xconst char *, _Xconst char *) = NULL;
251 static int xext_errhandler(Display *d, _Xconst char *ext, _Xconst char *reason)
254 printf("Xext error inside SDL (may be harmless):\n");
255 printf(" Extension \"%s\" %s on display \"%s\".\n",
256 ext, reason, XDisplayString(d));
259 if (SDL_strcmp(reason, "missing") == 0) {
261 * Since the query itself, elsewhere, can handle a missing extension
262 * and the default behaviour in Xlib is to write to stderr, which
263 * generates unnecessary bug reports, we just ignore these.
268 /* Everything else goes to the default handler... */
269 return Xext_handler(d, ext, reason);
272 /* Find out what class name we should use */
273 static char *get_classname(char *classname, int maxlen)
276 #if defined(__LINUX__) || defined(__FREEBSD__)
282 /* First allow environment variable override */
283 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
285 SDL_strlcpy(classname, spot, maxlen);
289 /* Next look at the application's executable name */
290 #if defined(__LINUX__) || defined(__FREEBSD__)
291 #if defined(__LINUX__)
292 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
293 #elif defined(__FREEBSD__)
294 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", getpid());
296 #error Where can we find the executable name?
298 linksize = readlink(procfile, linkfile, sizeof(linkfile)-1);
299 if ( linksize > 0 ) {
300 linkfile[linksize] = '\0';
301 spot = SDL_strrchr(linkfile, '/');
303 SDL_strlcpy(classname, spot+1, maxlen);
305 SDL_strlcpy(classname, linkfile, maxlen);
309 #endif /* __LINUX__ */
311 /* Finally use the default we've used forever */
312 SDL_strlcpy(classname, "SDL_App", maxlen);
316 /* Create auxiliary (toplevel) windows with the current visual */
317 static void create_aux_windows(_THIS)
320 char classname[1024];
321 XSetWindowAttributes xattr;
323 unsigned long app_event_mask;
324 int def_vis = (SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen));
326 /* Look up some useful Atoms */
327 WM_DELETE_WINDOW = XInternAtom(SDL_Display, "WM_DELETE_WINDOW", False);
329 /* Don't create any extra windows if we are being managed */
330 if ( SDL_windowid ) {
332 WMwindow = SDL_strtol(SDL_windowid, NULL, 0);
337 XDestroyWindow(SDL_Display, FSwindow);
339 #if SDL_VIDEO_DRIVER_X11_XINERAMA
340 if ( use_xinerama ) {
341 x = xinerama_info.x_org;
342 y = xinerama_info.y_org;
345 xattr.override_redirect = True;
346 xattr.background_pixel = def_vis ? BlackPixel(SDL_Display, SDL_Screen) : 0;
347 xattr.border_pixel = 0;
348 xattr.colormap = SDL_XColorMap;
350 FSwindow = XCreateWindow(SDL_Display, SDL_Root,
352 this->hidden->depth, InputOutput, SDL_Visual,
353 CWOverrideRedirect | CWBackPixel | CWBorderPixel
357 XSelectInput(SDL_Display, FSwindow, StructureNotifyMask);
359 /* Tell KDE to keep the fullscreen window on top */
364 SDL_memset(&ev, 0, sizeof(ev));
365 ev.xclient.type = ClientMessage;
366 ev.xclient.window = SDL_Root;
367 ev.xclient.message_type = XInternAtom(SDL_Display,
368 "KWM_KEEP_ON_TOP", False);
369 ev.xclient.format = 32;
370 ev.xclient.data.l[0] = FSwindow;
371 ev.xclient.data.l[1] = CurrentTime;
372 mask = SubstructureRedirectMask;
373 XSendEvent(SDL_Display, SDL_Root, False, mask, &ev);
378 /* All window attributes must survive the recreation */
379 hints = XGetWMHints(SDL_Display, WMwindow);
380 XDestroyWindow(SDL_Display, WMwindow);
383 /* Create the window for windowed management */
384 /* (reusing the xattr structure above) */
385 WMwindow = XCreateWindow(SDL_Display, SDL_Root,
387 this->hidden->depth, InputOutput, SDL_Visual,
388 CWBackPixel | CWBorderPixel | CWColormap,
391 /* Set the input hints so we get keyboard input */
393 hints = XAllocWMHints();
395 hints->flags = InputHint;
397 XSetWMHints(SDL_Display, WMwindow, hints);
399 X11_SetCaptionNoLock(this, this->wm_title, this->wm_icon);
401 app_event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
402 | PropertyChangeMask | StructureNotifyMask | KeymapStateMask;
403 XSelectInput(SDL_Display, WMwindow, app_event_mask);
405 /* Set the class hints so we can get an icon (AfterStep) */
406 get_classname(classname, sizeof(classname));
408 XClassHint *classhints;
409 classhints = XAllocClassHint();
410 if(classhints != NULL) {
411 classhints->res_name = classname;
412 classhints->res_class = classname;
413 XSetClassHint(SDL_Display, WMwindow, classhints);
418 /* Setup the communication with the IM server */
419 /* create_aux_windows may be called several times against the same
420 Display. We should reuse the SDL_IM if one has been opened for
421 the Display, so we should not simply reset SDL_IM here. */
423 #ifdef X_HAVE_UTF8_STRING
424 if (SDL_X11_HAVE_UTF8) {
425 /* Discard obsolete resources if any. */
426 if (SDL_IM != NULL && SDL_Display != XDisplayOfIM(SDL_IM)) {
427 /* Just a double check. I don't think this
428 code is ever executed. */
429 SDL_SetError("display has changed while an IM is kept");
431 XUnsetICFocus(SDL_IC);
439 /* Open an input method. */
440 if (SDL_IM == NULL) {
441 char *old_locale = NULL, *old_modifiers = NULL;
444 /* I'm not comfortable to do locale setup
445 here. However, we need C library locale
446 (and xlib modifiers) to be set based on the
447 user's preference to use XIM, and many
448 existing game programs doesn't take care of
449 users' locale preferences, so someone other
450 than the game program should do it.
451 Moreover, ones say that some game programs
452 heavily rely on the C locale behaviour,
453 e.g., strcol()'s, and we can't change the C
454 library locale. Given the situation, I
455 couldn't find better place to do the
458 /* Save the current (application program's)
460 p = setlocale(LC_ALL, NULL);
463 old_locale = SDL_stack_alloc(char, n);
465 SDL_strlcpy(old_locale, p, n);
468 p = XSetLocaleModifiers(NULL);
471 old_modifiers = SDL_stack_alloc(char, n);
472 if ( old_modifiers ) {
473 SDL_strlcpy(old_modifiers, p, n);
477 /* Fetch the user's preferences and open the
478 input method with them. */
479 setlocale(LC_ALL, "");
480 XSetLocaleModifiers("");
481 SDL_IM = XOpenIM(SDL_Display, NULL, classname, classname);
483 /* Restore the application's locale settings
484 so that we don't break the application's
485 expected behaviour. */
487 /* We need to restore the C library
488 locale first, since the
489 interpretation of the X modifier
491 setlocale(LC_ALL, old_locale);
492 SDL_stack_free(old_locale);
494 if ( old_modifiers ) {
495 XSetLocaleModifiers(old_modifiers);
496 SDL_stack_free(old_modifiers);
500 /* Create a new input context for the new window just created. */
501 if (SDL_IM == NULL) {
502 SDL_SetError("no input method could be opened");
504 if (SDL_IC != NULL) {
505 /* Discard the old IC before creating new one. */
506 XUnsetICFocus(SDL_IC);
509 /* Theoretically we should check the current IM supports
510 PreeditNothing+StatusNothing style (i.e., root window method)
511 before creating the IC. However, it is the bottom line method,
512 and we supports any other options. If the IM didn't support
513 root window method, the following call fails, and SDL falls
514 back to pre-XIM keyboard handling. */
515 SDL_IC = pXCreateIC(SDL_IM,
516 XNClientWindow, WMwindow,
517 XNFocusWindow, WMwindow,
518 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
519 XNResourceName, classname,
520 XNResourceClass, classname,
523 if (SDL_IC == NULL) {
524 SDL_SetError("no input context could be created");
528 /* We need to receive X events that an IM wants and to pass
529 them to the IM through XFilterEvent. The set of events may
530 vary depending on the IM implementation and the options
531 specified through various routes. Although unlikely, the
532 xlib specification allows IM to change the event requirement
533 with its own circumstances, it is safe to call SelectInput
534 whenever we re-create an IC. */
535 unsigned long mask = 0;
536 char *ret = pXGetICValues(SDL_IC, XNFilterEvents, &mask, NULL);
538 XUnsetICFocus(SDL_IC);
541 SDL_SetError("no input context could be created");
545 XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
553 /* Allow the window to be deleted by the window manager */
554 XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
557 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
563 /* Open the X11 display */
564 display = NULL; /* Get it from DISPLAY environment variable */
566 if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
567 (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
572 SDL_Display = XOpenDisplay(display);
573 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
574 /* On Tru64 if linking without -lX11, it fails and you get following message.
575 * Xlib: connection to ":0.0" refused by server
576 * Xlib: XDM authorization key matches an existing client!
578 * It succeeds if retrying 1 second later
579 * or if running xhost +localhost on shell.
582 if ( SDL_Display == NULL ) {
584 SDL_Display = XOpenDisplay(display);
587 if ( SDL_Display == NULL ) {
588 SDL_SetError("Couldn't open X11 display");
592 XSynchronize(SDL_Display, True);
595 /* Create an alternate X display for graphics updates -- allows us
596 to do graphics updates in a separate thread from event handling.
597 Thread-safe X11 doesn't seem to exist.
599 GFX_Display = XOpenDisplay(display);
600 if ( GFX_Display == NULL ) {
601 XCloseDisplay(SDL_Display);
603 SDL_SetError("Couldn't open X11 display");
607 /* Set the normal X error handler */
608 X_handler = XSetErrorHandler(x_errhandler);
610 /* Set the error handler if we lose the X display */
611 XIO_handler = XSetIOErrorHandler(xio_errhandler);
613 /* Set the X extension error handler */
614 Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
616 /* use default screen (from $DISPLAY) */
617 SDL_Screen = DefaultScreen(SDL_Display);
619 #ifndef NO_SHARED_MEMORY
620 /* Check for MIT shared memory extension */
623 use_mitshm = XShmQueryExtension(SDL_Display);
625 #endif /* NO_SHARED_MEMORY */
627 /* Get the available video modes */
628 if(X11_GetVideoModes(this) < 0) {
629 XCloseDisplay(GFX_Display);
631 XCloseDisplay(SDL_Display);
636 /* Determine the current screen size */
637 this->info.current_w = DisplayWidth(SDL_Display, SDL_Screen);
638 this->info.current_h = DisplayHeight(SDL_Display, SDL_Screen);
640 /* Determine the default screen depth:
641 Use the default visual (or at least one with the same depth) */
642 SDL_DisplayColormap = DefaultColormap(SDL_Display, SDL_Screen);
643 for(i = 0; i < this->hidden->nvisuals; i++)
644 if(this->hidden->visuals[i].depth == DefaultDepth(SDL_Display,
647 if(i == this->hidden->nvisuals) {
648 /* default visual was useless, take the deepest one instead */
651 SDL_Visual = this->hidden->visuals[i].visual;
652 if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
653 SDL_XColorMap = SDL_DisplayColormap;
655 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
656 SDL_Visual, AllocNone);
658 this->hidden->depth = this->hidden->visuals[i].depth;
659 vformat->BitsPerPixel = this->hidden->visuals[i].bpp;
660 if ( vformat->BitsPerPixel > 8 ) {
661 vformat->Rmask = SDL_Visual->red_mask;
662 vformat->Gmask = SDL_Visual->green_mask;
663 vformat->Bmask = SDL_Visual->blue_mask;
665 if ( this->hidden->depth == 32 ) {
666 vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
668 X11_SaveVidModeGamma(this);
670 /* Allow environment override of screensaver disable. */
671 env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
673 allow_screensaver = SDL_atoi(env);
675 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
676 allow_screensaver = 0;
678 allow_screensaver = 1;
682 /* See if we have been passed a window to use */
683 SDL_windowid = SDL_getenv("SDL_WINDOWID");
685 /* Create the fullscreen and managed windows */
686 create_aux_windows(this);
688 /* Create the blank cursor */
689 SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask,
690 BLANK_CWIDTH, BLANK_CHEIGHT,
691 BLANK_CHOTX, BLANK_CHOTY);
693 /* Fill in some window manager capabilities */
694 this->info.wm_available = 1;
701 static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
703 /* Clean up OpenGL */
705 screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
707 X11_GL_Shutdown(this);
709 if ( ! SDL_windowid ) {
710 /* Hide the managed window */
712 XUnmapWindow(SDL_Display, WMwindow);
714 if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
715 screen->flags &= ~SDL_FULLSCREEN;
716 X11_LeaveFullScreen(this);
719 /* Destroy the output window */
721 XDestroyWindow(SDL_Display, SDL_Window);
724 /* Free the colormap entries */
728 numcolors = SDL_Visual->map_entries;
729 for ( pixel=0; pixel<numcolors; ++pixel ) {
730 while ( SDL_XPixels[pixel] > 0 ) {
731 XFreeColors(GFX_Display,
732 SDL_DisplayColormap,&pixel,1,0);
733 --SDL_XPixels[pixel];
736 SDL_free(SDL_XPixels);
740 /* Free the graphics context */
742 XFreeGC(SDL_Display, SDL_GC);
748 static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
750 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
751 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
753 if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
756 if ( SDL_strcmp(window, "center") == 0 ) {
761 *x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
762 *y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
768 static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
772 hints = XAllocSizeHints();
774 if (!(flags & SDL_RESIZABLE)) {
775 hints->min_width = hints->max_width = w;
776 hints->min_height = hints->max_height = h;
777 hints->flags = PMaxSize | PMinSize;
779 if ( flags & SDL_FULLSCREEN ) {
782 hints->flags |= USPosition;
784 /* Center it, if desired */
785 if ( X11_WindowPosition(this, &hints->x, &hints->y, w, h) ) {
786 hints->flags |= USPosition;
787 XMoveWindow(SDL_Display, WMwindow, hints->x, hints->y);
789 /* Flush the resize event so we don't catch it later */
790 XSync(SDL_Display, True);
792 XSetWMNormalHints(SDL_Display, WMwindow, hints);
796 /* Respect the window caption style */
797 if ( flags & SDL_NOFRAME ) {
801 /* We haven't modified the window manager hints yet */
804 /* First try to set MWM hints */
805 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
806 if ( WM_HINTS != None ) {
807 /* Hints used by Motif compliant window managers */
810 unsigned long functions;
811 unsigned long decorations;
813 unsigned long status;
814 } MWMHints = { (1L << 1), 0, 0, 0, 0 };
816 XChangeProperty(SDL_Display, WMwindow,
817 WM_HINTS, WM_HINTS, 32,
819 (unsigned char *)&MWMHints,
820 sizeof(MWMHints)/sizeof(long));
823 /* Now try to set KWM hints */
824 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
825 if ( WM_HINTS != None ) {
828 XChangeProperty(SDL_Display, WMwindow,
829 WM_HINTS, WM_HINTS, 32,
831 (unsigned char *)&KWMHints,
832 sizeof(KWMHints)/sizeof(long));
835 /* Now try to set GNOME hints */
836 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
837 if ( WM_HINTS != None ) {
840 XChangeProperty(SDL_Display, WMwindow,
841 WM_HINTS, WM_HINTS, 32,
843 (unsigned char *)&GNOMEHints,
844 sizeof(GNOMEHints)/sizeof(long));
847 /* Finally set the transient hints if necessary */
849 XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
855 /* We haven't modified the window manager hints yet */
858 /* First try to unset MWM hints */
859 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
860 if ( WM_HINTS != None ) {
861 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
864 /* Now try to unset KWM hints */
865 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
866 if ( WM_HINTS != None ) {
867 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
870 /* Now try to unset GNOME hints */
871 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
872 if ( WM_HINTS != None ) {
873 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
876 /* Finally unset the transient hints if necessary */
878 /* NOTE: Does this work? */
879 XSetTransientForHint(SDL_Display, WMwindow, None);
884 static int X11_CreateWindow(_THIS, SDL_Surface *screen,
885 int w, int h, int bpp, Uint32 flags)
892 /* If a window is already present, destroy it and start fresh */
894 X11_DestroyWindow(this, screen);
895 switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
898 /* See if we have been given a window id */
899 if ( SDL_windowid ) {
900 SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
905 /* find out which visual we are going to use */
906 if ( flags & SDL_OPENGL ) {
909 vi = X11_GL_GetVisual(this);
915 } else if ( SDL_windowid ) {
918 XGetWindowAttributes(SDL_Display, SDL_Window, &a);
922 for ( i = 0; i < this->hidden->nvisuals; i++ ) {
923 if ( this->hidden->visuals[i].bpp == bpp )
926 if ( i == this->hidden->nvisuals ) {
927 SDL_SetError("No matching visual for requested depth");
928 return -1; /* should never happen */
930 vis = this->hidden->visuals[i].visual;
931 depth = this->hidden->visuals[i].depth;
934 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);
936 vis_change = (vis != SDL_Visual);
938 this->hidden->depth = depth;
940 /* Allocate the new pixel format for this video mode */
941 if ( this->hidden->depth == 32 ) {
942 Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask));
946 if ( ! SDL_ReallocFormat(screen, bpp,
947 vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
951 /* Create the appropriate colormap */
952 if ( SDL_XColorMap != SDL_DisplayColormap ) {
953 XFreeColormap(SDL_Display, SDL_XColorMap);
955 if ( SDL_Visual->class == PseudoColor ) {
958 /* Allocate the pixel flags */
959 ncolors = SDL_Visual->map_entries;
960 SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
961 if(SDL_XPixels == NULL) {
965 SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
967 /* always allocate a private colormap on non-default visuals */
968 if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
969 flags |= SDL_HWPALETTE;
971 if ( flags & SDL_HWPALETTE ) {
972 screen->flags |= SDL_HWPALETTE;
973 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
974 SDL_Visual, AllocAll);
976 SDL_XColorMap = SDL_DisplayColormap;
978 } else if ( SDL_Visual->class == DirectColor ) {
980 /* Create a colormap which we can manipulate for gamma */
981 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
982 SDL_Visual, AllocAll);
983 XSync(SDL_Display, False);
985 /* Initialize the colormap to the identity mapping */
986 SDL_GetGammaRamp(0, 0, 0);
987 this->screen = screen;
988 X11_SetGammaRamp(this, this->gamma);
991 /* Create a read-only colormap for our window */
992 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
993 SDL_Visual, AllocNone);
996 /* Recreate the auxiliary windows, if needed (required for GL) */
998 create_aux_windows(this);
1000 if(screen->flags & SDL_HWPALETTE) {
1001 /* Since the full-screen window might have got a nonzero background
1002 colour (0 is white on some displays), we should reset the
1003 background to 0 here since that is what the user expects
1004 with a private colormap */
1005 XSetWindowBackground(SDL_Display, FSwindow, 0);
1006 XClearWindow(SDL_Display, FSwindow);
1009 /* resize the (possibly new) window manager window */
1010 if( !SDL_windowid ) {
1011 X11_SetSizeHints(this, w, h, flags);
1014 XResizeWindow(SDL_Display, WMwindow, w, h);
1017 /* Create (or use) the X11 display window */
1018 if ( !SDL_windowid ) {
1019 if ( flags & SDL_OPENGL ) {
1020 if ( X11_GL_CreateWindow(this, w, h) < 0 ) {
1024 XSetWindowAttributes swa;
1026 swa.background_pixel = 0;
1027 swa.border_pixel = 0;
1028 swa.colormap = SDL_XColorMap;
1029 SDL_Window = XCreateWindow(SDL_Display, WMwindow,
1030 0, 0, w, h, 0, depth,
1031 InputOutput, SDL_Visual,
1032 CWBackPixel | CWBorderPixel
1033 | CWColormap, &swa);
1035 /* Only manage our input if we own the window */
1036 XSelectInput(SDL_Display, SDL_Window,
1037 ( EnterWindowMask | LeaveWindowMask
1038 | ButtonPressMask | ButtonReleaseMask
1039 | PointerMotionMask | ExposureMask ));
1041 /* Create the graphics context here, once we have a window */
1042 if ( flags & SDL_OPENGL ) {
1043 if ( X11_GL_CreateContext(this) < 0 ) {
1046 screen->flags |= SDL_OPENGL;
1051 gcv.graphics_exposures = False;
1052 SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1053 GCGraphicsExposures, &gcv);
1055 SDL_SetError("Couldn't create graphics context");
1060 /* Set our colormaps when not setting a GL mode */
1061 if ( ! (flags & SDL_OPENGL) ) {
1062 XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
1063 if( !SDL_windowid ) {
1064 XSetWindowColormap(SDL_Display, FSwindow, SDL_XColorMap);
1065 XSetWindowColormap(SDL_Display, WMwindow, SDL_XColorMap);
1069 #if 0 /* This is an experiment - are the graphics faster now? - nope. */
1070 if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1072 /* Cache the window in the server, when possible */
1075 XSetWindowAttributes a;
1077 xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen);
1078 a.backing_store = DoesBackingStore(xscreen);
1079 if ( a.backing_store != NotUseful ) {
1080 XChangeWindowAttributes(SDL_Display, SDL_Window,
1081 CWBackingStore, &a);
1085 /* Map them both and go fullscreen, if requested */
1086 if ( ! SDL_windowid ) {
1087 XMapWindow(SDL_Display, SDL_Window);
1088 XMapWindow(SDL_Display, WMwindow);
1089 X11_WaitMapped(this, WMwindow);
1090 if ( flags & SDL_FULLSCREEN ) {
1091 screen->flags |= SDL_FULLSCREEN;
1092 X11_EnterFullScreen(this);
1094 screen->flags &= ~SDL_FULLSCREEN;
1101 static int X11_ResizeWindow(_THIS,
1102 SDL_Surface *screen, int w, int h, Uint32 flags)
1104 if ( ! SDL_windowid ) {
1105 /* Resize the window manager window */
1106 X11_SetSizeHints(this, w, h, flags);
1109 XResizeWindow(SDL_Display, WMwindow, w, h);
1111 /* Resize the fullscreen and display windows */
1112 if ( flags & SDL_FULLSCREEN ) {
1113 if ( screen->flags & SDL_FULLSCREEN ) {
1114 X11_ResizeFullScreen(this);
1116 screen->flags |= SDL_FULLSCREEN;
1117 X11_EnterFullScreen(this);
1120 if ( screen->flags & SDL_FULLSCREEN ) {
1121 screen->flags &= ~SDL_FULLSCREEN;
1122 X11_LeaveFullScreen(this);
1125 XResizeWindow(SDL_Display, SDL_Window, w, h);
1130 SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1131 int width, int height, int bpp, Uint32 flags)
1135 /* Lock the event thread, in multi-threading environments */
1136 SDL_Lock_EventThread();
1138 /* Check the combination of flags we were passed */
1139 if ( flags & SDL_FULLSCREEN ) {
1140 /* Clear fullscreen flag if not supported */
1141 if ( SDL_windowid ) {
1142 flags &= ~SDL_FULLSCREEN;
1146 /* Flush any delayed updates */
1147 XSync(GFX_Display, False);
1149 /* Set up the X11 window */
1150 saved_flags = current->flags;
1151 if ( (SDL_Window) && ((saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL))
1152 && (bpp == current->format->BitsPerPixel)
1153 && ((saved_flags&SDL_NOFRAME) == (flags&SDL_NOFRAME)) ) {
1154 if (X11_ResizeWindow(this, current, width, height, flags) < 0) {
1159 if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1165 /* Update the internal keyboard state */
1166 X11_SetKeyboardState(SDL_Display, NULL);
1168 /* When the window is first mapped, ignore non-modifier keys */
1169 if ( !current->w && !current->h ) {
1170 Uint8 *keys = SDL_GetKeyState(NULL);
1172 for ( i = 0; i < SDLK_LAST; ++i ) {
1187 keys[i] = SDL_RELEASED;
1193 /* Set up the new mode framebuffer */
1194 if ( ((current->w != width) || (current->h != height)) ||
1195 ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1197 current->h = height;
1198 current->pitch = SDL_CalculatePitch(current);
1199 if (X11_ResizeImage(this, current, flags) < 0) {
1205 /* Clear these flags and set them only if they are in the new set. */
1206 current->flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
1207 current->flags |= (flags&(SDL_RESIZABLE|SDL_NOFRAME));
1210 /* Release the event thread */
1211 XSync(SDL_Display, False);
1212 SDL_Unlock_EventThread();
1218 static int X11_ToggleFullScreen(_THIS, int on)
1220 Uint32 event_thread;
1222 /* Don't switch if we don't own the window */
1223 if ( SDL_windowid ) {
1227 /* Don't lock if we are the event thread */
1228 event_thread = SDL_EventThreadID();
1229 if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1232 if ( event_thread ) {
1233 SDL_Lock_EventThread();
1236 this->screen->flags |= SDL_FULLSCREEN;
1237 X11_EnterFullScreen(this);
1239 this->screen->flags &= ~SDL_FULLSCREEN;
1240 X11_LeaveFullScreen(this);
1242 X11_RefreshDisplay(this);
1243 if ( event_thread ) {
1244 SDL_Unlock_EventThread();
1246 SDL_ResetKeyboard();
1250 /* Update the current mouse state and position */
1251 static void X11_UpdateMouse(_THIS)
1258 /* Lock the event thread, in multi-threading environments */
1259 SDL_Lock_EventThread();
1260 if ( XQueryPointer(SDL_Display, SDL_Window, &u1, ¤t_win,
1261 &u2, &u2, &x, &y, &mask) ) {
1262 if ( (x >= 0) && (x < SDL_VideoSurface->w) &&
1263 (y >= 0) && (y < SDL_VideoSurface->h) ) {
1264 SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
1265 SDL_PrivateMouseMotion(0, 0, x, y);
1267 SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1270 SDL_Unlock_EventThread();
1273 /* simple colour distance metric. Supposed to be better than a plain
1274 Euclidian distance anyway. */
1275 #define COLOUR_FACTOR 3
1276 #define LIGHT_FACTOR 1
1277 #define COLOUR_DIST(r1, g1, b1, r2, g2, b2) \
1278 (COLOUR_FACTOR * (abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)) \
1279 + LIGHT_FACTOR * abs(r1 + g1 + b1 - (r2 + g2 + b2)))
1281 static void allocate_nearest(_THIS, SDL_Color *colors,
1282 SDL_Color *want, int nwant)
1285 * There is no way to know which ones to choose from, so we retrieve
1286 * the entire colormap and try the nearest possible, until we find one
1291 for(i = 0; i < 256; i++)
1294 * XQueryColors sets the flags in the XColor struct, so we use
1295 * that to keep track of which colours are available
1297 XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1299 for(i = 0; i < nwant; i++) {
1303 int mindist = 0x7fffffff;
1307 for(j = 0; j < 256; j++) {
1310 continue; /* unavailable colour cell */
1311 rj = all[j].red >> 8;
1312 gj = all[j].green >> 8;
1313 bj = all[j].blue >> 8;
1314 d2 = COLOUR_DIST(ri, gi, bi, rj, gj, bj);
1320 if(SDL_XPixels[best])
1321 continue; /* already allocated, waste no more time */
1323 if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1325 colors[c->pixel].r = c->red >> 8;
1326 colors[c->pixel].g = c->green >> 8;
1327 colors[c->pixel].b = c->blue >> 8;
1328 ++SDL_XPixels[c->pixel];
1331 * The colour couldn't be allocated, probably being
1332 * owned as a r/w cell by another client. Flag it as
1333 * unavailable and try again. The termination of the
1334 * loop is guaranteed since at least black and white
1343 int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1347 /* Check to make sure we have a colormap allocated */
1348 if ( SDL_XPixels == NULL ) {
1351 if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1352 /* private writable colormap: just set the colours we need */
1355 xcmap = SDL_stack_alloc(XColor, ncolors);
1358 for ( i=0; i<ncolors; ++i ) {
1359 xcmap[i].pixel = i + firstcolor;
1360 xcmap[i].red = (colors[i].r<<8)|colors[i].r;
1361 xcmap[i].green = (colors[i].g<<8)|colors[i].g;
1362 xcmap[i].blue = (colors[i].b<<8)|colors[i].b;
1363 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1365 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1366 XSync(GFX_Display, False);
1367 SDL_stack_free(xcmap);
1370 * Shared colormap: We only allocate read-only cells, which
1371 * increases the likelyhood of colour sharing with other
1372 * clients. The pixel values will almost certainly be
1373 * different from the requested ones, so the user has to
1374 * walk the colormap and see which index got what colour.
1376 * We can work directly with the logical palette since it
1377 * has already been set when we get here.
1379 SDL_Color *want, *reject;
1380 unsigned long *freelist;
1383 int nc = this->screen->format->palette->ncolors;
1384 colors = this->screen->format->palette->colors;
1385 freelist = SDL_stack_alloc(unsigned long, nc);
1386 /* make sure multiple allocations of the same cell are freed */
1387 for(i = 0; i < ncolors; i++) {
1388 int pixel = firstcolor + i;
1389 while(SDL_XPixels[pixel]) {
1390 freelist[nfree++] = pixel;
1391 --SDL_XPixels[pixel];
1394 XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1395 SDL_stack_free(freelist);
1397 want = SDL_stack_alloc(SDL_Color, ncolors);
1398 reject = SDL_stack_alloc(SDL_Color, ncolors);
1399 SDL_memcpy(want, colors + firstcolor, ncolors * sizeof(SDL_Color));
1400 /* make sure the user isn't fooled by her own wishes
1401 (black is safe, always available in the default colormap) */
1402 SDL_memset(colors + firstcolor, 0, ncolors * sizeof(SDL_Color));
1404 /* now try to allocate the colours */
1405 for(i = 0; i < ncolors; i++) {
1407 col.red = want[i].r << 8;
1408 col.green = want[i].g << 8;
1409 col.blue = want[i].b << 8;
1410 col.flags = DoRed | DoGreen | DoBlue;
1411 if(XAllocColor(GFX_Display, SDL_XColorMap, &col)) {
1412 /* We got the colour, or at least the nearest
1413 the hardware could get. */
1414 colors[col.pixel].r = col.red >> 8;
1415 colors[col.pixel].g = col.green >> 8;
1416 colors[col.pixel].b = col.blue >> 8;
1417 ++SDL_XPixels[col.pixel];
1420 * no more free cells, add it to the list
1421 * of rejected colours
1423 reject[nrej++] = want[i];
1427 allocate_nearest(this, colors, reject, nrej);
1428 SDL_stack_free(reject);
1429 SDL_stack_free(want);
1434 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1439 /* See if actually setting the gamma is supported */
1440 if ( SDL_Visual->class != DirectColor ) {
1441 SDL_SetError("Gamma correction not supported on this visual");
1445 /* Calculate the appropriate palette for the given gamma ramp */
1446 ncolors = SDL_Visual->map_entries;
1447 for ( i=0; i<ncolors; ++i ) {
1448 Uint8 c = (256 * i / ncolors);
1449 xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
1450 xcmap[i].red = ramp[0*256+c];
1451 xcmap[i].green = ramp[1*256+c];
1452 xcmap[i].blue = ramp[2*256+c];
1453 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1455 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1456 XSync(GFX_Display, False);
1460 /* Note: If we are terminated, this could be called in the middle of
1461 another SDL video routine -- notably UpdateRects.
1463 void X11_VideoQuit(_THIS)
1465 /* Shutdown everything that's still up */
1466 /* The event thread should be done, so we can touch SDL_Display */
1467 if ( SDL_Display != NULL ) {
1468 /* Flush any delayed updates */
1469 XSync(GFX_Display, False);
1471 /* Close the connection with the IM server */
1472 #ifdef X_HAVE_UTF8_STRING
1473 if (SDL_IC != NULL) {
1474 XUnsetICFocus(SDL_IC);
1478 if (SDL_IM != NULL) {
1484 /* Start shutting down the windows */
1485 X11_DestroyImage(this, this->screen);
1486 X11_DestroyWindow(this, this->screen);
1487 X11_FreeVideoModes(this);
1488 if ( SDL_XColorMap != SDL_DisplayColormap ) {
1489 XFreeColormap(SDL_Display, SDL_XColorMap);
1491 if ( SDL_iconcolors ) {
1492 unsigned long pixel;
1493 Colormap dcmap = DefaultColormap(SDL_Display,
1495 for(pixel = 0; pixel < 256; ++pixel) {
1496 while(SDL_iconcolors[pixel] > 0) {
1497 XFreeColors(GFX_Display,
1498 dcmap, &pixel, 1, 0);
1499 --SDL_iconcolors[pixel];
1502 SDL_free(SDL_iconcolors);
1503 SDL_iconcolors = NULL;
1506 /* Restore gamma settings if they've changed */
1507 if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1508 X11_SwapVidModeGamma(this);
1511 /* Free that blank cursor */
1512 if ( SDL_BlankCursor != NULL ) {
1513 this->FreeWMCursor(this, SDL_BlankCursor);
1514 SDL_BlankCursor = NULL;
1517 /* Close the X11 graphics connection */
1518 if ( GFX_Display != NULL ) {
1519 XCloseDisplay(GFX_Display);
1523 /* Close the X11 display connection */
1524 XCloseDisplay(SDL_Display);
1527 /* Reset the X11 error handlers */
1528 if ( XIO_handler ) {
1529 XSetIOErrorHandler(XIO_handler);
1532 XSetErrorHandler(X_handler);
1535 /* Unload GL library after X11 shuts down */
1536 X11_GL_UnloadLibrary(this);
1538 if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1539 /* Direct screen access, no memory buffer */
1540 this->screen->pixels = NULL;
1543 #if SDL_VIDEO_DRIVER_X11_XME