SDL-1.2.14
[sdl_omap.git] / src / video / x11 / SDL_x11video.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /* 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().
29 */
30
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #ifdef MTRR_SUPPORT
34 #include <asm/mtrr.h>
35 #include <sys/fcntl.h>
36 #endif
37
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"
56
57 #ifdef X_HAVE_UTF8_STRING
58 #include <locale.h>
59 #endif
60
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,
67                          SDL_Color *colors);
68 static int X11_SetGammaRamp(_THIS, Uint16 *ramp);
69 static void X11_VideoQuit(_THIS);
70
71
72 /* X11 driver bootstrap functions */
73
74 static int X11_Available(void)
75 {
76         Display *display = NULL;
77         if ( SDL_X11_LoadSymbols() ) {
78                 display = XOpenDisplay(NULL);
79                 if ( display != NULL ) {
80                         XCloseDisplay(display);
81                 }
82                 SDL_X11_UnloadSymbols();
83         }
84         return(display != NULL);
85 }
86
87 static void X11_DeleteDevice(SDL_VideoDevice *device)
88 {
89         if ( device ) {
90                 if ( device->hidden ) {
91                         SDL_free(device->hidden);
92                 }
93                 if ( device->gl_data ) {
94                         SDL_free(device->gl_data);
95                 }
96                 SDL_free(device);
97                 SDL_X11_UnloadSymbols();
98         }
99 }
100
101 static SDL_VideoDevice *X11_CreateDevice(int devindex)
102 {
103         SDL_VideoDevice *device = NULL;
104
105         if ( SDL_X11_LoadSymbols() ) {
106                 /* Initialize all variables that we clean on shutdown */
107                 device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
108                 if ( device ) {
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));
114                 }
115                 if ( (device == NULL) || (device->hidden == NULL) ||
116                                          (device->gl_data == NULL) ) {
117                         SDL_OutOfMemory();
118                         X11_DeleteDevice(device); /* calls SDL_X11_UnloadSymbols(). */
119                         return(0);
120                 }
121                 SDL_memset(device->hidden, 0, (sizeof *device->hidden));
122                 SDL_memset(device->gl_data, 0, (sizeof *device->gl_data));
123
124 #if SDL_VIDEO_OPENGL_GLX
125                 device->gl_data->swap_interval = -1;
126 #endif
127
128                 /* Set the driver flags */
129                 device->handles_any_size = 1;
130
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;
139 #endif
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;
162 #endif
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;
175
176                 device->free = X11_DeleteDevice;
177         }
178
179         return device;
180 }
181
182 VideoBootStrap X11_bootstrap = {
183         "x11", "X Window System",
184         X11_Available, X11_CreateDevice
185 };
186
187 /* Normal X11 error handler routine */
188 static int (*X_handler)(Display *, XErrorEvent *) = NULL;
189 static int x_errhandler(Display *d, XErrorEvent *e)
190 {
191 #if SDL_VIDEO_DRIVER_X11_VIDMODE
192         extern int vm_error;
193 #endif
194 #if SDL_VIDEO_DRIVER_X11_DGAMOUSE
195         extern int dga_error;
196 #endif
197
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) ?
203          */
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)))) ) {
208 #ifdef X11_DEBUG
209 { char errmsg[1024];
210   XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
211 printf("VidMode error: %s\n", errmsg);
212 }
213 #endif
214                 return(0);
215         }
216 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
217
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))) ) {
223 #ifdef X11_DEBUG
224 { char errmsg[1024];
225   XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
226 printf("DGA error: %s\n", errmsg);
227 }
228 #endif
229                 return(0);
230         }
231 #endif /* SDL_VIDEO_DRIVER_X11_DGAMOUSE */
232
233         return(X_handler(d,e));
234 }
235
236 /* X11 I/O error handler routine */
237 static int (*XIO_handler)(Display *) = NULL;
238 static int xio_errhandler(Display *d)
239 {
240         /* Ack!  Lost X11 connection! */
241
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;
245         }
246         current_video->hidden->X11_Display = NULL;
247
248         /* Continue with the standard X11 error handler */
249         return(XIO_handler(d));
250 }
251
252 static int (*Xext_handler)(Display *, _Xconst char *, _Xconst char *) = NULL;
253 static int xext_errhandler(Display *d, _Xconst char *ext, _Xconst char *reason)
254 {
255 #ifdef X11_DEBUG
256         printf("Xext error inside SDL (may be harmless):\n");
257         printf("  Extension \"%s\" %s on display \"%s\".\n",
258                ext, reason, XDisplayString(d));
259 #endif
260
261         if (SDL_strcmp(reason, "missing") == 0) {
262                 /*
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.
266                  */
267                 return 0;
268         }
269
270         /* Everything else goes to the default handler... */
271         return Xext_handler(d, ext, reason);
272 }
273
274 /* Find out what class name we should use */
275 static char *get_classname(char *classname, int maxlen)
276 {
277         char *spot;
278 #if defined(__LINUX__) || defined(__FREEBSD__)
279         char procfile[1024];
280         char linkfile[1024];
281         int linksize;
282 #endif
283
284         /* First allow environment variable override */
285         spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
286         if ( spot ) {
287                 SDL_strlcpy(classname, spot, maxlen);
288                 return classname;
289         }
290
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());
297 #else
298 #error Where can we find the executable name?
299 #endif
300         linksize = readlink(procfile, linkfile, sizeof(linkfile)-1);
301         if ( linksize > 0 ) {
302                 linkfile[linksize] = '\0';
303                 spot = SDL_strrchr(linkfile, '/');
304                 if ( spot ) {
305                         SDL_strlcpy(classname, spot+1, maxlen);
306                 } else {
307                         SDL_strlcpy(classname, linkfile, maxlen);
308                 }
309                 return classname;
310         }
311 #endif /* __LINUX__ */
312
313         /* Finally use the default we've used forever */
314         SDL_strlcpy(classname, "SDL_App", maxlen);
315         return classname;
316 }
317
318 /* Create auxiliary (toplevel) windows with the current visual */
319 static void create_aux_windows(_THIS)
320 {
321     int x = 0, y = 0;
322     char classname[1024];
323     XSetWindowAttributes xattr;
324     XWMHints *hints;
325     unsigned long app_event_mask;
326     int def_vis = (SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen));
327
328     /* Look up some useful Atoms */
329     WM_DELETE_WINDOW = XInternAtom(SDL_Display, "WM_DELETE_WINDOW", False);
330
331     /* Don't create any extra windows if we are being managed */
332     if ( SDL_windowid ) {
333         FSwindow = 0;
334         WMwindow = SDL_strtol(SDL_windowid, NULL, 0);
335         return;
336     }
337
338     if(FSwindow)
339         XDestroyWindow(SDL_Display, FSwindow);
340
341 #if SDL_VIDEO_DRIVER_X11_XINERAMA
342     if ( use_xinerama ) {
343         x = xinerama_info.x_org;
344         y = xinerama_info.y_org;
345     }
346 #endif
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;
351
352     FSwindow = XCreateWindow(SDL_Display, SDL_Root,
353                              x, y, 32, 32, 0,
354                              this->hidden->depth, InputOutput, SDL_Visual,
355                              CWOverrideRedirect | CWBackPixel | CWBorderPixel
356                              | CWColormap,
357                              &xattr);
358
359     XSelectInput(SDL_Display, FSwindow, StructureNotifyMask);
360
361     /* Tell KDE to keep the fullscreen window on top */
362     {
363         XEvent ev;
364         long mask;
365
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);
376     }
377
378     hints = NULL;
379     if(WMwindow) {
380         /* All window attributes must survive the recreation */
381         hints = XGetWMHints(SDL_Display, WMwindow);
382         XDestroyWindow(SDL_Display, WMwindow);
383     }
384
385     /* Create the window for windowed management */
386     /* (reusing the xattr structure above) */
387     WMwindow = XCreateWindow(SDL_Display, SDL_Root,
388                              x, y, 32, 32, 0,
389                              this->hidden->depth, InputOutput, SDL_Visual,
390                              CWBackPixel | CWBorderPixel | CWColormap,
391                              &xattr);
392
393     /* Set the input hints so we get keyboard input */
394     if(!hints) {
395         hints = XAllocWMHints();
396         hints->input = True;
397         hints->flags = InputHint;
398     }
399     XSetWMHints(SDL_Display, WMwindow, hints);
400     XFree(hints);
401     X11_SetCaptionNoLock(this, this->wm_title, this->wm_icon);
402
403     app_event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
404         | PropertyChangeMask | StructureNotifyMask | KeymapStateMask;
405     XSelectInput(SDL_Display, WMwindow, app_event_mask);
406
407     /* Set the class hints so we can get an icon (AfterStep) */
408     get_classname(classname, sizeof(classname));
409     {
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);
416             XFree(classhints);
417         }
418     }
419
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.  */
424
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");
432                         if (SDL_IC) {
433                                 XUnsetICFocus(SDL_IC);
434                                 XDestroyIC(SDL_IC);
435                                 SDL_IC = NULL;
436                         }
437                         XCloseIM(SDL_IM);
438                         SDL_IM = NULL;
439                 }
440
441                 /* Open an input method.  */
442                 if (SDL_IM == NULL) {
443                         char *old_locale = NULL, *old_modifiers = NULL;
444                         const char *p;
445                         size_t n;
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
458                            job... */
459
460                         /* Save the current (application program's)
461                            locale settings.  */
462                         p = setlocale(LC_ALL, NULL);
463                         if ( p ) {
464                                 n = SDL_strlen(p)+1;
465                                 old_locale = SDL_stack_alloc(char, n);
466                                 if ( old_locale ) {
467                                         SDL_strlcpy(old_locale, p, n);
468                                 }
469                         }
470                         p = XSetLocaleModifiers(NULL);
471                         if ( p ) {
472                                 n = SDL_strlen(p)+1;
473                                 old_modifiers = SDL_stack_alloc(char, n);
474                                 if ( old_modifiers ) {
475                                         SDL_strlcpy(old_modifiers, p, n);
476                                 }
477                         }
478
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);
484
485                         /* Restore the application's locale settings
486                            so that we don't break the application's
487                            expected behaviour.  */
488                         if ( old_locale ) {
489                                 /* We need to restore the C library
490                                    locale first, since the
491                                    interpretation of the X modifier
492                                    may depend on it.  */
493                                 setlocale(LC_ALL, old_locale);
494                                 SDL_stack_free(old_locale);
495                         }
496                         if ( old_modifiers ) {
497                                 XSetLocaleModifiers(old_modifiers);
498                                 SDL_stack_free(old_modifiers);
499                         }
500                 }
501
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");
505                 } else {
506                         if (SDL_IC != NULL) {
507                                 /* Discard the old IC before creating new one.  */
508                             XUnsetICFocus(SDL_IC);
509                             XDestroyIC(SDL_IC);
510                         }
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,
523                                         NULL);
524
525                         if (SDL_IC == NULL) {
526                                 SDL_SetError("no input context could be created");
527                                 XCloseIM(SDL_IM);
528                                 SDL_IM = NULL;
529                         } else {
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);
539                                 if (ret != NULL) {
540                                         XUnsetICFocus(SDL_IC);
541                                         XDestroyIC(SDL_IC);
542                                         SDL_IC = NULL;
543                                         SDL_SetError("no input context could be created");
544                                         XCloseIM(SDL_IM);
545                                         SDL_IM = NULL;
546                                 } else {
547                                         XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
548                                         XSetICFocus(SDL_IC);
549                                 }
550                         }
551                 }
552         }
553         #endif
554
555         /* Allow the window to be deleted by the window manager */
556         XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
557 }
558
559 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
560 {
561         const char *env;
562         char *display;
563         int i;
564
565         /* Open the X11 display */
566         display = NULL;         /* Get it from DISPLAY environment variable */
567
568         if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
569              (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
570                 local_X11 = 1;
571         } else {
572                 local_X11 = 0;
573         }
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!
579          *
580          * It succeeds if retrying 1 second later
581          * or if running xhost +localhost on shell.
582          *
583          */
584         if ( SDL_Display == NULL ) {
585                 SDL_Delay(1000);
586                 SDL_Display = XOpenDisplay(display);
587         }
588 #endif
589         if ( SDL_Display == NULL ) {
590                 SDL_SetError("Couldn't open X11 display");
591                 return(-1);
592         }
593 #ifdef X11_DEBUG
594         XSynchronize(SDL_Display, True);
595 #endif
596
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.
600          */
601         GFX_Display = XOpenDisplay(display);
602         if ( GFX_Display == NULL ) {
603                 XCloseDisplay(SDL_Display);
604                 SDL_Display = NULL;
605                 SDL_SetError("Couldn't open X11 display");
606                 return(-1);
607         }
608
609         /* Set the normal X error handler */
610         X_handler = XSetErrorHandler(x_errhandler);
611
612         /* Set the error handler if we lose the X display */
613         XIO_handler = XSetIOErrorHandler(xio_errhandler);
614
615         /* Set the X extension error handler */
616         Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
617
618         /* use default screen (from $DISPLAY) */
619         SDL_Screen = DefaultScreen(SDL_Display);
620
621 #ifndef NO_SHARED_MEMORY
622         /* Check for MIT shared memory extension */
623         use_mitshm = 0;
624         if ( local_X11 ) {
625                 use_mitshm = XShmQueryExtension(SDL_Display);
626         }
627 #endif /* NO_SHARED_MEMORY */
628
629         /* Get the available video modes */
630         if(X11_GetVideoModes(this) < 0) {
631                 XCloseDisplay(GFX_Display);
632                 GFX_Display = NULL;
633                 XCloseDisplay(SDL_Display);
634                 SDL_Display = NULL;
635             return -1;
636         }
637
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);
641
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,
647                                                               SDL_Screen))
648                 break;
649         if(i == this->hidden->nvisuals) {
650             /* default visual was useless, take the deepest one instead */
651             i = 0;
652         }
653         SDL_Visual = this->hidden->visuals[i].visual;
654         if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
655             SDL_XColorMap = SDL_DisplayColormap;
656         } else {
657             SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
658                                             SDL_Visual, AllocNone);
659         }
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;
666         }
667         if ( this->hidden->depth == 32 ) {
668                 vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
669         }
670         X11_SaveVidModeGamma(this);
671
672         /* Allow environment override of screensaver disable. */
673         env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
674         if ( env ) {
675                 allow_screensaver = SDL_atoi(env);
676         } else {
677 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
678                 allow_screensaver = 0;
679 #else
680                 allow_screensaver = 1;
681 #endif
682         }
683
684         /* See if we have been passed a window to use */
685         SDL_windowid = SDL_getenv("SDL_WINDOWID");
686
687         /* Create the fullscreen and managed windows */
688         create_aux_windows(this);
689
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);
694
695         /* Fill in some window manager capabilities */
696         this->info.wm_available = 1;
697
698         /* We're done! */
699         XFlush(SDL_Display);
700         return(0);
701 }
702
703 static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
704 {
705         /* Clean up OpenGL */
706         if ( screen ) {
707                 screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
708         }
709         X11_GL_Shutdown(this);
710
711         if ( ! SDL_windowid ) {
712                 /* Hide the managed window */
713                 if ( WMwindow ) {
714                         XUnmapWindow(SDL_Display, WMwindow);
715                 }
716                 if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
717                         screen->flags &= ~SDL_FULLSCREEN;
718                         X11_LeaveFullScreen(this);
719                 }
720
721                 /* Destroy the output window */
722                 if ( SDL_Window ) {
723                         XDestroyWindow(SDL_Display, SDL_Window);
724                 }
725
726                 /* Free the colormap entries */
727                 if ( SDL_XPixels ) {
728                         int numcolors;
729                         unsigned long pixel;
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];
736                                 }
737                         }
738                         SDL_free(SDL_XPixels);
739                         SDL_XPixels = NULL;
740                 } 
741
742                 /* Free the graphics context */
743                 if ( SDL_GC ) {
744                         XFreeGC(SDL_Display, SDL_GC);
745                         SDL_GC = 0;
746                 }
747         }
748 }
749
750 static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
751 {
752         const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
753         const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
754         if ( window ) {
755                 if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
756                         return SDL_TRUE;
757                 }
758                 if ( SDL_strcmp(window, "center") == 0 ) {
759                         center = window;
760                 }
761         }
762         if ( center ) {
763                 *x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
764                 *y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
765                 return SDL_TRUE;
766         }
767         return SDL_FALSE;
768 }
769
770 static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
771 {
772         XSizeHints *hints;
773
774         hints = XAllocSizeHints();
775         if ( hints ) {
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;
780                 }
781                 if ( flags & SDL_FULLSCREEN ) {
782                         hints->x = 0;
783                         hints->y = 0;
784                         hints->flags |= USPosition;
785                 } else
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);
790
791                         /* Flush the resize event so we don't catch it later */
792                         XSync(SDL_Display, True);
793                 }
794                 XSetWMNormalHints(SDL_Display, WMwindow, hints);
795                 XFree(hints);
796         }
797
798         /* Respect the window caption style */
799         if ( flags & SDL_NOFRAME ) {
800                 SDL_bool set;
801                 Atom WM_HINTS;
802
803                 /* We haven't modified the window manager hints yet */
804                 set = SDL_FALSE;
805
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 */
810                         struct {
811                                 unsigned long flags;
812                                 unsigned long functions;
813                                 unsigned long decorations;
814                                 long input_mode;
815                                 unsigned long status;
816                         } MWMHints = { (1L << 1), 0, 0, 0, 0 };
817
818                         XChangeProperty(SDL_Display, WMwindow,
819                                         WM_HINTS, WM_HINTS, 32,
820                                         PropModeReplace,
821                                         (unsigned char *)&MWMHints,
822                                         sizeof(MWMHints)/sizeof(long));
823                         set = SDL_TRUE;
824                 }
825                 /* Now try to set KWM hints */
826                 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
827                 if ( WM_HINTS != None ) {
828                         long KWMHints = 0;
829
830                         XChangeProperty(SDL_Display, WMwindow,
831                                         WM_HINTS, WM_HINTS, 32,
832                                         PropModeReplace,
833                                         (unsigned char *)&KWMHints,
834                                         sizeof(KWMHints)/sizeof(long));
835                         set = SDL_TRUE;
836                 }
837                 /* Now try to set GNOME hints */
838                 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
839                 if ( WM_HINTS != None ) {
840                         long GNOMEHints = 0;
841
842                         XChangeProperty(SDL_Display, WMwindow,
843                                         WM_HINTS, WM_HINTS, 32,
844                                         PropModeReplace,
845                                         (unsigned char *)&GNOMEHints,
846                                         sizeof(GNOMEHints)/sizeof(long));
847                         set = SDL_TRUE;
848                 }
849                 /* Finally set the transient hints if necessary */
850                 if ( ! set ) {
851                         XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
852                 }
853         } else {
854                 SDL_bool set;
855                 Atom WM_HINTS;
856
857                 /* We haven't modified the window manager hints yet */
858                 set = SDL_FALSE;
859
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);
864                         set = SDL_TRUE;
865                 }
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);
870                         set = SDL_TRUE;
871                 }
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);
876                         set = SDL_TRUE;
877                 }
878                 /* Finally unset the transient hints if necessary */
879                 if ( ! set ) {
880                         /* NOTE: Does this work? */
881                         XSetTransientForHint(SDL_Display, WMwindow, None);
882                 }
883         }
884 }
885
886 static int X11_CreateWindow(_THIS, SDL_Surface *screen,
887                             int w, int h, int bpp, Uint32 flags)
888 {
889         int i, depth;
890         Visual *vis;
891         int vis_change;
892         Uint32 Amask;
893
894         /* If a window is already present, destroy it and start fresh */
895         if ( SDL_Window ) {
896                 X11_DestroyWindow(this, screen);
897                 switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
898         }
899
900         /* See if we have been given a window id */
901         if ( SDL_windowid ) {
902                 SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
903         } else {
904                 SDL_Window = 0;
905         }
906
907         /* find out which visual we are going to use */
908         if ( flags & SDL_OPENGL ) {
909                 XVisualInfo *vi;
910
911                 vi = X11_GL_GetVisual(this);
912                 if( !vi ) {
913                         return -1;
914                 }
915                 vis = vi->visual;
916                 depth = vi->depth;
917         } else if ( SDL_windowid ) {
918                 XWindowAttributes a;
919
920                 XGetWindowAttributes(SDL_Display, SDL_Window, &a);
921                 vis = a.visual;
922                 depth = a.depth;
923         } else {
924                 for ( i = 0; i < this->hidden->nvisuals; i++ ) {
925                         if ( this->hidden->visuals[i].bpp == bpp )
926                                 break;
927                 }
928                 if ( i == this->hidden->nvisuals ) {
929                         SDL_SetError("No matching visual for requested depth");
930                         return -1;      /* should never happen */
931                 }
932                 vis = this->hidden->visuals[i].visual;
933                 depth = this->hidden->visuals[i].depth;
934         }
935 #ifdef X11_DEBUG
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);
937 #endif
938         vis_change = (vis != SDL_Visual);
939         SDL_Visual = vis;
940         this->hidden->depth = depth;
941
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));
945         } else {
946                 Amask = 0;
947         }
948         if ( ! SDL_ReallocFormat(screen, bpp,
949                         vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
950                 return -1;
951         }
952
953         /* Create the appropriate colormap */
954         if ( SDL_XColorMap != SDL_DisplayColormap ) {
955                 XFreeColormap(SDL_Display, SDL_XColorMap);
956         }
957         if ( SDL_Visual->class == PseudoColor ) {
958             int ncolors;
959
960             /* Allocate the pixel flags */
961             ncolors = SDL_Visual->map_entries;
962             SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
963             if(SDL_XPixels == NULL) {
964                 SDL_OutOfMemory();
965                 return -1;
966             }
967             SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
968
969             /* always allocate a private colormap on non-default visuals */
970             if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
971                 flags |= SDL_HWPALETTE;
972             }
973             if ( flags & SDL_HWPALETTE ) {
974                 screen->flags |= SDL_HWPALETTE;
975                 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
976                                                 SDL_Visual, AllocAll);
977             } else {
978                 SDL_XColorMap = SDL_DisplayColormap;
979             }
980         } else if ( SDL_Visual->class == DirectColor ) {
981
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);
986
987             /* Initialize the colormap to the identity mapping */
988             SDL_GetGammaRamp(0, 0, 0);
989             this->screen = screen;
990             X11_SetGammaRamp(this, this->gamma);
991             this->screen = NULL;
992         } else {
993             /* Create a read-only colormap for our window */
994             SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
995                                             SDL_Visual, AllocNone);
996         }
997
998         /* Recreate the auxiliary windows, if needed (required for GL) */
999         if ( vis_change )
1000             create_aux_windows(this);
1001
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);
1009         }
1010
1011         /* resize the (possibly new) window manager window */
1012         if( !SDL_windowid ) {
1013                 X11_SetSizeHints(this, w, h, flags);
1014                 window_w = w;
1015                 window_h = h;
1016                 XResizeWindow(SDL_Display, WMwindow, w, h);
1017         }
1018
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 ) {
1023                                 return(-1);
1024                         }
1025                 } else {
1026                         XSetWindowAttributes swa;
1027
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);
1036                 }
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 ));
1042         }
1043         /* Create the graphics context here, once we have a window */
1044         if ( flags & SDL_OPENGL ) {
1045                 if ( X11_GL_CreateContext(this) < 0 ) {
1046                         return(-1);
1047                 } else {
1048                         screen->flags |= SDL_OPENGL;
1049                 }
1050         } else {
1051                 XGCValues gcv;
1052
1053                 gcv.graphics_exposures = False;
1054                 SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1055                                    GCGraphicsExposures, &gcv);
1056                 if ( ! SDL_GC ) {
1057                         SDL_SetError("Couldn't create graphics context");
1058                         return(-1);
1059                 }
1060         }
1061
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);
1068                 }
1069         }
1070
1071 #if 0 /* This is an experiment - are the graphics faster now? - nope. */
1072         if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1073 #endif
1074         /* Cache the window in the server, when possible */
1075         {
1076                 Screen *xscreen;
1077                 XSetWindowAttributes a;
1078
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);
1084                 }
1085         }
1086
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);
1095                 } else {
1096                         screen->flags &= ~SDL_FULLSCREEN;
1097                 }
1098         }
1099         
1100         return(0);
1101 }
1102
1103 static int X11_ResizeWindow(_THIS,
1104                         SDL_Surface *screen, int w, int h, Uint32 flags)
1105 {
1106         if ( ! SDL_windowid ) {
1107                 /* Resize the window manager window */
1108                 X11_SetSizeHints(this, w, h, flags);
1109                 window_w = w;
1110                 window_h = h;
1111                 XResizeWindow(SDL_Display, WMwindow, w, h);
1112
1113                 /* Resize the fullscreen and display windows */
1114                 if ( flags & SDL_FULLSCREEN ) {
1115                         if ( screen->flags & SDL_FULLSCREEN ) {
1116                                 X11_ResizeFullScreen(this);
1117                         } else {
1118                                 screen->flags |= SDL_FULLSCREEN;
1119                                 X11_EnterFullScreen(this);
1120                         }
1121                 } else {
1122                         if ( screen->flags & SDL_FULLSCREEN ) {
1123                                 screen->flags &= ~SDL_FULLSCREEN;
1124                                 X11_LeaveFullScreen(this);
1125                         }
1126                 }
1127                 XResizeWindow(SDL_Display, SDL_Window, w, h);
1128         }
1129         return(0);
1130 }
1131
1132 SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1133                                 int width, int height, int bpp, Uint32 flags)
1134 {
1135         Uint32 saved_flags;
1136
1137         /* Lock the event thread, in multi-threading environments */
1138         SDL_Lock_EventThread();
1139
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;
1145                 }
1146         }
1147
1148         /* Flush any delayed updates */
1149         XSync(GFX_Display, False);
1150
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) {
1157                         current = NULL;
1158                         goto done;
1159                 }
1160         } else {
1161                 if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1162                         current = NULL;
1163                         goto done;
1164                 }
1165         }
1166
1167         /* Update the internal keyboard state */
1168         X11_SetKeyboardState(SDL_Display, NULL);
1169
1170         /* When the window is first mapped, ignore non-modifier keys */
1171         if ( !current->w && !current->h ) {
1172                 Uint8 *keys = SDL_GetKeyState(NULL);
1173                 int i;
1174                 for ( i = 0; i < SDLK_LAST; ++i ) {
1175                         switch (i) {
1176                             case SDLK_NUMLOCK:
1177                             case SDLK_CAPSLOCK:
1178                             case SDLK_LCTRL:
1179                             case SDLK_RCTRL:
1180                             case SDLK_LSHIFT:
1181                             case SDLK_RSHIFT:
1182                             case SDLK_LALT:
1183                             case SDLK_RALT:
1184                             case SDLK_LMETA:
1185                             case SDLK_RMETA:
1186                             case SDLK_MODE:
1187                                 break;
1188                             default:
1189                                 keys[i] = SDL_RELEASED;
1190                                 break;
1191                         }
1192                 }
1193         }
1194
1195         /* Set up the new mode framebuffer */
1196         if ( ((current->w != width) || (current->h != height)) ||
1197              ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1198                 current->w = width;
1199                 current->h = height;
1200                 current->pitch = SDL_CalculatePitch(current);
1201                 if (X11_ResizeImage(this, current, flags) < 0) {
1202                         current = NULL;
1203                         goto done;
1204                 }
1205         }
1206
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));
1210
1211   done:
1212         /* Release the event thread */
1213         XSync(SDL_Display, False);
1214         SDL_Unlock_EventThread();
1215
1216         /* We're done! */
1217         return(current);
1218 }
1219
1220 static int X11_ToggleFullScreen(_THIS, int on)
1221 {
1222         Uint32 event_thread;
1223
1224         /* Don't switch if we don't own the window */
1225         if ( SDL_windowid ) {
1226                 return(0);
1227         }
1228
1229         /* Don't lock if we are the event thread */
1230         event_thread = SDL_EventThreadID();
1231         if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1232                 event_thread = 0;
1233         }
1234         if ( event_thread ) {
1235                 SDL_Lock_EventThread();
1236         }
1237         if ( on ) {
1238                 this->screen->flags |= SDL_FULLSCREEN;
1239                 X11_EnterFullScreen(this);
1240         } else {
1241                 this->screen->flags &= ~SDL_FULLSCREEN;
1242                 X11_LeaveFullScreen(this);
1243         }
1244         X11_RefreshDisplay(this);
1245         if ( event_thread ) {
1246                 SDL_Unlock_EventThread();
1247         }
1248         SDL_ResetKeyboard();
1249         return(1);
1250 }
1251
1252 /* Update the current mouse state and position */
1253 static void X11_UpdateMouse(_THIS)
1254 {
1255         Window u1; int u2;
1256         Window current_win;
1257         int x, y;
1258         unsigned int mask;
1259
1260         /* Lock the event thread, in multi-threading environments */
1261         SDL_Lock_EventThread();
1262         if ( XQueryPointer(SDL_Display, SDL_Window, &u1, &current_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);
1268                 } else {
1269                         SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1270                 }
1271         }
1272         SDL_Unlock_EventThread();
1273 }
1274
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)))
1282
1283 static void allocate_nearest(_THIS, SDL_Color *colors,
1284                              SDL_Color *want, int nwant)
1285 {
1286         /*
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
1289          * that is shared.
1290          */
1291         XColor all[256];
1292         int i;
1293         for(i = 0; i < 256; i++)
1294                 all[i].pixel = i;
1295         /* 
1296          * XQueryColors sets the flags in the XColor struct, so we use
1297          * that to keep track of which colours are available
1298          */
1299         XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1300
1301         for(i = 0; i < nwant; i++) {
1302                 XColor *c;
1303                 int j;
1304                 int best = 0;
1305                 int mindist = 0x7fffffff;
1306                 int ri = want[i].r;
1307                 int gi = want[i].g;
1308                 int bi = want[i].b;
1309                 for(j = 0; j < 256; j++) {
1310                         int rj, gj, bj, d2;
1311                         if(!all[j].flags)
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);
1317                         if(d2 < mindist) {
1318                                 mindist = d2;
1319                                 best = j;
1320                         }
1321                 }
1322                 if(SDL_XPixels[best])
1323                         continue; /* already allocated, waste no more time */
1324                 c = all + best;
1325                 if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1326                         /* got it */
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];
1331                 } else {
1332                         /* 
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
1337                          * are always there.
1338                          */
1339                         c->flags = 0;
1340                         i--;
1341                 }
1342         }
1343 }
1344
1345 int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1346 {
1347         int nrej = 0;
1348
1349         /* Check to make sure we have a colormap allocated */
1350         if ( SDL_XPixels == NULL ) {
1351                 return(0);
1352         }
1353         if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1354                 /* private writable colormap: just set the colours we need */
1355                 XColor  *xcmap;
1356                 int i;
1357                 xcmap = SDL_stack_alloc(XColor, ncolors);
1358                 if(xcmap == NULL)
1359                         return 0;
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);
1366                 }
1367                 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1368                 XSync(GFX_Display, False);
1369                 SDL_stack_free(xcmap);
1370         } else {
1371                 /*
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.
1377                  *
1378                  * We can work directly with the logical palette since it
1379                  * has already been set when we get here.
1380                  */
1381                 SDL_Color *want, *reject;
1382                 unsigned long *freelist;
1383                 int i;
1384                 int nfree = 0;
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];
1394                         }
1395                 }
1396                 XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1397                 SDL_stack_free(freelist);
1398
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));
1405
1406                 /* now try to allocate the colours */
1407                 for(i = 0; i < ncolors; i++) {
1408                         XColor col;
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];
1420                         } else {
1421                                 /*
1422                                  * no more free cells, add it to the list
1423                                  * of rejected colours
1424                                  */
1425                                 reject[nrej++] = want[i];
1426                         }
1427                 }
1428                 if(nrej)
1429                         allocate_nearest(this, colors, reject, nrej);
1430                 SDL_stack_free(reject);
1431                 SDL_stack_free(want);
1432         }
1433         return nrej == 0;
1434 }
1435
1436 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1437 {
1438         int i, ncolors;
1439         XColor xcmap[256];
1440
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");
1444             return(-1);
1445         }
1446
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);
1456         }
1457         XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1458         XSync(GFX_Display, False);
1459         return(0);
1460 }
1461
1462 /* Note:  If we are terminated, this could be called in the middle of
1463    another SDL video routine -- notably UpdateRects.
1464 */
1465 void X11_VideoQuit(_THIS)
1466 {
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);
1472
1473                 /* Close the connection with the IM server */
1474                 #ifdef X_HAVE_UTF8_STRING
1475                 if (SDL_IC != NULL) {
1476                         XUnsetICFocus(SDL_IC);
1477                         XDestroyIC(SDL_IC);
1478                         SDL_IC = NULL;
1479                 }
1480                 if (SDL_IM != NULL) {
1481                         XCloseIM(SDL_IM);
1482                         SDL_IM = NULL;
1483                 }
1484                 #endif
1485
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);
1492                 }
1493                 if ( SDL_iconcolors ) {
1494                         unsigned long pixel;
1495                         Colormap dcmap = DefaultColormap(SDL_Display,
1496                                                          SDL_Screen);
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];
1502                                 }
1503                         }
1504                         SDL_free(SDL_iconcolors);
1505                         SDL_iconcolors = NULL;
1506                 } 
1507
1508                 /* Restore gamma settings if they've changed */
1509                 if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1510                         X11_SwapVidModeGamma(this);
1511                 }
1512
1513                 /* Free that blank cursor */
1514                 if ( SDL_BlankCursor != NULL ) {
1515                         this->FreeWMCursor(this, SDL_BlankCursor);
1516                         SDL_BlankCursor = NULL;
1517                 }
1518
1519                 /* Close the X11 graphics connection */
1520                 if ( GFX_Display != NULL ) {
1521                         XCloseDisplay(GFX_Display);
1522                         GFX_Display = NULL;
1523                 }
1524
1525                 /* Close the X11 display connection */
1526                 XCloseDisplay(SDL_Display);
1527                 SDL_Display = NULL;
1528
1529                 /* Reset the X11 error handlers */
1530                 if ( XIO_handler ) {
1531                         XSetIOErrorHandler(XIO_handler);
1532                 }
1533                 if ( X_handler ) {
1534                         XSetErrorHandler(X_handler);
1535                 }
1536
1537                 /* Unload GL library after X11 shuts down */
1538                 X11_GL_UnloadLibrary(this);
1539         }
1540         if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1541                 /* Direct screen access, no memory buffer */
1542                 this->screen->pixels = NULL;
1543         }
1544
1545 #if SDL_VIDEO_DRIVER_X11_XME
1546     XiGMiscDestroy();
1547 #endif
1548 }
1549