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 #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);
420 /* Setup the communication with the IM server */
421 /* create_aux_windows may be called several times against the same
422 Display. We should reuse the SDL_IM if one has been opened for
423 the Display, so we should not simply reset SDL_IM here. */
425 #ifdef X_HAVE_UTF8_STRING
426 if (SDL_X11_HAVE_UTF8) {
427 /* Discard obsolete resources if any. */
428 if (SDL_IM != NULL && SDL_Display != XDisplayOfIM(SDL_IM)) {
429 /* Just a double check. I don't think this
430 code is ever executed. */
431 SDL_SetError("display has changed while an IM is kept");
433 XUnsetICFocus(SDL_IC);
441 /* Open an input method. */
442 if (SDL_IM == NULL) {
443 char *old_locale = NULL, *old_modifiers = NULL;
446 /* I'm not comfortable to do locale setup
447 here. However, we need C library locale
448 (and xlib modifiers) to be set based on the
449 user's preference to use XIM, and many
450 existing game programs doesn't take care of
451 users' locale preferences, so someone other
452 than the game program should do it.
453 Moreover, ones say that some game programs
454 heavily rely on the C locale behaviour,
455 e.g., strcol()'s, and we can't change the C
456 library locale. Given the situation, I
457 couldn't find better place to do the
460 /* Save the current (application program's)
462 p = setlocale(LC_ALL, NULL);
465 old_locale = SDL_stack_alloc(char, n);
467 SDL_strlcpy(old_locale, p, n);
470 p = XSetLocaleModifiers(NULL);
473 old_modifiers = SDL_stack_alloc(char, n);
474 if ( old_modifiers ) {
475 SDL_strlcpy(old_modifiers, p, n);
479 /* Fetch the user's preferences and open the
480 input method with them. */
481 setlocale(LC_ALL, "");
482 XSetLocaleModifiers("");
483 SDL_IM = XOpenIM(SDL_Display, NULL, classname, classname);
485 /* Restore the application's locale settings
486 so that we don't break the application's
487 expected behaviour. */
489 /* We need to restore the C library
490 locale first, since the
491 interpretation of the X modifier
493 setlocale(LC_ALL, old_locale);
494 SDL_stack_free(old_locale);
496 if ( old_modifiers ) {
497 XSetLocaleModifiers(old_modifiers);
498 SDL_stack_free(old_modifiers);
502 /* Create a new input context for the new window just created. */
503 if (SDL_IM == NULL) {
504 SDL_SetError("no input method could be opened");
506 if (SDL_IC != NULL) {
507 /* Discard the old IC before creating new one. */
508 XUnsetICFocus(SDL_IC);
511 /* Theoretically we should check the current IM supports
512 PreeditNothing+StatusNothing style (i.e., root window method)
513 before creating the IC. However, it is the bottom line method,
514 and we supports any other options. If the IM didn't support
515 root window method, the following call fails, and SDL falls
516 back to pre-XIM keyboard handling. */
517 SDL_IC = pXCreateIC(SDL_IM,
518 XNClientWindow, WMwindow,
519 XNFocusWindow, WMwindow,
520 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
521 XNResourceName, classname,
522 XNResourceClass, classname,
525 if (SDL_IC == NULL) {
526 SDL_SetError("no input context could be created");
530 /* We need to receive X events that an IM wants and to pass
531 them to the IM through XFilterEvent. The set of events may
532 vary depending on the IM implementation and the options
533 specified through various routes. Although unlikely, the
534 xlib specification allows IM to change the event requirement
535 with its own circumstances, it is safe to call SelectInput
536 whenever we re-create an IC. */
537 unsigned long mask = 0;
538 char *ret = pXGetICValues(SDL_IC, XNFilterEvents, &mask, NULL);
540 XUnsetICFocus(SDL_IC);
543 SDL_SetError("no input context could be created");
547 XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
555 /* Allow the window to be deleted by the window manager */
556 XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
559 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
565 /* Open the X11 display */
566 display = NULL; /* Get it from DISPLAY environment variable */
568 if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
569 (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
574 SDL_Display = XOpenDisplay(display);
575 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
576 /* On Tru64 if linking without -lX11, it fails and you get following message.
577 * Xlib: connection to ":0.0" refused by server
578 * Xlib: XDM authorization key matches an existing client!
580 * It succeeds if retrying 1 second later
581 * or if running xhost +localhost on shell.
584 if ( SDL_Display == NULL ) {
586 SDL_Display = XOpenDisplay(display);
589 if ( SDL_Display == NULL ) {
590 SDL_SetError("Couldn't open X11 display");
594 XSynchronize(SDL_Display, True);
597 /* Create an alternate X display for graphics updates -- allows us
598 to do graphics updates in a separate thread from event handling.
599 Thread-safe X11 doesn't seem to exist.
601 GFX_Display = XOpenDisplay(display);
602 if ( GFX_Display == NULL ) {
603 XCloseDisplay(SDL_Display);
605 SDL_SetError("Couldn't open X11 display");
609 /* Set the normal X error handler */
610 X_handler = XSetErrorHandler(x_errhandler);
612 /* Set the error handler if we lose the X display */
613 XIO_handler = XSetIOErrorHandler(xio_errhandler);
615 /* Set the X extension error handler */
616 Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
618 /* use default screen (from $DISPLAY) */
619 SDL_Screen = DefaultScreen(SDL_Display);
621 #ifndef NO_SHARED_MEMORY
622 /* Check for MIT shared memory extension */
625 use_mitshm = XShmQueryExtension(SDL_Display);
627 #endif /* NO_SHARED_MEMORY */
629 /* Get the available video modes */
630 if(X11_GetVideoModes(this) < 0) {
631 XCloseDisplay(GFX_Display);
633 XCloseDisplay(SDL_Display);
638 /* Determine the current screen size */
639 this->info.current_w = DisplayWidth(SDL_Display, SDL_Screen);
640 this->info.current_h = DisplayHeight(SDL_Display, SDL_Screen);
642 /* Determine the default screen depth:
643 Use the default visual (or at least one with the same depth) */
644 SDL_DisplayColormap = DefaultColormap(SDL_Display, SDL_Screen);
645 for(i = 0; i < this->hidden->nvisuals; i++)
646 if(this->hidden->visuals[i].depth == DefaultDepth(SDL_Display,
649 if(i == this->hidden->nvisuals) {
650 /* default visual was useless, take the deepest one instead */
653 SDL_Visual = this->hidden->visuals[i].visual;
654 if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
655 SDL_XColorMap = SDL_DisplayColormap;
657 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
658 SDL_Visual, AllocNone);
660 this->hidden->depth = this->hidden->visuals[i].depth;
661 vformat->BitsPerPixel = this->hidden->visuals[i].bpp;
662 if ( vformat->BitsPerPixel > 8 ) {
663 vformat->Rmask = SDL_Visual->red_mask;
664 vformat->Gmask = SDL_Visual->green_mask;
665 vformat->Bmask = SDL_Visual->blue_mask;
667 if ( this->hidden->depth == 32 ) {
668 vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
670 X11_SaveVidModeGamma(this);
672 /* Allow environment override of screensaver disable. */
673 env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
675 allow_screensaver = SDL_atoi(env);
677 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
678 allow_screensaver = 0;
680 allow_screensaver = 1;
684 /* See if we have been passed a window to use */
685 SDL_windowid = SDL_getenv("SDL_WINDOWID");
687 /* Create the fullscreen and managed windows */
688 create_aux_windows(this);
690 /* Create the blank cursor */
691 SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask,
692 BLANK_CWIDTH, BLANK_CHEIGHT,
693 BLANK_CHOTX, BLANK_CHOTY);
695 /* Fill in some window manager capabilities */
696 this->info.wm_available = 1;
703 static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
705 /* Clean up OpenGL */
707 screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
709 X11_GL_Shutdown(this);
711 if ( ! SDL_windowid ) {
712 /* Hide the managed window */
714 XUnmapWindow(SDL_Display, WMwindow);
716 if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
717 screen->flags &= ~SDL_FULLSCREEN;
718 X11_LeaveFullScreen(this);
721 /* Destroy the output window */
723 XDestroyWindow(SDL_Display, SDL_Window);
726 /* Free the colormap entries */
730 numcolors = SDL_Visual->map_entries;
731 for ( pixel=0; pixel<numcolors; ++pixel ) {
732 while ( SDL_XPixels[pixel] > 0 ) {
733 XFreeColors(GFX_Display,
734 SDL_DisplayColormap,&pixel,1,0);
735 --SDL_XPixels[pixel];
738 SDL_free(SDL_XPixels);
742 /* Free the graphics context */
744 XFreeGC(SDL_Display, SDL_GC);
750 static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
752 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
753 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
755 if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
758 if ( SDL_strcmp(window, "center") == 0 ) {
763 *x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
764 *y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
770 static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
774 hints = XAllocSizeHints();
776 if (!(flags & SDL_RESIZABLE)) {
777 hints->min_width = hints->max_width = w;
778 hints->min_height = hints->max_height = h;
779 hints->flags = PMaxSize | PMinSize;
781 if ( flags & SDL_FULLSCREEN ) {
784 hints->flags |= USPosition;
786 /* Center it, if desired */
787 if ( X11_WindowPosition(this, &hints->x, &hints->y, w, h) ) {
788 hints->flags |= USPosition;
789 XMoveWindow(SDL_Display, WMwindow, hints->x, hints->y);
791 /* Flush the resize event so we don't catch it later */
792 XSync(SDL_Display, True);
794 XSetWMNormalHints(SDL_Display, WMwindow, hints);
798 /* Respect the window caption style */
799 if ( flags & SDL_NOFRAME ) {
803 /* We haven't modified the window manager hints yet */
806 /* First try to set MWM hints */
807 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
808 if ( WM_HINTS != None ) {
809 /* Hints used by Motif compliant window managers */
812 unsigned long functions;
813 unsigned long decorations;
815 unsigned long status;
816 } MWMHints = { (1L << 1), 0, 0, 0, 0 };
818 XChangeProperty(SDL_Display, WMwindow,
819 WM_HINTS, WM_HINTS, 32,
821 (unsigned char *)&MWMHints,
822 sizeof(MWMHints)/sizeof(long));
825 /* Now try to set KWM hints */
826 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
827 if ( WM_HINTS != None ) {
830 XChangeProperty(SDL_Display, WMwindow,
831 WM_HINTS, WM_HINTS, 32,
833 (unsigned char *)&KWMHints,
834 sizeof(KWMHints)/sizeof(long));
837 /* Now try to set GNOME hints */
838 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
839 if ( WM_HINTS != None ) {
842 XChangeProperty(SDL_Display, WMwindow,
843 WM_HINTS, WM_HINTS, 32,
845 (unsigned char *)&GNOMEHints,
846 sizeof(GNOMEHints)/sizeof(long));
849 /* Finally set the transient hints if necessary */
851 XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
857 /* We haven't modified the window manager hints yet */
860 /* First try to unset MWM hints */
861 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
862 if ( WM_HINTS != None ) {
863 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
866 /* Now try to unset KWM hints */
867 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
868 if ( WM_HINTS != None ) {
869 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
872 /* Now try to unset GNOME hints */
873 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
874 if ( WM_HINTS != None ) {
875 XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
878 /* Finally unset the transient hints if necessary */
880 /* NOTE: Does this work? */
881 XSetTransientForHint(SDL_Display, WMwindow, None);
886 static int X11_CreateWindow(_THIS, SDL_Surface *screen,
887 int w, int h, int bpp, Uint32 flags)
894 /* If a window is already present, destroy it and start fresh */
896 X11_DestroyWindow(this, screen);
897 switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
900 /* See if we have been given a window id */
901 if ( SDL_windowid ) {
902 SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
907 /* find out which visual we are going to use */
908 if ( flags & SDL_OPENGL ) {
911 vi = X11_GL_GetVisual(this);
917 } else if ( SDL_windowid ) {
920 XGetWindowAttributes(SDL_Display, SDL_Window, &a);
924 for ( i = 0; i < this->hidden->nvisuals; i++ ) {
925 if ( this->hidden->visuals[i].bpp == bpp )
928 if ( i == this->hidden->nvisuals ) {
929 SDL_SetError("No matching visual for requested depth");
930 return -1; /* should never happen */
932 vis = this->hidden->visuals[i].visual;
933 depth = this->hidden->visuals[i].depth;
936 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);
938 vis_change = (vis != SDL_Visual);
940 this->hidden->depth = depth;
942 /* Allocate the new pixel format for this video mode */
943 if ( this->hidden->depth == 32 ) {
944 Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask));
948 if ( ! SDL_ReallocFormat(screen, bpp,
949 vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
953 /* Create the appropriate colormap */
954 if ( SDL_XColorMap != SDL_DisplayColormap ) {
955 XFreeColormap(SDL_Display, SDL_XColorMap);
957 if ( SDL_Visual->class == PseudoColor ) {
960 /* Allocate the pixel flags */
961 ncolors = SDL_Visual->map_entries;
962 SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
963 if(SDL_XPixels == NULL) {
967 SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
969 /* always allocate a private colormap on non-default visuals */
970 if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
971 flags |= SDL_HWPALETTE;
973 if ( flags & SDL_HWPALETTE ) {
974 screen->flags |= SDL_HWPALETTE;
975 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
976 SDL_Visual, AllocAll);
978 SDL_XColorMap = SDL_DisplayColormap;
980 } else if ( SDL_Visual->class == DirectColor ) {
982 /* Create a colormap which we can manipulate for gamma */
983 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
984 SDL_Visual, AllocAll);
985 XSync(SDL_Display, False);
987 /* Initialize the colormap to the identity mapping */
988 SDL_GetGammaRamp(0, 0, 0);
989 this->screen = screen;
990 X11_SetGammaRamp(this, this->gamma);
993 /* Create a read-only colormap for our window */
994 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
995 SDL_Visual, AllocNone);
998 /* Recreate the auxiliary windows, if needed (required for GL) */
1000 create_aux_windows(this);
1002 if(screen->flags & SDL_HWPALETTE) {
1003 /* Since the full-screen window might have got a nonzero background
1004 colour (0 is white on some displays), we should reset the
1005 background to 0 here since that is what the user expects
1006 with a private colormap */
1007 XSetWindowBackground(SDL_Display, FSwindow, 0);
1008 XClearWindow(SDL_Display, FSwindow);
1011 /* resize the (possibly new) window manager window */
1012 if( !SDL_windowid ) {
1013 X11_SetSizeHints(this, w, h, flags);
1016 XResizeWindow(SDL_Display, WMwindow, w, h);
1019 /* Create (or use) the X11 display window */
1020 if ( !SDL_windowid ) {
1021 if ( flags & SDL_OPENGL ) {
1022 if ( X11_GL_CreateWindow(this, w, h) < 0 ) {
1026 XSetWindowAttributes swa;
1028 swa.background_pixel = 0;
1029 swa.border_pixel = 0;
1030 swa.colormap = SDL_XColorMap;
1031 SDL_Window = XCreateWindow(SDL_Display, WMwindow,
1032 0, 0, w, h, 0, depth,
1033 InputOutput, SDL_Visual,
1034 CWBackPixel | CWBorderPixel
1035 | CWColormap, &swa);
1037 /* Only manage our input if we own the window */
1038 XSelectInput(SDL_Display, SDL_Window,
1039 ( EnterWindowMask | LeaveWindowMask
1040 | ButtonPressMask | ButtonReleaseMask
1041 | PointerMotionMask | ExposureMask ));
1043 /* Create the graphics context here, once we have a window */
1044 if ( flags & SDL_OPENGL ) {
1045 if ( X11_GL_CreateContext(this) < 0 ) {
1048 screen->flags |= SDL_OPENGL;
1053 gcv.graphics_exposures = False;
1054 SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1055 GCGraphicsExposures, &gcv);
1057 SDL_SetError("Couldn't create graphics context");
1062 /* Set our colormaps when not setting a GL mode */
1063 if ( ! (flags & SDL_OPENGL) ) {
1064 XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
1065 if( !SDL_windowid ) {
1066 XSetWindowColormap(SDL_Display, FSwindow, SDL_XColorMap);
1067 XSetWindowColormap(SDL_Display, WMwindow, SDL_XColorMap);
1071 #if 0 /* This is an experiment - are the graphics faster now? - nope. */
1072 if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1074 /* Cache the window in the server, when possible */
1077 XSetWindowAttributes a;
1079 xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen);
1080 a.backing_store = DoesBackingStore(xscreen);
1081 if ( a.backing_store != NotUseful ) {
1082 XChangeWindowAttributes(SDL_Display, SDL_Window,
1083 CWBackingStore, &a);
1087 /* Map them both and go fullscreen, if requested */
1088 if ( ! SDL_windowid ) {
1089 XMapWindow(SDL_Display, SDL_Window);
1090 XMapWindow(SDL_Display, WMwindow);
1091 X11_WaitMapped(this, WMwindow);
1092 if ( flags & SDL_FULLSCREEN ) {
1093 screen->flags |= SDL_FULLSCREEN;
1094 X11_EnterFullScreen(this);
1096 screen->flags &= ~SDL_FULLSCREEN;
1103 static int X11_ResizeWindow(_THIS,
1104 SDL_Surface *screen, int w, int h, Uint32 flags)
1106 if ( ! SDL_windowid ) {
1107 /* Resize the window manager window */
1108 X11_SetSizeHints(this, w, h, flags);
1111 XResizeWindow(SDL_Display, WMwindow, w, h);
1113 /* Resize the fullscreen and display windows */
1114 if ( flags & SDL_FULLSCREEN ) {
1115 if ( screen->flags & SDL_FULLSCREEN ) {
1116 X11_ResizeFullScreen(this);
1118 screen->flags |= SDL_FULLSCREEN;
1119 X11_EnterFullScreen(this);
1122 if ( screen->flags & SDL_FULLSCREEN ) {
1123 screen->flags &= ~SDL_FULLSCREEN;
1124 X11_LeaveFullScreen(this);
1127 XResizeWindow(SDL_Display, SDL_Window, w, h);
1132 SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1133 int width, int height, int bpp, Uint32 flags)
1137 /* Lock the event thread, in multi-threading environments */
1138 SDL_Lock_EventThread();
1140 /* Check the combination of flags we were passed */
1141 if ( flags & SDL_FULLSCREEN ) {
1142 /* Clear fullscreen flag if not supported */
1143 if ( SDL_windowid ) {
1144 flags &= ~SDL_FULLSCREEN;
1148 /* Flush any delayed updates */
1149 XSync(GFX_Display, False);
1151 /* Set up the X11 window */
1152 saved_flags = current->flags;
1153 if ( (SDL_Window) && ((saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL))
1154 && (bpp == current->format->BitsPerPixel)
1155 && ((saved_flags&SDL_NOFRAME) == (flags&SDL_NOFRAME)) ) {
1156 if (X11_ResizeWindow(this, current, width, height, flags) < 0) {
1161 if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1167 /* Update the internal keyboard state */
1168 X11_SetKeyboardState(SDL_Display, NULL);
1170 /* When the window is first mapped, ignore non-modifier keys */
1171 if ( !current->w && !current->h ) {
1172 Uint8 *keys = SDL_GetKeyState(NULL);
1174 for ( i = 0; i < SDLK_LAST; ++i ) {
1189 keys[i] = SDL_RELEASED;
1195 /* Set up the new mode framebuffer */
1196 if ( ((current->w != width) || (current->h != height)) ||
1197 ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1199 current->h = height;
1200 current->pitch = SDL_CalculatePitch(current);
1201 if (X11_ResizeImage(this, current, flags) < 0) {
1207 /* Clear these flags and set them only if they are in the new set. */
1208 current->flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
1209 current->flags |= (flags&(SDL_RESIZABLE|SDL_NOFRAME));
1212 /* Release the event thread */
1213 XSync(SDL_Display, False);
1214 SDL_Unlock_EventThread();
1220 static int X11_ToggleFullScreen(_THIS, int on)
1222 Uint32 event_thread;
1224 /* Don't switch if we don't own the window */
1225 if ( SDL_windowid ) {
1229 /* Don't lock if we are the event thread */
1230 event_thread = SDL_EventThreadID();
1231 if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1234 if ( event_thread ) {
1235 SDL_Lock_EventThread();
1238 this->screen->flags |= SDL_FULLSCREEN;
1239 X11_EnterFullScreen(this);
1241 this->screen->flags &= ~SDL_FULLSCREEN;
1242 X11_LeaveFullScreen(this);
1244 X11_RefreshDisplay(this);
1245 if ( event_thread ) {
1246 SDL_Unlock_EventThread();
1248 SDL_ResetKeyboard();
1252 /* Update the current mouse state and position */
1253 static void X11_UpdateMouse(_THIS)
1260 /* Lock the event thread, in multi-threading environments */
1261 SDL_Lock_EventThread();
1262 if ( XQueryPointer(SDL_Display, SDL_Window, &u1, ¤t_win,
1263 &u2, &u2, &x, &y, &mask) ) {
1264 if ( (x >= 0) && (x < SDL_VideoSurface->w) &&
1265 (y >= 0) && (y < SDL_VideoSurface->h) ) {
1266 SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
1267 SDL_PrivateMouseMotion(0, 0, x, y);
1269 SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1272 SDL_Unlock_EventThread();
1275 /* simple colour distance metric. Supposed to be better than a plain
1276 Euclidian distance anyway. */
1277 #define COLOUR_FACTOR 3
1278 #define LIGHT_FACTOR 1
1279 #define COLOUR_DIST(r1, g1, b1, r2, g2, b2) \
1280 (COLOUR_FACTOR * (abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)) \
1281 + LIGHT_FACTOR * abs(r1 + g1 + b1 - (r2 + g2 + b2)))
1283 static void allocate_nearest(_THIS, SDL_Color *colors,
1284 SDL_Color *want, int nwant)
1287 * There is no way to know which ones to choose from, so we retrieve
1288 * the entire colormap and try the nearest possible, until we find one
1293 for(i = 0; i < 256; i++)
1296 * XQueryColors sets the flags in the XColor struct, so we use
1297 * that to keep track of which colours are available
1299 XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1301 for(i = 0; i < nwant; i++) {
1305 int mindist = 0x7fffffff;
1309 for(j = 0; j < 256; j++) {
1312 continue; /* unavailable colour cell */
1313 rj = all[j].red >> 8;
1314 gj = all[j].green >> 8;
1315 bj = all[j].blue >> 8;
1316 d2 = COLOUR_DIST(ri, gi, bi, rj, gj, bj);
1322 if(SDL_XPixels[best])
1323 continue; /* already allocated, waste no more time */
1325 if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1327 colors[c->pixel].r = c->red >> 8;
1328 colors[c->pixel].g = c->green >> 8;
1329 colors[c->pixel].b = c->blue >> 8;
1330 ++SDL_XPixels[c->pixel];
1333 * The colour couldn't be allocated, probably being
1334 * owned as a r/w cell by another client. Flag it as
1335 * unavailable and try again. The termination of the
1336 * loop is guaranteed since at least black and white
1345 int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1349 /* Check to make sure we have a colormap allocated */
1350 if ( SDL_XPixels == NULL ) {
1353 if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1354 /* private writable colormap: just set the colours we need */
1357 xcmap = SDL_stack_alloc(XColor, ncolors);
1360 for ( i=0; i<ncolors; ++i ) {
1361 xcmap[i].pixel = i + firstcolor;
1362 xcmap[i].red = (colors[i].r<<8)|colors[i].r;
1363 xcmap[i].green = (colors[i].g<<8)|colors[i].g;
1364 xcmap[i].blue = (colors[i].b<<8)|colors[i].b;
1365 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1367 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1368 XSync(GFX_Display, False);
1369 SDL_stack_free(xcmap);
1372 * Shared colormap: We only allocate read-only cells, which
1373 * increases the likelyhood of colour sharing with other
1374 * clients. The pixel values will almost certainly be
1375 * different from the requested ones, so the user has to
1376 * walk the colormap and see which index got what colour.
1378 * We can work directly with the logical palette since it
1379 * has already been set when we get here.
1381 SDL_Color *want, *reject;
1382 unsigned long *freelist;
1385 int nc = this->screen->format->palette->ncolors;
1386 colors = this->screen->format->palette->colors;
1387 freelist = SDL_stack_alloc(unsigned long, nc);
1388 /* make sure multiple allocations of the same cell are freed */
1389 for(i = 0; i < ncolors; i++) {
1390 int pixel = firstcolor + i;
1391 while(SDL_XPixels[pixel]) {
1392 freelist[nfree++] = pixel;
1393 --SDL_XPixels[pixel];
1396 XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1397 SDL_stack_free(freelist);
1399 want = SDL_stack_alloc(SDL_Color, ncolors);
1400 reject = SDL_stack_alloc(SDL_Color, ncolors);
1401 SDL_memcpy(want, colors + firstcolor, ncolors * sizeof(SDL_Color));
1402 /* make sure the user isn't fooled by her own wishes
1403 (black is safe, always available in the default colormap) */
1404 SDL_memset(colors + firstcolor, 0, ncolors * sizeof(SDL_Color));
1406 /* now try to allocate the colours */
1407 for(i = 0; i < ncolors; i++) {
1409 col.red = want[i].r << 8;
1410 col.green = want[i].g << 8;
1411 col.blue = want[i].b << 8;
1412 col.flags = DoRed | DoGreen | DoBlue;
1413 if(XAllocColor(GFX_Display, SDL_XColorMap, &col)) {
1414 /* We got the colour, or at least the nearest
1415 the hardware could get. */
1416 colors[col.pixel].r = col.red >> 8;
1417 colors[col.pixel].g = col.green >> 8;
1418 colors[col.pixel].b = col.blue >> 8;
1419 ++SDL_XPixels[col.pixel];
1422 * no more free cells, add it to the list
1423 * of rejected colours
1425 reject[nrej++] = want[i];
1429 allocate_nearest(this, colors, reject, nrej);
1430 SDL_stack_free(reject);
1431 SDL_stack_free(want);
1436 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1441 /* See if actually setting the gamma is supported */
1442 if ( SDL_Visual->class != DirectColor ) {
1443 SDL_SetError("Gamma correction not supported on this visual");
1447 /* Calculate the appropriate palette for the given gamma ramp */
1448 ncolors = SDL_Visual->map_entries;
1449 for ( i=0; i<ncolors; ++i ) {
1450 Uint8 c = (256 * i / ncolors);
1451 xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
1452 xcmap[i].red = ramp[0*256+c];
1453 xcmap[i].green = ramp[1*256+c];
1454 xcmap[i].blue = ramp[2*256+c];
1455 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1457 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1458 XSync(GFX_Display, False);
1462 /* Note: If we are terminated, this could be called in the middle of
1463 another SDL video routine -- notably UpdateRects.
1465 void X11_VideoQuit(_THIS)
1467 /* Shutdown everything that's still up */
1468 /* The event thread should be done, so we can touch SDL_Display */
1469 if ( SDL_Display != NULL ) {
1470 /* Flush any delayed updates */
1471 XSync(GFX_Display, False);
1473 /* Close the connection with the IM server */
1474 #ifdef X_HAVE_UTF8_STRING
1475 if (SDL_IC != NULL) {
1476 XUnsetICFocus(SDL_IC);
1480 if (SDL_IM != NULL) {
1486 /* Start shutting down the windows */
1487 X11_DestroyImage(this, this->screen);
1488 X11_DestroyWindow(this, this->screen);
1489 X11_FreeVideoModes(this);
1490 if ( SDL_XColorMap != SDL_DisplayColormap ) {
1491 XFreeColormap(SDL_Display, SDL_XColorMap);
1493 if ( SDL_iconcolors ) {
1494 unsigned long pixel;
1495 Colormap dcmap = DefaultColormap(SDL_Display,
1497 for(pixel = 0; pixel < 256; ++pixel) {
1498 while(SDL_iconcolors[pixel] > 0) {
1499 XFreeColors(GFX_Display,
1500 dcmap, &pixel, 1, 0);
1501 --SDL_iconcolors[pixel];
1504 SDL_free(SDL_iconcolors);
1505 SDL_iconcolors = NULL;
1508 /* Restore gamma settings if they've changed */
1509 if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1510 X11_SwapVidModeGamma(this);
1513 /* Free that blank cursor */
1514 if ( SDL_BlankCursor != NULL ) {
1515 this->FreeWMCursor(this, SDL_BlankCursor);
1516 SDL_BlankCursor = NULL;
1519 /* Close the X11 graphics connection */
1520 if ( GFX_Display != NULL ) {
1521 XCloseDisplay(GFX_Display);
1525 /* Close the X11 display connection */
1526 XCloseDisplay(SDL_Display);
1529 /* Reset the X11 error handlers */
1530 if ( XIO_handler ) {
1531 XSetIOErrorHandler(XIO_handler);
1534 XSetErrorHandler(X_handler);
1537 /* Unload GL library after X11 shuts down */
1538 X11_GL_UnloadLibrary(this);
1540 if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1541 /* Direct screen access, no memory buffer */
1542 this->screen->pixels = NULL;
1545 #if SDL_VIDEO_DRIVER_X11_XME