Revert "change SDL 1.2 to SDL 2.0"
[platform/upstream/SDL.git] / src / video / x11 / SDL_x11video.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 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         {
421                 pid_t pid = getpid();
422                 char hostname[256];
423
424                 if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1) {
425                         Atom _NET_WM_PID = XInternAtom(SDL_Display, "_NET_WM_PID", False);
426                         Atom WM_CLIENT_MACHINE = XInternAtom(SDL_Display, "WM_CLIENT_MACHINE", False);
427                         
428                         hostname[sizeof(hostname)-1] = '\0';
429                         XChangeProperty(SDL_Display, WMwindow, _NET_WM_PID, XA_CARDINAL, 32,
430                                         PropModeReplace, (unsigned char *)&pid, 1);
431                         XChangeProperty(SDL_Display, WMwindow, WM_CLIENT_MACHINE, XA_STRING, 8,
432                                         PropModeReplace, (unsigned char *)hostname, SDL_strlen(hostname));
433                 }
434         }
435
436         /* Setup the communication with the IM server */
437         /* create_aux_windows may be called several times against the same
438            Display.  We should reuse the SDL_IM if one has been opened for
439            the Display, so we should not simply reset SDL_IM here.  */
440
441         #ifdef X_HAVE_UTF8_STRING
442         if (SDL_X11_HAVE_UTF8) {
443                 /* Discard obsolete resources if any.  */
444                 if (SDL_IM != NULL && SDL_Display != XDisplayOfIM(SDL_IM)) {
445                         /* Just a double check. I don't think this
446                            code is ever executed. */
447                         SDL_SetError("display has changed while an IM is kept");
448                         if (SDL_IC) {
449                                 XUnsetICFocus(SDL_IC);
450                                 XDestroyIC(SDL_IC);
451                                 SDL_IC = NULL;
452                         }
453                         XCloseIM(SDL_IM);
454                         SDL_IM = NULL;
455                 }
456
457                 /* Open an input method.  */
458                 if (SDL_IM == NULL) {
459                         char *old_locale = NULL, *old_modifiers = NULL;
460                         const char *p;
461                         size_t n;
462                         /* I'm not comfortable to do locale setup
463                            here.  However, we need C library locale
464                            (and xlib modifiers) to be set based on the
465                            user's preference to use XIM, and many
466                            existing game programs doesn't take care of
467                            users' locale preferences, so someone other
468                            than the game program should do it.
469                            Moreover, ones say that some game programs
470                            heavily rely on the C locale behaviour,
471                            e.g., strcol()'s, and we can't change the C
472                            library locale.  Given the situation, I
473                            couldn't find better place to do the
474                            job... */
475
476                         /* Save the current (application program's)
477                            locale settings.  */
478                         p = setlocale(LC_ALL, NULL);
479                         if ( p ) {
480                                 n = SDL_strlen(p)+1;
481                                 old_locale = SDL_stack_alloc(char, n);
482                                 if ( old_locale ) {
483                                         SDL_strlcpy(old_locale, p, n);
484                                 }
485                         }
486                         p = XSetLocaleModifiers(NULL);
487                         if ( p ) {
488                                 n = SDL_strlen(p)+1;
489                                 old_modifiers = SDL_stack_alloc(char, n);
490                                 if ( old_modifiers ) {
491                                         SDL_strlcpy(old_modifiers, p, n);
492                                 }
493                         }
494
495                         /* Fetch the user's preferences and open the
496                            input method with them.  */
497                         setlocale(LC_ALL, "");
498                         XSetLocaleModifiers("");
499                         SDL_IM = XOpenIM(SDL_Display, NULL, classname, classname);
500
501                         /* Restore the application's locale settings
502                            so that we don't break the application's
503                            expected behaviour.  */
504                         if ( old_locale ) {
505                                 /* We need to restore the C library
506                                    locale first, since the
507                                    interpretation of the X modifier
508                                    may depend on it.  */
509                                 setlocale(LC_ALL, old_locale);
510                                 SDL_stack_free(old_locale);
511                         }
512                         if ( old_modifiers ) {
513                                 XSetLocaleModifiers(old_modifiers);
514                                 SDL_stack_free(old_modifiers);
515                         }
516                 }
517
518                 /* Create a new input context for the new window just created.  */
519                 if (SDL_IM == NULL) {
520                         SDL_SetError("no input method could be opened");
521                 } else {
522                         if (SDL_IC != NULL) {
523                                 /* Discard the old IC before creating new one.  */
524                             XUnsetICFocus(SDL_IC);
525                             XDestroyIC(SDL_IC);
526                         }
527                         /* Theoretically we should check the current IM supports
528                            PreeditNothing+StatusNothing style (i.e., root window method)
529                            before creating the IC.  However, it is the bottom line method,
530                            and we supports any other options.  If the IM didn't support
531                            root window method, the following call fails, and SDL falls
532                            back to pre-XIM keyboard handling.  */
533                         SDL_IC = pXCreateIC(SDL_IM,
534                                         XNClientWindow, WMwindow,
535                                         XNFocusWindow, WMwindow,
536                                         XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
537                                         XNResourceName, classname,
538                                         XNResourceClass, classname,
539                                         NULL);
540
541                         if (SDL_IC == NULL) {
542                                 SDL_SetError("no input context could be created");
543                                 XCloseIM(SDL_IM);
544                                 SDL_IM = NULL;
545                         } else {
546                                 /* We need to receive X events that an IM wants and to pass
547                                    them to the IM through XFilterEvent. The set of events may
548                                    vary depending on the IM implementation and the options
549                                    specified through various routes. Although unlikely, the
550                                    xlib specification allows IM to change the event requirement
551                                    with its own circumstances, it is safe to call SelectInput
552                                    whenever we re-create an IC.  */
553                                 unsigned long mask = 0;
554                                 char *ret = pXGetICValues(SDL_IC, XNFilterEvents, &mask, NULL);
555                                 if (ret != NULL) {
556                                         XUnsetICFocus(SDL_IC);
557                                         XDestroyIC(SDL_IC);
558                                         SDL_IC = NULL;
559                                         SDL_SetError("no input context could be created");
560                                         XCloseIM(SDL_IM);
561                                         SDL_IM = NULL;
562                                 } else {
563                                         XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
564                                         XSetICFocus(SDL_IC);
565                                 }
566                         }
567                 }
568         }
569         #endif
570
571         /* Allow the window to be deleted by the window manager */
572         XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
573 }
574
575 static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
576 {
577         const char *env;
578         char *display;
579         int i;
580
581         /* Open the X11 display */
582         display = NULL;         /* Get it from DISPLAY environment variable */
583
584         if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
585              (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
586                 local_X11 = 1;
587         } else {
588                 local_X11 = 0;
589         }
590         SDL_Display = XOpenDisplay(display);
591 #if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
592         /* On Tru64 if linking without -lX11, it fails and you get following message.
593          * Xlib: connection to ":0.0" refused by server
594          * Xlib: XDM authorization key matches an existing client!
595          *
596          * It succeeds if retrying 1 second later
597          * or if running xhost +localhost on shell.
598          *
599          */
600         if ( SDL_Display == NULL ) {
601                 SDL_Delay(1000);
602                 SDL_Display = XOpenDisplay(display);
603         }
604 #endif
605         if ( SDL_Display == NULL ) {
606                 SDL_SetError("Couldn't open X11 display");
607                 return(-1);
608         }
609 #ifdef X11_DEBUG
610         XSynchronize(SDL_Display, True);
611 #endif
612
613         /* Create an alternate X display for graphics updates -- allows us
614            to do graphics updates in a separate thread from event handling.
615            Thread-safe X11 doesn't seem to exist.
616          */
617         GFX_Display = XOpenDisplay(display);
618         if ( GFX_Display == NULL ) {
619                 XCloseDisplay(SDL_Display);
620                 SDL_Display = NULL;
621                 SDL_SetError("Couldn't open X11 display");
622                 return(-1);
623         }
624
625         /* Set the normal X error handler */
626         X_handler = XSetErrorHandler(x_errhandler);
627
628         /* Set the error handler if we lose the X display */
629         XIO_handler = XSetIOErrorHandler(xio_errhandler);
630
631         /* Set the X extension error handler */
632         Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
633
634         /* use default screen (from $DISPLAY) */
635         SDL_Screen = DefaultScreen(SDL_Display);
636
637 #ifndef NO_SHARED_MEMORY
638         /* Check for MIT shared memory extension */
639         use_mitshm = 0;
640         if ( local_X11 ) {
641                 use_mitshm = XShmQueryExtension(SDL_Display);
642         }
643 #endif /* NO_SHARED_MEMORY */
644
645         /* Get the available video modes */
646         if(X11_GetVideoModes(this) < 0) {
647                 XCloseDisplay(GFX_Display);
648                 GFX_Display = NULL;
649                 XCloseDisplay(SDL_Display);
650                 SDL_Display = NULL;
651             return -1;
652         }
653
654         /* Determine the current screen size */
655         this->info.current_w = DisplayWidth(SDL_Display, SDL_Screen);
656         this->info.current_h = DisplayHeight(SDL_Display, SDL_Screen);
657
658         /* Determine the default screen depth:
659            Use the default visual (or at least one with the same depth) */
660         SDL_DisplayColormap = DefaultColormap(SDL_Display, SDL_Screen);
661         for(i = 0; i < this->hidden->nvisuals; i++)
662             if(this->hidden->visuals[i].depth == DefaultDepth(SDL_Display,
663                                                               SDL_Screen))
664                 break;
665         if(i == this->hidden->nvisuals) {
666             /* default visual was useless, take the deepest one instead */
667             i = 0;
668         }
669         SDL_Visual = this->hidden->visuals[i].visual;
670         if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
671             SDL_XColorMap = SDL_DisplayColormap;
672         } else {
673             SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
674                                             SDL_Visual, AllocNone);
675         }
676         this->hidden->depth = this->hidden->visuals[i].depth;
677         vformat->BitsPerPixel = this->hidden->visuals[i].bpp;
678         if ( vformat->BitsPerPixel > 8 ) {
679                 vformat->Rmask = SDL_Visual->red_mask;
680                 vformat->Gmask = SDL_Visual->green_mask;
681                 vformat->Bmask = SDL_Visual->blue_mask;
682         }
683         if ( this->hidden->depth == 32 ) {
684                 vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
685         }
686         X11_SaveVidModeGamma(this);
687
688         /* Allow environment override of screensaver disable. */
689         env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
690         if ( env ) {
691                 allow_screensaver = SDL_atoi(env);
692         } else {
693 #ifdef SDL_VIDEO_DISABLE_SCREENSAVER
694                 allow_screensaver = 0;
695 #else
696                 allow_screensaver = 1;
697 #endif
698         }
699
700         /* See if we have been passed a window to use */
701         SDL_windowid = SDL_getenv("SDL_WINDOWID");
702
703         /* Create the fullscreen and managed windows */
704         create_aux_windows(this);
705
706         /* Create the blank cursor */
707         SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask,
708                                         BLANK_CWIDTH, BLANK_CHEIGHT,
709                                                 BLANK_CHOTX, BLANK_CHOTY);
710
711         /* Fill in some window manager capabilities */
712         this->info.wm_available = 1;
713
714         /* We're done! */
715         XFlush(SDL_Display);
716         return(0);
717 }
718
719 static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
720 {
721         /* Clean up OpenGL */
722         if ( screen ) {
723                 screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
724         }
725         X11_GL_Shutdown(this);
726
727         if ( ! SDL_windowid ) {
728                 /* Hide the managed window */
729                 if ( WMwindow ) {
730                         XUnmapWindow(SDL_Display, WMwindow);
731                 }
732                 if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
733                         screen->flags &= ~SDL_FULLSCREEN;
734                         X11_LeaveFullScreen(this);
735                 }
736
737                 /* Destroy the output window */
738                 if ( SDL_Window ) {
739                         XDestroyWindow(SDL_Display, SDL_Window);
740                 }
741
742                 /* Free the colormap entries */
743                 if ( SDL_XPixels ) {
744                         int numcolors;
745                         unsigned long pixel;
746                         numcolors = SDL_Visual->map_entries;
747                         for ( pixel=0; pixel<numcolors; ++pixel ) {
748                                 while ( SDL_XPixels[pixel] > 0 ) {
749                                         XFreeColors(GFX_Display,
750                                                 SDL_DisplayColormap,&pixel,1,0);
751                                         --SDL_XPixels[pixel];
752                                 }
753                         }
754                         SDL_free(SDL_XPixels);
755                         SDL_XPixels = NULL;
756                 } 
757
758                 /* Free the graphics context */
759                 if ( SDL_GC ) {
760                         XFreeGC(SDL_Display, SDL_GC);
761                         SDL_GC = 0;
762                 }
763         }
764 }
765
766 static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
767 {
768         const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
769         const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
770         if ( window ) {
771                 if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
772                         return SDL_TRUE;
773                 }
774                 if ( SDL_strcmp(window, "center") == 0 ) {
775                         center = window;
776                 }
777         }
778         if ( center ) {
779                 *x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
780                 *y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
781                 return SDL_TRUE;
782         }
783         return SDL_FALSE;
784 }
785
786 static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
787 {
788         XSizeHints *hints;
789
790         hints = XAllocSizeHints();
791         if ( hints ) {
792                 if (!(flags & SDL_RESIZABLE)) {
793                         hints->min_width = hints->max_width = w;
794                         hints->min_height = hints->max_height = h;
795                         hints->flags = PMaxSize | PMinSize;
796                 }
797                 if ( flags & SDL_FULLSCREEN ) {
798                         hints->x = 0;
799                         hints->y = 0;
800                         hints->flags |= USPosition;
801                 } else
802                 /* Center it, if desired */
803                 if ( X11_WindowPosition(this, &hints->x, &hints->y, w, h) ) {
804                         hints->flags |= USPosition;
805
806                         /* Hints must be set before moving the window, otherwise an
807                            unwanted ConfigureNotify event will be issued */
808                         XSetWMNormalHints(SDL_Display, WMwindow, hints);
809
810                         XMoveWindow(SDL_Display, WMwindow, hints->x, hints->y);
811
812                         /* Flush the resize event so we don't catch it later */
813                         XSync(SDL_Display, True);
814                 }
815                 XSetWMNormalHints(SDL_Display, WMwindow, hints);
816                 XFree(hints);
817         }
818
819         /* Respect the window caption style */
820         if ( flags & SDL_NOFRAME ) {
821                 SDL_bool set;
822                 Atom WM_HINTS;
823
824                 /* We haven't modified the window manager hints yet */
825                 set = SDL_FALSE;
826
827                 /* First try to set MWM hints */
828                 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
829                 if ( WM_HINTS != None ) {
830                         /* Hints used by Motif compliant window managers */
831                         struct {
832                                 unsigned long flags;
833                                 unsigned long functions;
834                                 unsigned long decorations;
835                                 long input_mode;
836                                 unsigned long status;
837                         } MWMHints = { (1L << 1), 0, 0, 0, 0 };
838
839                         XChangeProperty(SDL_Display, WMwindow,
840                                         WM_HINTS, WM_HINTS, 32,
841                                         PropModeReplace,
842                                         (unsigned char *)&MWMHints,
843                                         sizeof(MWMHints)/sizeof(long));
844                         set = SDL_TRUE;
845                 }
846                 /* Now try to set KWM hints */
847                 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
848                 if ( WM_HINTS != None ) {
849                         long KWMHints = 0;
850
851                         XChangeProperty(SDL_Display, WMwindow,
852                                         WM_HINTS, WM_HINTS, 32,
853                                         PropModeReplace,
854                                         (unsigned char *)&KWMHints,
855                                         sizeof(KWMHints)/sizeof(long));
856                         set = SDL_TRUE;
857                 }
858                 /* Now try to set GNOME hints */
859                 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
860                 if ( WM_HINTS != None ) {
861                         long GNOMEHints = 0;
862
863                         XChangeProperty(SDL_Display, WMwindow,
864                                         WM_HINTS, WM_HINTS, 32,
865                                         PropModeReplace,
866                                         (unsigned char *)&GNOMEHints,
867                                         sizeof(GNOMEHints)/sizeof(long));
868                         set = SDL_TRUE;
869                 }
870                 /* Finally set the transient hints if necessary */
871                 if ( ! set ) {
872                         XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
873                 }
874         } else {
875                 SDL_bool set;
876                 Atom WM_HINTS;
877
878                 /* We haven't modified the window manager hints yet */
879                 set = SDL_FALSE;
880
881                 /* First try to unset MWM hints */
882                 WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
883                 if ( WM_HINTS != None ) {
884                         XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
885                         set = SDL_TRUE;
886                 }
887                 /* Now try to unset KWM hints */
888                 WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
889                 if ( WM_HINTS != None ) {
890                         XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
891                         set = SDL_TRUE;
892                 }
893                 /* Now try to unset GNOME hints */
894                 WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
895                 if ( WM_HINTS != None ) {
896                         XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
897                         set = SDL_TRUE;
898                 }
899                 /* Finally unset the transient hints if necessary */
900                 if ( ! set ) {
901                         XDeleteProperty(SDL_Display, WMwindow, XA_WM_TRANSIENT_FOR);
902                 }
903         }
904 }
905
906 static int X11_CreateWindow(_THIS, SDL_Surface *screen,
907                             int w, int h, int bpp, Uint32 flags)
908 {
909         int i, depth;
910         Visual *vis;
911         int vis_change;
912         Uint32 Amask;
913
914         /* If a window is already present, destroy it and start fresh */
915         if ( SDL_Window ) {
916                 X11_DestroyWindow(this, screen);
917                 switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
918         }
919
920         /* See if we have been given a window id */
921         if ( SDL_windowid ) {
922                 SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
923         } else {
924                 SDL_Window = 0;
925         }
926
927         /* find out which visual we are going to use */
928         if ( flags & SDL_OPENGL ) {
929                 XVisualInfo *vi;
930
931                 vi = X11_GL_GetVisual(this);
932                 if( !vi ) {
933                         return -1;
934                 }
935                 vis = vi->visual;
936                 depth = vi->depth;
937         } else if ( SDL_windowid ) {
938                 XWindowAttributes a;
939
940                 XGetWindowAttributes(SDL_Display, SDL_Window, &a);
941                 vis = a.visual;
942                 depth = a.depth;
943         } else {
944                 for ( i = 0; i < this->hidden->nvisuals; i++ ) {
945                         if ( this->hidden->visuals[i].bpp == bpp )
946                                 break;
947                 }
948                 if ( i == this->hidden->nvisuals ) {
949                         SDL_SetError("No matching visual for requested depth");
950                         return -1;      /* should never happen */
951                 }
952                 vis = this->hidden->visuals[i].visual;
953                 depth = this->hidden->visuals[i].depth;
954         }
955 #ifdef X11_DEBUG
956         printf("Choosing %s visual at %d bpp - %d colormap entries\n", vis->class == PseudoColor ? "PseudoColor" : (vis->class == TrueColor ? "TrueColor" : (vis->class == DirectColor ? "DirectColor" : "Unknown")), depth, vis->map_entries);
957 #endif
958         vis_change = (vis != SDL_Visual);
959         SDL_Visual = vis;
960         this->hidden->depth = depth;
961
962         /* Allocate the new pixel format for this video mode */
963         if ( this->hidden->depth == 32 ) {
964                 Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask));
965         } else {
966                 Amask = 0;
967         }
968         if ( ! SDL_ReallocFormat(screen, bpp,
969                         vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
970                 return -1;
971         }
972
973         /* Create the appropriate colormap */
974         if ( SDL_XColorMap != SDL_DisplayColormap ) {
975                 XFreeColormap(SDL_Display, SDL_XColorMap);
976         }
977         if ( SDL_Visual->class == PseudoColor ) {
978             int ncolors;
979
980             /* Allocate the pixel flags */
981             ncolors = SDL_Visual->map_entries;
982             SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
983             if(SDL_XPixels == NULL) {
984                 SDL_OutOfMemory();
985                 return -1;
986             }
987             SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
988
989             /* always allocate a private colormap on non-default visuals */
990             if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
991                 flags |= SDL_HWPALETTE;
992             }
993             if ( flags & SDL_HWPALETTE ) {
994                 screen->flags |= SDL_HWPALETTE;
995                 SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
996                                                 SDL_Visual, AllocAll);
997             } else {
998                 SDL_XColorMap = SDL_DisplayColormap;
999             }
1000         } else if ( SDL_Visual->class == DirectColor ) {
1001
1002             /* Create a colormap which we can manipulate for gamma */
1003             SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1004                                             SDL_Visual, AllocAll);
1005             XSync(SDL_Display, False);
1006
1007             /* Initialize the colormap to the identity mapping */
1008             SDL_GetGammaRamp(0, 0, 0);
1009             this->screen = screen;
1010             X11_SetGammaRamp(this, this->gamma);
1011             this->screen = NULL;
1012         } else {
1013             /* Create a read-only colormap for our window */
1014             SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1015                                             SDL_Visual, AllocNone);
1016         }
1017
1018         /* Recreate the auxiliary windows, if needed (required for GL) */
1019         if ( vis_change )
1020             create_aux_windows(this);
1021
1022         if(screen->flags & SDL_HWPALETTE) {
1023             /* Since the full-screen window might have got a nonzero background
1024                colour (0 is white on some displays), we should reset the
1025                background to 0 here since that is what the user expects
1026                with a private colormap */
1027             XSetWindowBackground(SDL_Display, FSwindow, 0);
1028             XClearWindow(SDL_Display, FSwindow);
1029         }
1030
1031         /* resize the (possibly new) window manager window */
1032         if( !SDL_windowid ) {
1033                 X11_SetSizeHints(this, w, h, flags);
1034                 window_w = w;
1035                 window_h = h;
1036                 XResizeWindow(SDL_Display, WMwindow, w, h);
1037         }
1038
1039         /* Create (or use) the X11 display window */
1040         if ( !SDL_windowid ) {
1041                 if ( flags & SDL_OPENGL ) {
1042                         if ( X11_GL_CreateWindow(this, w, h) < 0 ) {
1043                                 return(-1);
1044                         }
1045                 } else {
1046                         XSetWindowAttributes swa;
1047
1048                         swa.background_pixel = 0;
1049                         swa.border_pixel = 0;
1050                         swa.colormap = SDL_XColorMap;
1051                         SDL_Window = XCreateWindow(SDL_Display, WMwindow,
1052                                                 0, 0, w, h, 0, depth,
1053                                                 InputOutput, SDL_Visual,
1054                                                 CWBackPixel | CWBorderPixel
1055                                                 | CWColormap, &swa);
1056                 }
1057                 /* Only manage our input if we own the window */
1058                 XSelectInput(SDL_Display, SDL_Window,
1059                                         ( EnterWindowMask | LeaveWindowMask
1060                                         | ButtonPressMask | ButtonReleaseMask
1061                                         | PointerMotionMask | ExposureMask ));
1062         }
1063         /* Create the graphics context here, once we have a window */
1064         if ( flags & SDL_OPENGL ) {
1065                 if ( X11_GL_CreateContext(this) < 0 ) {
1066                         return(-1);
1067                 } else {
1068                         screen->flags |= SDL_OPENGL;
1069                 }
1070         } else {
1071                 XGCValues gcv;
1072
1073                 gcv.graphics_exposures = False;
1074                 SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1075                                    GCGraphicsExposures, &gcv);
1076                 if ( ! SDL_GC ) {
1077                         SDL_SetError("Couldn't create graphics context");
1078                         return(-1);
1079                 }
1080         }
1081
1082         /* Set our colormaps when not setting a GL mode */
1083         if ( ! (flags & SDL_OPENGL) ) {
1084                 XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
1085                 if( !SDL_windowid ) {
1086                     XSetWindowColormap(SDL_Display, FSwindow, SDL_XColorMap);
1087                     XSetWindowColormap(SDL_Display, WMwindow, SDL_XColorMap);
1088                 }
1089         }
1090
1091 #if 0 /* This is an experiment - are the graphics faster now? - nope. */
1092         if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1093 #endif
1094         /* Cache the window in the server, when possible */
1095         {
1096                 Screen *xscreen;
1097                 XSetWindowAttributes a;
1098
1099                 xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen);
1100                 a.backing_store = DoesBackingStore(xscreen);
1101                 if ( a.backing_store != NotUseful ) {
1102                         XChangeWindowAttributes(SDL_Display, SDL_Window,
1103                                                 CWBackingStore, &a);
1104                 }
1105         }
1106
1107         /* Map them both and go fullscreen, if requested */
1108         if ( ! SDL_windowid ) {
1109                 XMapWindow(SDL_Display, SDL_Window);
1110                 XMapWindow(SDL_Display, WMwindow);
1111                 X11_WaitMapped(this, WMwindow);
1112                 if ( flags & SDL_FULLSCREEN ) {
1113                         screen->flags |= SDL_FULLSCREEN;
1114                         X11_EnterFullScreen(this);
1115                 } else {
1116                         screen->flags &= ~SDL_FULLSCREEN;
1117                 }
1118         }
1119         
1120         return(0);
1121 }
1122
1123 static int X11_ResizeWindow(_THIS,
1124                         SDL_Surface *screen, int w, int h, Uint32 flags)
1125 {
1126         if ( ! SDL_windowid ) {
1127                 /* Resize the window manager window */
1128                 X11_SetSizeHints(this, w, h, flags);
1129                 window_w = w;
1130                 window_h = h;
1131                 XResizeWindow(SDL_Display, WMwindow, w, h);
1132
1133                 /* Resize the fullscreen and display windows */
1134                 if ( flags & SDL_FULLSCREEN ) {
1135                         if ( screen->flags & SDL_FULLSCREEN ) {
1136                                 X11_ResizeFullScreen(this);
1137                         } else {
1138                                 screen->flags |= SDL_FULLSCREEN;
1139                                 X11_EnterFullScreen(this);
1140                         }
1141                 } else {
1142                         if ( screen->flags & SDL_FULLSCREEN ) {
1143                                 screen->flags &= ~SDL_FULLSCREEN;
1144                                 X11_LeaveFullScreen(this);
1145                         }
1146                 }
1147                 XResizeWindow(SDL_Display, SDL_Window, w, h);
1148         }
1149         return(0);
1150 }
1151
1152 SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1153                                 int width, int height, int bpp, Uint32 flags)
1154 {
1155         Uint32 saved_flags;
1156
1157         /* Lock the event thread, in multi-threading environments */
1158         SDL_Lock_EventThread();
1159
1160         /* Check the combination of flags we were passed */
1161         if ( flags & SDL_FULLSCREEN ) {
1162                 /* Clear fullscreen flag if not supported */
1163                 if ( SDL_windowid ) {
1164                         flags &= ~SDL_FULLSCREEN;
1165                 }
1166         }
1167
1168         /* Flush any delayed updates */
1169         XSync(GFX_Display, False);
1170
1171         /* Set up the X11 window */
1172         saved_flags = current->flags;
1173         if ( (SDL_Window) && ((saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL))
1174               && (bpp == current->format->BitsPerPixel)
1175           && ((saved_flags&SDL_NOFRAME) == (flags&SDL_NOFRAME)) ) {
1176                 if (X11_ResizeWindow(this, current, width, height, flags) < 0) {
1177                         current = NULL;
1178                         goto done;
1179                 }
1180                 X11_PendingConfigureNotifyWidth = width;
1181                 X11_PendingConfigureNotifyHeight = height;
1182         } else {
1183                 if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1184                         current = NULL;
1185                         goto done;
1186                 }
1187         }
1188
1189         /* Update the internal keyboard state */
1190         X11_SetKeyboardState(SDL_Display, NULL);
1191
1192         /* When the window is first mapped, ignore non-modifier keys */
1193         if ( !current->w && !current->h ) {
1194                 Uint8 *keys = SDL_GetKeyState(NULL);
1195                 int i;
1196                 for ( i = 0; i < SDLK_LAST; ++i ) {
1197                         switch (i) {
1198                             case SDLK_NUMLOCK:
1199                             case SDLK_CAPSLOCK:
1200                             case SDLK_LCTRL:
1201                             case SDLK_RCTRL:
1202                             case SDLK_LSHIFT:
1203                             case SDLK_RSHIFT:
1204                             case SDLK_LALT:
1205                             case SDLK_RALT:
1206                             case SDLK_LMETA:
1207                             case SDLK_RMETA:
1208                             case SDLK_MODE:
1209                                 break;
1210                             default:
1211                                 keys[i] = SDL_RELEASED;
1212                                 break;
1213                         }
1214                 }
1215         }
1216
1217         /* Set up the new mode framebuffer */
1218         if ( ((current->w != width) || (current->h != height)) ||
1219              ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1220                 current->w = width;
1221                 current->h = height;
1222                 current->pitch = SDL_CalculatePitch(current);
1223                 if (X11_ResizeImage(this, current, flags) < 0) {
1224                         current = NULL;
1225                         goto done;
1226                 }
1227         }
1228
1229         /* Clear these flags and set them only if they are in the new set. */
1230         current->flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
1231         current->flags |= (flags&(SDL_RESIZABLE|SDL_NOFRAME));
1232
1233   done:
1234         /* Release the event thread */
1235         XSync(SDL_Display, False);
1236         SDL_Unlock_EventThread();
1237
1238         /* We're done! */
1239         return(current);
1240 }
1241
1242 static int X11_ToggleFullScreen(_THIS, int on)
1243 {
1244         Uint32 event_thread;
1245
1246         /* Don't switch if we don't own the window */
1247         if ( SDL_windowid ) {
1248                 return(0);
1249         }
1250
1251         /* Don't lock if we are the event thread */
1252         event_thread = SDL_EventThreadID();
1253         if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1254                 event_thread = 0;
1255         }
1256         if ( event_thread ) {
1257                 SDL_Lock_EventThread();
1258         }
1259         if ( on ) {
1260                 this->screen->flags |= SDL_FULLSCREEN;
1261                 X11_EnterFullScreen(this);
1262         } else {
1263                 this->screen->flags &= ~SDL_FULLSCREEN;
1264                 X11_LeaveFullScreen(this);
1265         }
1266         X11_RefreshDisplay(this);
1267         if ( event_thread ) {
1268                 SDL_Unlock_EventThread();
1269         }
1270         SDL_ResetKeyboard();
1271         return(1);
1272 }
1273
1274 /* Update the current mouse state and position */
1275 static void X11_UpdateMouse(_THIS)
1276 {
1277         Window u1; int u2;
1278         Window current_win;
1279         int x, y;
1280         unsigned int mask;
1281
1282         /* Lock the event thread, in multi-threading environments */
1283         SDL_Lock_EventThread();
1284         if ( XQueryPointer(SDL_Display, SDL_Window, &u1, &current_win,
1285                            &u2, &u2, &x, &y, &mask) ) {
1286                 if ( (x >= 0) && (x < SDL_VideoSurface->w) &&
1287                      (y >= 0) && (y < SDL_VideoSurface->h) ) {
1288                         SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
1289                         SDL_PrivateMouseMotion(0, 0, x, y);
1290                 } else {
1291                         SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1292                 }
1293         }
1294         SDL_Unlock_EventThread();
1295 }
1296
1297 /* simple colour distance metric. Supposed to be better than a plain
1298    Euclidian distance anyway. */
1299 #define COLOUR_FACTOR 3
1300 #define LIGHT_FACTOR 1
1301 #define COLOUR_DIST(r1, g1, b1, r2, g2, b2)                             \
1302         (COLOUR_FACTOR * (abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2))   \
1303          + LIGHT_FACTOR * abs(r1 + g1 + b1 - (r2 + g2 + b2)))
1304
1305 static void allocate_nearest(_THIS, SDL_Color *colors,
1306                              SDL_Color *want, int nwant)
1307 {
1308         /*
1309          * There is no way to know which ones to choose from, so we retrieve
1310          * the entire colormap and try the nearest possible, until we find one
1311          * that is shared.
1312          */
1313         XColor all[256];
1314         int i;
1315         for(i = 0; i < 256; i++)
1316                 all[i].pixel = i;
1317         /* 
1318          * XQueryColors sets the flags in the XColor struct, so we use
1319          * that to keep track of which colours are available
1320          */
1321         XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1322
1323         for(i = 0; i < nwant; i++) {
1324                 XColor *c;
1325                 int j;
1326                 int best = 0;
1327                 int mindist = 0x7fffffff;
1328                 int ri = want[i].r;
1329                 int gi = want[i].g;
1330                 int bi = want[i].b;
1331                 for(j = 0; j < 256; j++) {
1332                         int rj, gj, bj, d2;
1333                         if(!all[j].flags)
1334                                 continue;       /* unavailable colour cell */
1335                         rj = all[j].red >> 8;
1336                         gj = all[j].green >> 8;
1337                         bj = all[j].blue >> 8;
1338                         d2 = COLOUR_DIST(ri, gi, bi, rj, gj, bj);
1339                         if(d2 < mindist) {
1340                                 mindist = d2;
1341                                 best = j;
1342                         }
1343                 }
1344                 if(SDL_XPixels[best])
1345                         continue; /* already allocated, waste no more time */
1346                 c = all + best;
1347                 if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1348                         /* got it */
1349                         colors[c->pixel].r = c->red >> 8;
1350                         colors[c->pixel].g = c->green >> 8;
1351                         colors[c->pixel].b = c->blue >> 8;
1352                         ++SDL_XPixels[c->pixel];
1353                 } else {
1354                         /* 
1355                          * The colour couldn't be allocated, probably being
1356                          * owned as a r/w cell by another client. Flag it as
1357                          * unavailable and try again. The termination of the
1358                          * loop is guaranteed since at least black and white
1359                          * are always there.
1360                          */
1361                         c->flags = 0;
1362                         i--;
1363                 }
1364         }
1365 }
1366
1367 int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1368 {
1369         int nrej = 0;
1370
1371         /* Check to make sure we have a colormap allocated */
1372         if ( SDL_XPixels == NULL ) {
1373                 return(0);
1374         }
1375         if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1376                 /* private writable colormap: just set the colours we need */
1377                 XColor  *xcmap;
1378                 int i;
1379                 xcmap = SDL_stack_alloc(XColor, ncolors);
1380                 if(xcmap == NULL)
1381                         return 0;
1382                 for ( i=0; i<ncolors; ++i ) {
1383                         xcmap[i].pixel = i + firstcolor;
1384                         xcmap[i].red   = (colors[i].r<<8)|colors[i].r;
1385                         xcmap[i].green = (colors[i].g<<8)|colors[i].g;
1386                         xcmap[i].blue  = (colors[i].b<<8)|colors[i].b;
1387                         xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1388                 }
1389                 XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1390                 XSync(GFX_Display, False);
1391                 SDL_stack_free(xcmap);
1392         } else {
1393                 /*
1394                  * Shared colormap: We only allocate read-only cells, which
1395                  * increases the likelyhood of colour sharing with other
1396                  * clients. The pixel values will almost certainly be
1397                  * different from the requested ones, so the user has to
1398                  * walk the colormap and see which index got what colour.
1399                  *
1400                  * We can work directly with the logical palette since it
1401                  * has already been set when we get here.
1402                  */
1403                 SDL_Color *want, *reject;
1404                 unsigned long *freelist;
1405                 int i;
1406                 int nfree = 0;
1407                 int nc = this->screen->format->palette->ncolors;
1408                 colors = this->screen->format->palette->colors;
1409                 freelist = SDL_stack_alloc(unsigned long, nc);
1410                 /* make sure multiple allocations of the same cell are freed */
1411                 for(i = 0; i < ncolors; i++) {
1412                         int pixel = firstcolor + i;
1413                         while(SDL_XPixels[pixel]) {
1414                                 freelist[nfree++] = pixel;
1415                                 --SDL_XPixels[pixel];
1416                         }
1417                 }
1418                 XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1419                 SDL_stack_free(freelist);
1420
1421                 want = SDL_stack_alloc(SDL_Color, ncolors);
1422                 reject = SDL_stack_alloc(SDL_Color, ncolors);
1423                 SDL_memcpy(want, colors + firstcolor, ncolors * sizeof(SDL_Color));
1424                 /* make sure the user isn't fooled by her own wishes
1425                    (black is safe, always available in the default colormap) */
1426                 SDL_memset(colors + firstcolor, 0, ncolors * sizeof(SDL_Color));
1427
1428                 /* now try to allocate the colours */
1429                 for(i = 0; i < ncolors; i++) {
1430                         XColor col;
1431                         col.red = want[i].r << 8;
1432                         col.green = want[i].g << 8;
1433                         col.blue = want[i].b << 8;
1434                         col.flags = DoRed | DoGreen | DoBlue;
1435                         if(XAllocColor(GFX_Display, SDL_XColorMap, &col)) {
1436                                 /* We got the colour, or at least the nearest
1437                                    the hardware could get. */
1438                                 colors[col.pixel].r = col.red >> 8;
1439                                 colors[col.pixel].g = col.green >> 8;
1440                                 colors[col.pixel].b = col.blue >> 8;
1441                                 ++SDL_XPixels[col.pixel];
1442                         } else {
1443                                 /*
1444                                  * no more free cells, add it to the list
1445                                  * of rejected colours
1446                                  */
1447                                 reject[nrej++] = want[i];
1448                         }
1449                 }
1450                 if(nrej)
1451                         allocate_nearest(this, colors, reject, nrej);
1452                 SDL_stack_free(reject);
1453                 SDL_stack_free(want);
1454         }
1455         return nrej == 0;
1456 }
1457
1458 int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1459 {
1460         int i, ncolors;
1461         XColor xcmap[256];
1462
1463         /* See if actually setting the gamma is supported */
1464         if ( SDL_Visual->class != DirectColor ) {
1465             SDL_SetError("Gamma correction not supported on this visual");
1466             return(-1);
1467         }
1468
1469         /* Calculate the appropriate palette for the given gamma ramp */
1470         ncolors = SDL_Visual->map_entries;
1471         for ( i=0; i<ncolors; ++i ) {
1472                 Uint8 c = (256 * i / ncolors);
1473                 xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
1474                 xcmap[i].red   = ramp[0*256+c];
1475                 xcmap[i].green = ramp[1*256+c];
1476                 xcmap[i].blue  = ramp[2*256+c];
1477                 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1478         }
1479         XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1480         XSync(GFX_Display, False);
1481         return(0);
1482 }
1483
1484 /* Note:  If we are terminated, this could be called in the middle of
1485    another SDL video routine -- notably UpdateRects.
1486 */
1487 void X11_VideoQuit(_THIS)
1488 {
1489         /* Shutdown everything that's still up */
1490         /* The event thread should be done, so we can touch SDL_Display */
1491         if ( SDL_Display != NULL ) {
1492                 /* Flush any delayed updates */
1493                 XSync(GFX_Display, False);
1494
1495                 /* Close the connection with the IM server */
1496                 #ifdef X_HAVE_UTF8_STRING
1497                 if (SDL_IC != NULL) {
1498                         XUnsetICFocus(SDL_IC);
1499                         XDestroyIC(SDL_IC);
1500                         SDL_IC = NULL;
1501                 }
1502                 if (SDL_IM != NULL) {
1503                         XCloseIM(SDL_IM);
1504                         SDL_IM = NULL;
1505                 }
1506                 #endif
1507
1508                 /* Start shutting down the windows */
1509                 X11_DestroyImage(this, this->screen);
1510                 X11_DestroyWindow(this, this->screen);
1511                 X11_FreeVideoModes(this);
1512                 if ( SDL_XColorMap != SDL_DisplayColormap ) {
1513                         XFreeColormap(SDL_Display, SDL_XColorMap);
1514                 }
1515                 if ( SDL_iconcolors ) {
1516                         unsigned long pixel;
1517                         Colormap dcmap = DefaultColormap(SDL_Display,
1518                                                          SDL_Screen);
1519                         for(pixel = 0; pixel < 256; ++pixel) {
1520                                 while(SDL_iconcolors[pixel] > 0) {
1521                                         XFreeColors(GFX_Display,
1522                                                     dcmap, &pixel, 1, 0);
1523                                         --SDL_iconcolors[pixel];
1524                                 }
1525                         }
1526                         SDL_free(SDL_iconcolors);
1527                         SDL_iconcolors = NULL;
1528                 } 
1529
1530                 /* Restore gamma settings if they've changed */
1531                 if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1532                         X11_SwapVidModeGamma(this);
1533                 }
1534
1535                 /* Free that blank cursor */
1536                 if ( SDL_BlankCursor != NULL ) {
1537                         this->FreeWMCursor(this, SDL_BlankCursor);
1538                         SDL_BlankCursor = NULL;
1539                 }
1540
1541                 /* Close the X11 graphics connection */
1542                 if ( GFX_Display != NULL ) {
1543                         XCloseDisplay(GFX_Display);
1544                         GFX_Display = NULL;
1545                 }
1546
1547                 /* Close the X11 display connection */
1548                 XCloseDisplay(SDL_Display);
1549                 SDL_Display = NULL;
1550
1551                 /* Reset the X11 error handlers */
1552                 if ( XIO_handler ) {
1553                         XSetIOErrorHandler(XIO_handler);
1554                 }
1555                 if ( X_handler ) {
1556                         XSetErrorHandler(X_handler);
1557                 }
1558
1559                 /* Unload GL library after X11 shuts down */
1560                 X11_GL_UnloadLibrary(this);
1561         }
1562         if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1563                 /* Direct screen access, no memory buffer */
1564                 this->screen->pixels = NULL;
1565         }
1566
1567 #if SDL_VIDEO_DRIVER_X11_XME
1568     XiGMiscDestroy();
1569 #endif
1570 }
1571