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 */
62static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat);
63static SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
64static int X11_ToggleFullScreen(_THIS, int on);
65static void X11_UpdateMouse(_THIS);
66static int X11_SetColors(_THIS, int firstcolor, int ncolors,
67			 SDL_Color *colors);
68static int X11_SetGammaRamp(_THIS, Uint16 *ramp);
69static void X11_VideoQuit(_THIS);
70
71int  X11_wmXAdjust;
72int  X11_wmYAdjust;
73
74/* X11 driver bootstrap functions */
75
76static int X11_Available(void)
77{
78	Display *display = NULL;
79	if ( SDL_X11_LoadSymbols() ) {
80		display = XOpenDisplay(NULL);
81		if ( display != NULL ) {
82			XCloseDisplay(display);
83		}
84		SDL_X11_UnloadSymbols();
85	}
86	return(display != NULL);
87}
88
89static void X11_DeleteDevice(SDL_VideoDevice *device)
90{
91	if ( device ) {
92		if ( device->hidden ) {
93			SDL_free(device->hidden);
94		}
95		if ( device->gl_data ) {
96			SDL_free(device->gl_data);
97		}
98		SDL_free(device);
99		SDL_X11_UnloadSymbols();
100	}
101}
102
103static SDL_VideoDevice *X11_CreateDevice(int devindex)
104{
105	SDL_VideoDevice *device = NULL;
106
107	if ( SDL_X11_LoadSymbols() ) {
108		/* Initialize all variables that we clean on shutdown */
109		device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
110		if ( device ) {
111			SDL_memset(device, 0, (sizeof *device));
112			device->hidden = (struct SDL_PrivateVideoData *)
113					SDL_malloc((sizeof *device->hidden));
114			device->gl_data = (struct SDL_PrivateGLData *)
115					SDL_malloc((sizeof *device->gl_data));
116		}
117		if ( (device == NULL) || (device->hidden == NULL) ||
118		                         (device->gl_data == NULL) ) {
119			SDL_OutOfMemory();
120			X11_DeleteDevice(device); /* calls SDL_X11_UnloadSymbols(). */
121			return(0);
122		}
123		SDL_memset(device->hidden, 0, (sizeof *device->hidden));
124		SDL_memset(device->gl_data, 0, (sizeof *device->gl_data));
125
126#if SDL_VIDEO_OPENGL_GLX
127		device->gl_data->swap_interval = -1;
128#endif
129
130		/* Set the driver flags */
131		device->handles_any_size = 1;
132
133		/* Set the function pointers */
134		device->VideoInit = X11_VideoInit;
135		device->ListModes = X11_ListModes;
136		device->SetVideoMode = X11_SetVideoMode;
137		device->ToggleFullScreen = X11_ToggleFullScreen;
138		device->UpdateMouse = X11_UpdateMouse;
139#if SDL_VIDEO_DRIVER_X11_XV
140		device->CreateYUVOverlay = X11_CreateYUVOverlay;
141#endif
142		device->SetColors = X11_SetColors;
143		device->UpdateRects = NULL;
144		device->VideoQuit = X11_VideoQuit;
145		device->AllocHWSurface = X11_AllocHWSurface;
146		device->CheckHWBlit = NULL;
147		device->FillHWRect = NULL;
148		device->SetHWColorKey = NULL;
149		device->SetHWAlpha = NULL;
150		device->LockHWSurface = X11_LockHWSurface;
151		device->UnlockHWSurface = X11_UnlockHWSurface;
152		device->FlipHWSurface = X11_FlipHWSurface;
153		device->FreeHWSurface = X11_FreeHWSurface;
154		device->SetGamma = X11_SetVidModeGamma;
155		device->GetGamma = X11_GetVidModeGamma;
156		device->SetGammaRamp = X11_SetGammaRamp;
157		device->GetGammaRamp = NULL;
158#if SDL_VIDEO_OPENGL_GLX
159		device->GL_LoadLibrary = X11_GL_LoadLibrary;
160		device->GL_GetProcAddress = X11_GL_GetProcAddress;
161		device->GL_GetAttribute = X11_GL_GetAttribute;
162		device->GL_MakeCurrent = X11_GL_MakeCurrent;
163		device->GL_SwapBuffers = X11_GL_SwapBuffers;
164#endif
165		device->SetCaption = X11_SetCaption;
166		device->SetIcon = X11_SetIcon;
167		device->IconifyWindow = X11_IconifyWindow;
168		device->GrabInput = X11_GrabInput;
169		device->GetWindowPos = X11_GetWindowPos;
170		device->SetWindowPos = X11_SetWindowPos;
171		device->IsWindowVisible = X11_IsWindowVisible;
172		device->GetMonitorDPI = X11_GetMonitorDPI;
173		device->GetMonitorRect = X11_GetMonitorRect;
174		device->GetWMInfo = X11_GetWMInfo;
175		device->FreeWMCursor = X11_FreeWMCursor;
176		device->CreateWMCursor = X11_CreateWMCursor;
177		device->ShowWMCursor = X11_ShowWMCursor;
178		device->WarpWMCursor = X11_WarpWMCursor;
179		device->CheckMouseMode = X11_CheckMouseMode;
180		device->InitOSKeymap = X11_InitOSKeymap;
181		device->PumpEvents = X11_PumpEvents;
182
183		device->free = X11_DeleteDevice;
184	}
185
186	return device;
187}
188
189VideoBootStrap X11_bootstrap = {
190	"x11", "X Window System",
191	X11_Available, X11_CreateDevice
192};
193
194/* Normal X11 error handler routine */
195static int (*X_handler)(Display *, XErrorEvent *) = NULL;
196static int x_errhandler(Display *d, XErrorEvent *e)
197{
198#if SDL_VIDEO_DRIVER_X11_VIDMODE
199	extern int vm_error;
200#endif
201#if SDL_VIDEO_DRIVER_X11_DGAMOUSE
202	extern int dga_error;
203#endif
204
205#if SDL_VIDEO_DRIVER_X11_VIDMODE
206	/* VidMode errors are non-fatal. :) */
207	/* Are the errors offset by one from the error base?
208	   e.g. the error base is 143, the code is 148, and the
209	        actual error is XF86VidModeExtensionDisabled (4) ?
210	 */
211        if ( (vm_error >= 0) &&
212	     (((e->error_code == BadRequest)&&(e->request_code == vm_error)) ||
213	      ((e->error_code > vm_error) &&
214	       (e->error_code <= (vm_error+XF86VidModeNumberErrors)))) ) {
215#ifdef X11_DEBUG
216{ char errmsg[1024];
217  XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
218printf("VidMode error: %s\n", errmsg);
219}
220#endif
221        	return(0);
222        }
223#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
224
225#if SDL_VIDEO_DRIVER_X11_DGAMOUSE
226	/* DGA errors can be non-fatal. :) */
227        if ( (dga_error >= 0) &&
228	     ((e->error_code > dga_error) &&
229	      (e->error_code <= (dga_error+XF86DGANumberErrors))) ) {
230#ifdef X11_DEBUG
231{ char errmsg[1024];
232  XGetErrorText(d, e->error_code, errmsg, sizeof(errmsg));
233printf("DGA error: %s\n", errmsg);
234}
235#endif
236        	return(0);
237        }
238#endif /* SDL_VIDEO_DRIVER_X11_DGAMOUSE */
239
240	return(X_handler(d,e));
241}
242
243/* X11 I/O error handler routine */
244static int (*XIO_handler)(Display *) = NULL;
245static int xio_errhandler(Display *d)
246{
247	/* Ack!  Lost X11 connection! */
248
249	/* We will crash if we try to clean up our display */
250	if ( SDL_VideoSurface && current_video->hidden->Ximage ) {
251		SDL_VideoSurface->pixels = NULL;
252	}
253	current_video->hidden->X11_Display = NULL;
254
255	/* Continue with the standard X11 error handler */
256	return(XIO_handler(d));
257}
258
259static int (*Xext_handler)(Display *, _Xconst char *, _Xconst char *) = NULL;
260static int xext_errhandler(Display *d, _Xconst char *ext, _Xconst char *reason)
261{
262#ifdef X11_DEBUG
263	printf("Xext error inside SDL (may be harmless):\n");
264	printf("  Extension \"%s\" %s on display \"%s\".\n",
265	       ext, reason, XDisplayString(d));
266#endif
267
268	if (SDL_strcmp(reason, "missing") == 0) {
269		/*
270		 * Since the query itself, elsewhere, can handle a missing extension
271		 *  and the default behaviour in Xlib is to write to stderr, which
272		 *  generates unnecessary bug reports, we just ignore these.
273		 */
274		return 0;
275	}
276
277	/* Everything else goes to the default handler... */
278	return Xext_handler(d, ext, reason);
279}
280
281/* Find out what class name we should use */
282static char *get_classname(char *classname, int maxlen)
283{
284	char *spot;
285#if defined(__LINUX__) || defined(__FREEBSD__)
286	char procfile[1024];
287	char linkfile[1024];
288	int linksize;
289#endif
290
291	/* First allow environment variable override */
292	spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
293	if ( spot ) {
294		SDL_strlcpy(classname, spot, maxlen);
295		return classname;
296	}
297
298	/* Next look at the application's executable name */
299#if defined(__LINUX__) || defined(__FREEBSD__)
300#if defined(__LINUX__)
301	SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
302#elif defined(__FREEBSD__)
303	SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file", getpid());
304#else
305#error Where can we find the executable name?
306#endif
307	linksize = readlink(procfile, linkfile, sizeof(linkfile)-1);
308	if ( linksize > 0 ) {
309		linkfile[linksize] = '\0';
310		spot = SDL_strrchr(linkfile, '/');
311		if ( spot ) {
312			SDL_strlcpy(classname, spot+1, maxlen);
313		} else {
314			SDL_strlcpy(classname, linkfile, maxlen);
315		}
316		return classname;
317	}
318#endif /* __LINUX__ */
319
320	/* Finally use the default we've used forever */
321	SDL_strlcpy(classname, "SDL_App", maxlen);
322	return classname;
323}
324
325/* Create auxiliary (toplevel) windows with the current visual */
326static void create_aux_windows(_THIS)
327{
328    int x = 0, y = 0;
329    char classname[1024];
330    XSetWindowAttributes xattr;
331    XWMHints *hints;
332    unsigned long app_event_mask;
333    int def_vis = (SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen));
334
335    /* Look up some useful Atoms */
336    WM_DELETE_WINDOW = XInternAtom(SDL_Display, "WM_DELETE_WINDOW", False);
337
338    /* Don't create any extra windows if we are being managed */
339    if ( SDL_windowid ) {
340	FSwindow = 0;
341	WMwindow = SDL_strtol(SDL_windowid, NULL, 0);
342        return;
343    }
344
345    if(FSwindow)
346	XDestroyWindow(SDL_Display, FSwindow);
347
348#if SDL_VIDEO_DRIVER_X11_XINERAMA
349    if ( use_xinerama ) {
350        x = xinerama_info.x_org;
351        y = xinerama_info.y_org;
352    }
353#endif
354    xattr.override_redirect = True;
355    xattr.background_pixel = def_vis ? BlackPixel(SDL_Display, SDL_Screen) : 0;
356    xattr.border_pixel = 0;
357    xattr.colormap = SDL_XColorMap;
358
359    FSwindow = XCreateWindow(SDL_Display, SDL_Root,
360                             x + X11_wmXAdjust,
361                             y + X11_wmYAdjust,
362                             32, 32, 0,
363			     this->hidden->depth, InputOutput, SDL_Visual,
364			     CWOverrideRedirect | CWBackPixel | CWBorderPixel
365			     | CWColormap,
366			     &xattr);
367
368    XSelectInput(SDL_Display, FSwindow, StructureNotifyMask);
369
370    /* Tell KDE to keep the fullscreen window on top */
371    {
372	XEvent ev;
373	long mask;
374
375	SDL_memset(&ev, 0, sizeof(ev));
376	ev.xclient.type = ClientMessage;
377	ev.xclient.window = SDL_Root;
378	ev.xclient.message_type = XInternAtom(SDL_Display,
379					      "KWM_KEEP_ON_TOP", False);
380	ev.xclient.format = 32;
381	ev.xclient.data.l[0] = FSwindow;
382	ev.xclient.data.l[1] = CurrentTime;
383	mask = SubstructureRedirectMask;
384	XSendEvent(SDL_Display, SDL_Root, False, mask, &ev);
385    }
386
387    hints = NULL;
388    if(WMwindow) {
389	/* All window attributes must survive the recreation */
390	hints = XGetWMHints(SDL_Display, WMwindow);
391	XDestroyWindow(SDL_Display, WMwindow);
392    }
393
394    /* Create the window for windowed management */
395    /* (reusing the xattr structure above) */
396    WMwindow = XCreateWindow(SDL_Display, SDL_Root,
397                             x, y, 32, 32, 0,
398			     this->hidden->depth, InputOutput, SDL_Visual,
399			     CWBackPixel | CWBorderPixel | CWColormap,
400			     &xattr);
401
402    /* Set the input hints so we get keyboard input */
403    if(!hints) {
404	hints = XAllocWMHints();
405	hints->input = True;
406	hints->flags = InputHint;
407    }
408    XSetWMHints(SDL_Display, WMwindow, hints);
409    XFree(hints);
410    X11_SetCaptionNoLock(this, this->wm_title, this->wm_icon);
411
412    app_event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
413	| PropertyChangeMask | StructureNotifyMask | KeymapStateMask;
414    XSelectInput(SDL_Display, WMwindow, app_event_mask);
415
416    /* Set the class hints so we can get an icon (AfterStep) */
417    get_classname(classname, sizeof(classname));
418    {
419	XClassHint *classhints;
420	classhints = XAllocClassHint();
421	if(classhints != NULL) {
422	    classhints->res_name = classname;
423	    classhints->res_class = classname;
424	    XSetClassHint(SDL_Display, WMwindow, classhints);
425	    XFree(classhints);
426	}
427    }
428
429	{
430		pid_t pid = getpid();
431		char hostname[256];
432
433		if (pid > 0 && gethostname(hostname, sizeof(hostname)) > -1) {
434			Atom _NET_WM_PID = XInternAtom(SDL_Display, "_NET_WM_PID", False);
435			Atom WM_CLIENT_MACHINE = XInternAtom(SDL_Display, "WM_CLIENT_MACHINE", False);
436
437			hostname[sizeof(hostname)-1] = '\0';
438			XChangeProperty(SDL_Display, WMwindow, _NET_WM_PID, XA_CARDINAL, 32,
439					PropModeReplace, (unsigned char *)&pid, 1);
440			XChangeProperty(SDL_Display, WMwindow, WM_CLIENT_MACHINE, XA_STRING, 8,
441					PropModeReplace, (unsigned char *)hostname, SDL_strlen(hostname));
442		}
443	}
444
445	/* Setup the communication with the IM server */
446	/* create_aux_windows may be called several times against the same
447	   Display.  We should reuse the SDL_IM if one has been opened for
448	   the Display, so we should not simply reset SDL_IM here.  */
449
450	#ifdef X_HAVE_UTF8_STRING
451	if (SDL_X11_HAVE_UTF8) {
452		/* Discard obsolete resources if any.  */
453		if (SDL_IM != NULL && SDL_Display != XDisplayOfIM(SDL_IM)) {
454			/* Just a double check. I don't think this
455		           code is ever executed. */
456			SDL_SetError("display has changed while an IM is kept");
457			if (SDL_IC) {
458				XUnsetICFocus(SDL_IC);
459				XDestroyIC(SDL_IC);
460				SDL_IC = NULL;
461			}
462			XCloseIM(SDL_IM);
463			SDL_IM = NULL;
464		}
465
466		/* Open an input method.  */
467		if (SDL_IM == NULL) {
468			char *old_locale = NULL, *old_modifiers = NULL;
469			const char *p;
470			size_t n;
471			/* I'm not comfortable to do locale setup
472			   here.  However, we need C library locale
473			   (and xlib modifiers) to be set based on the
474			   user's preference to use XIM, and many
475			   existing game programs doesn't take care of
476			   users' locale preferences, so someone other
477			   than the game program should do it.
478			   Moreover, ones say that some game programs
479			   heavily rely on the C locale behaviour,
480			   e.g., strcol()'s, and we can't change the C
481			   library locale.  Given the situation, I
482			   couldn't find better place to do the
483			   job... */
484
485			/* Save the current (application program's)
486			   locale settings.  */
487			p = setlocale(LC_ALL, NULL);
488			if ( p ) {
489				n = SDL_strlen(p)+1;
490				old_locale = SDL_stack_alloc(char, n);
491				if ( old_locale ) {
492					SDL_strlcpy(old_locale, p, n);
493				}
494			}
495			p = XSetLocaleModifiers(NULL);
496			if ( p ) {
497				n = SDL_strlen(p)+1;
498				old_modifiers = SDL_stack_alloc(char, n);
499				if ( old_modifiers ) {
500					SDL_strlcpy(old_modifiers, p, n);
501				}
502			}
503
504			/* Fetch the user's preferences and open the
505			   input method with them.  */
506			setlocale(LC_ALL, "");
507			XSetLocaleModifiers("");
508			SDL_IM = XOpenIM(SDL_Display, NULL, classname, classname);
509
510			/* Restore the application's locale settings
511			   so that we don't break the application's
512			   expected behaviour.  */
513			if ( old_locale ) {
514				/* We need to restore the C library
515				   locale first, since the
516				   interpretation of the X modifier
517				   may depend on it.  */
518				setlocale(LC_ALL, old_locale);
519				SDL_stack_free(old_locale);
520			}
521			if ( old_modifiers ) {
522				XSetLocaleModifiers(old_modifiers);
523				SDL_stack_free(old_modifiers);
524			}
525		}
526
527		/* Create a new input context for the new window just created.  */
528		if (SDL_IM == NULL) {
529			SDL_SetError("no input method could be opened");
530		} else {
531			if (SDL_IC != NULL) {
532				/* Discard the old IC before creating new one.  */
533			    XUnsetICFocus(SDL_IC);
534			    XDestroyIC(SDL_IC);
535			}
536			/* Theoretically we should check the current IM supports
537			   PreeditNothing+StatusNothing style (i.e., root window method)
538			   before creating the IC.  However, it is the bottom line method,
539			   and we supports any other options.  If the IM didn't support
540			   root window method, the following call fails, and SDL falls
541			   back to pre-XIM keyboard handling.  */
542			SDL_IC = pXCreateIC(SDL_IM,
543					XNClientWindow, WMwindow,
544					XNFocusWindow, WMwindow,
545					XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
546					XNResourceName, classname,
547					XNResourceClass, classname,
548					NULL);
549
550			if (SDL_IC == NULL) {
551				SDL_SetError("no input context could be created");
552				XCloseIM(SDL_IM);
553				SDL_IM = NULL;
554			} else {
555				/* We need to receive X events that an IM wants and to pass
556				   them to the IM through XFilterEvent. The set of events may
557				   vary depending on the IM implementation and the options
558				   specified through various routes. Although unlikely, the
559				   xlib specification allows IM to change the event requirement
560				   with its own circumstances, it is safe to call SelectInput
561				   whenever we re-create an IC.  */
562				unsigned long mask = 0;
563				char *ret = pXGetICValues(SDL_IC, XNFilterEvents, &mask, NULL);
564				if (ret != NULL) {
565					XUnsetICFocus(SDL_IC);
566					XDestroyIC(SDL_IC);
567					SDL_IC = NULL;
568					SDL_SetError("no input context could be created");
569					XCloseIM(SDL_IM);
570					SDL_IM = NULL;
571				} else {
572					XSelectInput(SDL_Display, WMwindow, app_event_mask | mask);
573					XSetICFocus(SDL_IC);
574				}
575			}
576		}
577	}
578	#endif
579
580	/* Allow the window to be deleted by the window manager */
581	XSetWMProtocols(SDL_Display, WMwindow, &WM_DELETE_WINDOW, 1);
582}
583
584static int X11_VideoInit(_THIS, SDL_PixelFormat *vformat)
585{
586	const char *env;
587	char *display;
588	int i;
589
590	/* Open the X11 display */
591	display = NULL;		/* Get it from DISPLAY environment variable */
592
593	if ( (SDL_strncmp(XDisplayName(display), ":", 1) == 0) ||
594	     (SDL_strncmp(XDisplayName(display), "unix:", 5) == 0) ) {
595		local_X11 = 1;
596	} else {
597		local_X11 = 0;
598	}
599	SDL_Display = XOpenDisplay(display);
600#if defined(__osf__) && defined(SDL_VIDEO_DRIVER_X11_DYNAMIC)
601	/* On Tru64 if linking without -lX11, it fails and you get following message.
602	 * Xlib: connection to ":0.0" refused by server
603	 * Xlib: XDM authorization key matches an existing client!
604	 *
605	 * It succeeds if retrying 1 second later
606	 * or if running xhost +localhost on shell.
607	 *
608	 */
609	if ( SDL_Display == NULL ) {
610		SDL_Delay(1000);
611		SDL_Display = XOpenDisplay(display);
612	}
613#endif
614	if ( SDL_Display == NULL ) {
615		SDL_SetError("Couldn't open X11 display");
616		return(-1);
617	}
618#ifdef X11_DEBUG
619	XSynchronize(SDL_Display, True);
620#endif
621
622	/* Create an alternate X display for graphics updates -- allows us
623	   to do graphics updates in a separate thread from event handling.
624	   Thread-safe X11 doesn't seem to exist.
625	 */
626	GFX_Display = XOpenDisplay(display);
627	if ( GFX_Display == NULL ) {
628		XCloseDisplay(SDL_Display);
629		SDL_Display = NULL;
630		SDL_SetError("Couldn't open X11 display");
631		return(-1);
632	}
633
634	/* Set the normal X error handler */
635	X_handler = XSetErrorHandler(x_errhandler);
636
637	/* Set the error handler if we lose the X display */
638	XIO_handler = XSetIOErrorHandler(xio_errhandler);
639
640	/* Set the X extension error handler */
641	Xext_handler = XSetExtensionErrorHandler(xext_errhandler);
642
643	/* use default screen (from $DISPLAY) */
644	SDL_Screen = DefaultScreen(SDL_Display);
645
646#ifndef NO_SHARED_MEMORY
647	/* Check for MIT shared memory extension */
648	use_mitshm = 0;
649	if ( local_X11 ) {
650		use_mitshm = XShmQueryExtension(SDL_Display);
651	}
652#endif /* NO_SHARED_MEMORY */
653
654	/* Get the available video modes */
655	if(X11_GetVideoModes(this) < 0) {
656		XCloseDisplay(GFX_Display);
657		GFX_Display = NULL;
658		XCloseDisplay(SDL_Display);
659		SDL_Display = NULL;
660	    return -1;
661	}
662
663	/* Determine the current screen size */
664	this->info.current_w = DisplayWidth(SDL_Display, SDL_Screen);
665	this->info.current_h = DisplayHeight(SDL_Display, SDL_Screen);
666
667	/* Determine the default screen depth:
668	   Use the default visual (or at least one with the same depth) */
669	SDL_DisplayColormap = DefaultColormap(SDL_Display, SDL_Screen);
670	for(i = 0; i < this->hidden->nvisuals; i++)
671	    if(this->hidden->visuals[i].depth == DefaultDepth(SDL_Display,
672							      SDL_Screen))
673		break;
674	if(i == this->hidden->nvisuals) {
675	    /* default visual was useless, take the deepest one instead */
676	    i = 0;
677	}
678	SDL_Visual = this->hidden->visuals[i].visual;
679	if ( SDL_Visual == DefaultVisual(SDL_Display, SDL_Screen) ) {
680	    SDL_XColorMap = SDL_DisplayColormap;
681	} else {
682	    SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
683					    SDL_Visual, AllocNone);
684	}
685	this->hidden->depth = this->hidden->visuals[i].depth;
686	vformat->BitsPerPixel = this->hidden->visuals[i].bpp;
687	if ( vformat->BitsPerPixel > 8 ) {
688		vformat->Rmask = SDL_Visual->red_mask;
689	  	vformat->Gmask = SDL_Visual->green_mask;
690	  	vformat->Bmask = SDL_Visual->blue_mask;
691	}
692	if ( this->hidden->depth == 32 ) {
693		vformat->Amask = (0xFFFFFFFF & ~(vformat->Rmask|vformat->Gmask|vformat->Bmask));
694	}
695	X11_SaveVidModeGamma(this);
696
697	/* Allow environment override of screensaver disable. */
698	env = SDL_getenv("SDL_VIDEO_ALLOW_SCREENSAVER");
699	if ( env ) {
700		allow_screensaver = SDL_atoi(env);
701	} else {
702#ifdef SDL_VIDEO_DISABLE_SCREENSAVER
703		allow_screensaver = 0;
704#else
705		allow_screensaver = 1;
706#endif
707	}
708
709	/* See if we have been passed a window to use */
710	SDL_windowid = SDL_getenv("SDL_WINDOWID");
711
712	/* Create the fullscreen and managed windows */
713	create_aux_windows(this);
714
715	/* Create the blank cursor */
716	SDL_BlankCursor = this->CreateWMCursor(this, blank_cdata, blank_cmask,
717					BLANK_CWIDTH, BLANK_CHEIGHT,
718						BLANK_CHOTX, BLANK_CHOTY);
719
720	/* Fill in some window manager capabilities */
721	this->info.wm_available = 1;
722
723	/* We're done! */
724	XFlush(SDL_Display);
725	return(0);
726}
727
728static void X11_DestroyWindow(_THIS, SDL_Surface *screen)
729{
730	/* Clean up OpenGL */
731	if ( screen ) {
732		screen->flags &= ~(SDL_OPENGL|SDL_OPENGLBLIT);
733	}
734	X11_GL_Shutdown(this);
735
736	if ( ! SDL_windowid ) {
737		/* Hide the managed window */
738		if ( WMwindow ) {
739			XUnmapWindow(SDL_Display, WMwindow);
740		}
741		if ( screen && (screen->flags & SDL_FULLSCREEN) ) {
742			screen->flags &= ~SDL_FULLSCREEN;
743			X11_LeaveFullScreen(this);
744		}
745
746		/* Destroy the output window */
747		if ( SDL_Window ) {
748			XDestroyWindow(SDL_Display, SDL_Window);
749		}
750
751		/* Free the colormap entries */
752		if ( SDL_XPixels ) {
753			int numcolors;
754			unsigned long pixel;
755			numcolors = SDL_Visual->map_entries;
756			for ( pixel=0; pixel<numcolors; ++pixel ) {
757				while ( SDL_XPixels[pixel] > 0 ) {
758					XFreeColors(GFX_Display,
759						SDL_DisplayColormap,&pixel,1,0);
760					--SDL_XPixels[pixel];
761				}
762			}
763			SDL_free(SDL_XPixels);
764			SDL_XPixels = NULL;
765		}
766
767		/* Free the graphics context */
768		if ( SDL_GC ) {
769			XFreeGC(SDL_Display, SDL_GC);
770			SDL_GC = 0;
771		}
772	}
773}
774
775static SDL_bool X11_WindowPosition(_THIS, int *x, int *y, int w, int h)
776{
777	const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
778	const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
779	if ( window ) {
780		if ( SDL_sscanf(window, "%d,%d", x, y) == 2 ) {
781			return SDL_TRUE;
782		}
783		if ( SDL_strcmp(window, "center") == 0 ) {
784			center = window;
785		}
786	}
787	if ( center ) {
788		*x = (DisplayWidth(SDL_Display, SDL_Screen) - w)/2;
789		*y = (DisplayHeight(SDL_Display, SDL_Screen) - h)/2;
790		return SDL_TRUE;
791	}
792	return SDL_FALSE;
793}
794
795static void X11_SetSizeHints(_THIS, int w, int h, Uint32 flags)
796{
797	XSizeHints *hints;
798
799	hints = XAllocSizeHints();
800	if ( hints ) {
801		if (!(flags & SDL_RESIZABLE)) {
802			hints->min_width = hints->max_width = w;
803			hints->min_height = hints->max_height = h;
804			hints->flags = PMaxSize | PMinSize;
805		}
806		if ( flags & SDL_FULLSCREEN ) {
807			hints->x = 0;
808			hints->y = 0;
809			hints->flags |= USPosition;
810		} else
811		/* Center it, if desired */
812		if ( X11_WindowPosition(this, &hints->x, &hints->y, w, h) ) {
813			hints->flags |= USPosition;
814
815			/* Hints must be set before moving the window, otherwise an
816			   unwanted ConfigureNotify event will be issued */
817			XSetWMNormalHints(SDL_Display, WMwindow, hints);
818
819			XMoveWindow(SDL_Display, WMwindow, hints->x, hints->y);
820
821			/* Flush the resize event so we don't catch it later */
822			XSync(SDL_Display, True);
823		}
824		XSetWMNormalHints(SDL_Display, WMwindow, hints);
825		XFree(hints);
826	}
827
828	/* Respect the window caption style */
829	if ( flags & SDL_NOFRAME ) {
830		SDL_bool set;
831		Atom WM_HINTS;
832
833		/* We haven't modified the window manager hints yet */
834		set = SDL_FALSE;
835
836		/* First try to set MWM hints */
837		WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
838		if ( WM_HINTS != None ) {
839			/* Hints used by Motif compliant window managers */
840			struct {
841				unsigned long flags;
842				unsigned long functions;
843				unsigned long decorations;
844				long input_mode;
845				unsigned long status;
846			} MWMHints = { (1L << 1), 0, 0, 0, 0 };
847
848			XChangeProperty(SDL_Display, WMwindow,
849			                WM_HINTS, WM_HINTS, 32,
850			                PropModeReplace,
851					(unsigned char *)&MWMHints,
852					sizeof(MWMHints)/sizeof(long));
853			set = SDL_TRUE;
854		}
855		/* Now try to set KWM hints */
856		WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
857		if ( WM_HINTS != None ) {
858			long KWMHints = 0;
859
860			XChangeProperty(SDL_Display, WMwindow,
861			                WM_HINTS, WM_HINTS, 32,
862			                PropModeReplace,
863					(unsigned char *)&KWMHints,
864					sizeof(KWMHints)/sizeof(long));
865			set = SDL_TRUE;
866		}
867		/* Now try to set GNOME hints */
868		WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
869		if ( WM_HINTS != None ) {
870			long GNOMEHints = 0;
871
872			XChangeProperty(SDL_Display, WMwindow,
873			                WM_HINTS, WM_HINTS, 32,
874			                PropModeReplace,
875					(unsigned char *)&GNOMEHints,
876					sizeof(GNOMEHints)/sizeof(long));
877			set = SDL_TRUE;
878		}
879		/* Finally set the transient hints if necessary */
880		if ( ! set ) {
881			XSetTransientForHint(SDL_Display, WMwindow, SDL_Root);
882		}
883	} else {
884		SDL_bool set;
885		Atom WM_HINTS;
886
887		/* We haven't modified the window manager hints yet */
888		set = SDL_FALSE;
889
890		/* First try to unset MWM hints */
891		WM_HINTS = XInternAtom(SDL_Display, "_MOTIF_WM_HINTS", True);
892		if ( WM_HINTS != None ) {
893			XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
894			set = SDL_TRUE;
895		}
896		/* Now try to unset KWM hints */
897		WM_HINTS = XInternAtom(SDL_Display, "KWM_WIN_DECORATION", True);
898		if ( WM_HINTS != None ) {
899			XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
900			set = SDL_TRUE;
901		}
902		/* Now try to unset GNOME hints */
903		WM_HINTS = XInternAtom(SDL_Display, "_WIN_HINTS", True);
904		if ( WM_HINTS != None ) {
905			XDeleteProperty(SDL_Display, WMwindow, WM_HINTS);
906			set = SDL_TRUE;
907		}
908		/* Finally unset the transient hints if necessary */
909		if ( ! set ) {
910			XDeleteProperty(SDL_Display, WMwindow, XA_WM_TRANSIENT_FOR);
911		}
912	}
913}
914
915static int X11_CreateWindow(_THIS, SDL_Surface *screen,
916			    int w, int h, int bpp, Uint32 flags)
917{
918	int i, depth;
919	Visual *vis;
920	int vis_change;
921	Uint32 Amask;
922
923	/* If a window is already present, destroy it and start fresh */
924	if ( SDL_Window ) {
925		X11_DestroyWindow(this, screen);
926		switch_waiting = 0; /* Prevent jump back to now-meaningless state. */
927	}
928
929	/* See if we have been given a window id */
930	if ( SDL_windowid ) {
931		SDL_Window = SDL_strtol(SDL_windowid, NULL, 0);
932	} else {
933		SDL_Window = 0;
934	}
935
936	/* find out which visual we are going to use */
937	if ( flags & SDL_OPENGL ) {
938		XVisualInfo *vi;
939
940		vi = X11_GL_GetVisual(this);
941		if( !vi ) {
942			return -1;
943		}
944		vis = vi->visual;
945		depth = vi->depth;
946	} else if ( SDL_windowid ) {
947		XWindowAttributes a;
948
949		XGetWindowAttributes(SDL_Display, SDL_Window, &a);
950		vis = a.visual;
951		depth = a.depth;
952	} else {
953		for ( i = 0; i < this->hidden->nvisuals; i++ ) {
954			if ( this->hidden->visuals[i].bpp == bpp )
955				break;
956		}
957		if ( i == this->hidden->nvisuals ) {
958			SDL_SetError("No matching visual for requested depth");
959			return -1;	/* should never happen */
960		}
961		vis = this->hidden->visuals[i].visual;
962		depth = this->hidden->visuals[i].depth;
963	}
964#ifdef X11_DEBUG
965        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);
966#endif
967	vis_change = (vis != SDL_Visual);
968	SDL_Visual = vis;
969	this->hidden->depth = depth;
970
971	/* Allocate the new pixel format for this video mode */
972	if ( this->hidden->depth == 32 ) {
973		Amask = (0xFFFFFFFF & ~(vis->red_mask|vis->green_mask|vis->blue_mask));
974	} else {
975		Amask = 0;
976	}
977	if ( ! SDL_ReallocFormat(screen, bpp,
978			vis->red_mask, vis->green_mask, vis->blue_mask, Amask) ) {
979		return -1;
980	}
981
982	/* Create the appropriate colormap */
983	if ( SDL_XColorMap != SDL_DisplayColormap ) {
984		XFreeColormap(SDL_Display, SDL_XColorMap);
985	}
986	if ( SDL_Visual->class == PseudoColor ) {
987	    int ncolors;
988
989	    /* Allocate the pixel flags */
990	    ncolors = SDL_Visual->map_entries;
991	    SDL_XPixels = SDL_malloc(ncolors * sizeof(int));
992	    if(SDL_XPixels == NULL) {
993		SDL_OutOfMemory();
994		return -1;
995	    }
996	    SDL_memset(SDL_XPixels, 0, ncolors * sizeof(*SDL_XPixels));
997
998	    /* always allocate a private colormap on non-default visuals */
999	    if ( SDL_Visual != DefaultVisual(SDL_Display, SDL_Screen) ) {
1000		flags |= SDL_HWPALETTE;
1001	    }
1002	    if ( flags & SDL_HWPALETTE ) {
1003		screen->flags |= SDL_HWPALETTE;
1004		SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1005		                                SDL_Visual, AllocAll);
1006	    } else {
1007		SDL_XColorMap = SDL_DisplayColormap;
1008	    }
1009	} else if ( SDL_Visual->class == DirectColor ) {
1010
1011	    /* Create a colormap which we can manipulate for gamma */
1012	    SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1013		                            SDL_Visual, AllocAll);
1014            XSync(SDL_Display, False);
1015
1016	    /* Initialize the colormap to the identity mapping */
1017	    SDL_GetGammaRamp(0, 0, 0);
1018	    this->screen = screen;
1019	    X11_SetGammaRamp(this, this->gamma);
1020	    this->screen = NULL;
1021	} else {
1022	    /* Create a read-only colormap for our window */
1023	    SDL_XColorMap = XCreateColormap(SDL_Display, SDL_Root,
1024	                                    SDL_Visual, AllocNone);
1025	}
1026
1027	/* Recreate the auxiliary windows, if needed (required for GL) */
1028	if ( vis_change )
1029	    create_aux_windows(this);
1030
1031	if(screen->flags & SDL_HWPALETTE) {
1032	    /* Since the full-screen window might have got a nonzero background
1033	       colour (0 is white on some displays), we should reset the
1034	       background to 0 here since that is what the user expects
1035	       with a private colormap */
1036	    XSetWindowBackground(SDL_Display, FSwindow, 0);
1037	    XClearWindow(SDL_Display, FSwindow);
1038	}
1039
1040	/* resize the (possibly new) window manager window */
1041	if( !SDL_windowid ) {
1042	        X11_SetSizeHints(this, w, h, flags);
1043		window_w = w;
1044		window_h = h;
1045		XResizeWindow(SDL_Display, WMwindow, w, h);
1046	}
1047
1048	/* Create (or use) the X11 display window */
1049	if ( !SDL_windowid ) {
1050		if ( flags & SDL_OPENGL ) {
1051			if ( X11_GL_CreateWindow(this, w, h) < 0 ) {
1052				return(-1);
1053			}
1054		} else {
1055			XSetWindowAttributes swa;
1056
1057			swa.background_pixel = 0;
1058			swa.border_pixel = 0;
1059			swa.colormap = SDL_XColorMap;
1060			SDL_Window = XCreateWindow(SDL_Display, WMwindow,
1061		                           	0, 0, w, h, 0, depth,
1062		                           	InputOutput, SDL_Visual,
1063		                           	CWBackPixel | CWBorderPixel
1064		                           	| CWColormap, &swa);
1065		}
1066		/* Only manage our input if we own the window */
1067		XSelectInput(SDL_Display, SDL_Window,
1068					( EnterWindowMask | LeaveWindowMask
1069					| ButtonPressMask | ButtonReleaseMask
1070					| PointerMotionMask | ExposureMask ));
1071	}
1072	/* Create the graphics context here, once we have a window */
1073	if ( flags & SDL_OPENGL ) {
1074		if ( X11_GL_CreateContext(this) < 0 ) {
1075			return(-1);
1076		} else {
1077			screen->flags |= SDL_OPENGL;
1078		}
1079	} else {
1080		XGCValues gcv;
1081
1082		gcv.graphics_exposures = False;
1083		SDL_GC = XCreateGC(SDL_Display, SDL_Window,
1084		                   GCGraphicsExposures, &gcv);
1085		if ( ! SDL_GC ) {
1086			SDL_SetError("Couldn't create graphics context");
1087			return(-1);
1088		}
1089	}
1090
1091	/* Set our colormaps when not setting a GL mode */
1092	if ( ! (flags & SDL_OPENGL) ) {
1093		XSetWindowColormap(SDL_Display, SDL_Window, SDL_XColorMap);
1094		if( !SDL_windowid ) {
1095		    XSetWindowColormap(SDL_Display, FSwindow, SDL_XColorMap);
1096		    XSetWindowColormap(SDL_Display, WMwindow, SDL_XColorMap);
1097		}
1098	}
1099
1100#if 0 /* This is an experiment - are the graphics faster now? - nope. */
1101	if ( SDL_getenv("SDL_VIDEO_X11_BACKINGSTORE") )
1102#endif
1103	/* Cache the window in the server, when possible */
1104	{
1105		Screen *xscreen;
1106		XSetWindowAttributes a;
1107
1108		xscreen = ScreenOfDisplay(SDL_Display, SDL_Screen);
1109		a.backing_store = DoesBackingStore(xscreen);
1110		if ( a.backing_store != NotUseful ) {
1111			XChangeWindowAttributes(SDL_Display, SDL_Window,
1112			                        CWBackingStore, &a);
1113		}
1114	}
1115
1116	/* Map them both and go fullscreen, if requested */
1117	if ( ! SDL_windowid ) {
1118		XMapWindow(SDL_Display, SDL_Window);
1119		XMapWindow(SDL_Display, WMwindow);
1120		X11_WaitMapped(this, WMwindow);
1121		if ( flags & SDL_FULLSCREEN ) {
1122			screen->flags |= SDL_FULLSCREEN;
1123			X11_EnterFullScreen(this);
1124		} else {
1125			screen->flags &= ~SDL_FULLSCREEN;
1126		}
1127	}
1128
1129	return(0);
1130}
1131
1132static int X11_ResizeWindow(_THIS,
1133			SDL_Surface *screen, int w, int h, Uint32 flags)
1134{
1135	if ( ! SDL_windowid ) {
1136		/* Resize the window manager window */
1137		X11_SetSizeHints(this, w, h, flags);
1138		window_w = w;
1139		window_h = h;
1140		XResizeWindow(SDL_Display, WMwindow, w, h);
1141
1142		/* Resize the fullscreen and display windows */
1143		if ( flags & SDL_FULLSCREEN ) {
1144			if ( screen->flags & SDL_FULLSCREEN ) {
1145				X11_ResizeFullScreen(this);
1146			} else {
1147				screen->flags |= SDL_FULLSCREEN;
1148				X11_EnterFullScreen(this);
1149			}
1150		} else {
1151			if ( screen->flags & SDL_FULLSCREEN ) {
1152				screen->flags &= ~SDL_FULLSCREEN;
1153				X11_LeaveFullScreen(this);
1154			}
1155		}
1156		XResizeWindow(SDL_Display, SDL_Window, w, h);
1157	}
1158	return(0);
1159}
1160
1161SDL_Surface *X11_SetVideoMode(_THIS, SDL_Surface *current,
1162				int width, int height, int bpp, Uint32 flags)
1163{
1164	Uint32 saved_flags;
1165
1166	/* Lock the event thread, in multi-threading environments */
1167	SDL_Lock_EventThread();
1168
1169	/* Check the combination of flags we were passed */
1170	if ( flags & SDL_FULLSCREEN ) {
1171		/* Clear fullscreen flag if not supported */
1172		if ( SDL_windowid ) {
1173			flags &= ~SDL_FULLSCREEN;
1174		}
1175	}
1176
1177	/* Flush any delayed updates */
1178	XSync(GFX_Display, False);
1179
1180	/* Set up the X11 window */
1181	saved_flags = current->flags;
1182	if ( (SDL_Window) && ((saved_flags&SDL_OPENGL) == (flags&SDL_OPENGL))
1183	      && (bpp == current->format->BitsPerPixel)
1184          && ((saved_flags&SDL_NOFRAME) == (flags&SDL_NOFRAME)) ) {
1185		if (X11_ResizeWindow(this, current, width, height, flags) < 0) {
1186			current = NULL;
1187			goto done;
1188		}
1189		X11_PendingConfigureNotifyWidth = width;
1190		X11_PendingConfigureNotifyHeight = height;
1191	} else {
1192		if (X11_CreateWindow(this,current,width,height,bpp,flags) < 0) {
1193			current = NULL;
1194			goto done;
1195		}
1196	}
1197
1198	/* Update the internal keyboard state */
1199	X11_SetKeyboardState(SDL_Display, NULL);
1200
1201	/* When the window is first mapped, ignore non-modifier keys */
1202	if ( !current->w && !current->h ) {
1203		Uint8 *keys = SDL_GetKeyState(NULL);
1204		int i;
1205		for ( i = 0; i < SDLK_LAST; ++i ) {
1206			switch (i) {
1207			    case SDLK_NUMLOCK:
1208			    case SDLK_CAPSLOCK:
1209			    case SDLK_LCTRL:
1210			    case SDLK_RCTRL:
1211			    case SDLK_LSHIFT:
1212			    case SDLK_RSHIFT:
1213			    case SDLK_LALT:
1214			    case SDLK_RALT:
1215			    case SDLK_LMETA:
1216			    case SDLK_RMETA:
1217			    case SDLK_MODE:
1218				break;
1219			    default:
1220				keys[i] = SDL_RELEASED;
1221				break;
1222			}
1223		}
1224	}
1225
1226	/* Set up the new mode framebuffer */
1227	if ( ((current->w != width) || (current->h != height)) ||
1228             ((saved_flags&SDL_OPENGL) != (flags&SDL_OPENGL)) ) {
1229		current->w = width;
1230		current->h = height;
1231		current->pitch = SDL_CalculatePitch(current);
1232		if (X11_ResizeImage(this, current, flags) < 0) {
1233			current = NULL;
1234			goto done;
1235		}
1236	}
1237
1238	/* Clear these flags and set them only if they are in the new set. */
1239	current->flags &= ~(SDL_RESIZABLE|SDL_NOFRAME);
1240	current->flags |= (flags&(SDL_RESIZABLE|SDL_NOFRAME));
1241
1242  done:
1243	/* Release the event thread */
1244	XSync(SDL_Display, False);
1245	SDL_Unlock_EventThread();
1246
1247	/* We're done! */
1248	return(current);
1249}
1250
1251static int X11_ToggleFullScreen(_THIS, int on)
1252{
1253	Uint32 event_thread;
1254
1255	/* Don't switch if we don't own the window */
1256	if ( SDL_windowid ) {
1257		return(0);
1258	}
1259
1260	/* Don't lock if we are the event thread */
1261	event_thread = SDL_EventThreadID();
1262	if ( event_thread && (SDL_ThreadID() == event_thread) ) {
1263		event_thread = 0;
1264	}
1265	if ( event_thread ) {
1266		SDL_Lock_EventThread();
1267	}
1268	if ( on ) {
1269		this->screen->flags |= SDL_FULLSCREEN;
1270		X11_EnterFullScreen(this);
1271	} else {
1272		this->screen->flags &= ~SDL_FULLSCREEN;
1273		X11_LeaveFullScreen(this);
1274	}
1275	X11_RefreshDisplay(this);
1276	if ( event_thread ) {
1277		SDL_Unlock_EventThread();
1278	}
1279	SDL_ResetKeyboard();
1280	return(1);
1281}
1282
1283/* Update the current mouse state and position */
1284static void X11_UpdateMouse(_THIS)
1285{
1286	Window u1; int u2;
1287	Window current_win;
1288	int x, y;
1289	unsigned int mask;
1290
1291	/* Lock the event thread, in multi-threading environments */
1292	SDL_Lock_EventThread();
1293	if ( XQueryPointer(SDL_Display, SDL_Window, &u1, &current_win,
1294	                   &u2, &u2, &x, &y, &mask) ) {
1295		if ( (x >= 0) && (x < SDL_VideoSurface->w) &&
1296		     (y >= 0) && (y < SDL_VideoSurface->h) ) {
1297			SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
1298			SDL_PrivateMouseMotion(0, 0, x, y);
1299		} else {
1300			SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
1301		}
1302	}
1303	SDL_Unlock_EventThread();
1304}
1305
1306/* simple colour distance metric. Supposed to be better than a plain
1307   Euclidian distance anyway. */
1308#define COLOUR_FACTOR 3
1309#define LIGHT_FACTOR 1
1310#define COLOUR_DIST(r1, g1, b1, r2, g2, b2)				\
1311	(COLOUR_FACTOR * (abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2))	\
1312	 + LIGHT_FACTOR * abs(r1 + g1 + b1 - (r2 + g2 + b2)))
1313
1314static void allocate_nearest(_THIS, SDL_Color *colors,
1315			     SDL_Color *want, int nwant)
1316{
1317	/*
1318	 * There is no way to know which ones to choose from, so we retrieve
1319	 * the entire colormap and try the nearest possible, until we find one
1320	 * that is shared.
1321	 */
1322	XColor all[256];
1323	int i;
1324	for(i = 0; i < 256; i++)
1325		all[i].pixel = i;
1326	/*
1327	 * XQueryColors sets the flags in the XColor struct, so we use
1328	 * that to keep track of which colours are available
1329	 */
1330	XQueryColors(GFX_Display, SDL_XColorMap, all, 256);
1331
1332	for(i = 0; i < nwant; i++) {
1333		XColor *c;
1334		int j;
1335		int best = 0;
1336		int mindist = 0x7fffffff;
1337		int ri = want[i].r;
1338		int gi = want[i].g;
1339		int bi = want[i].b;
1340		for(j = 0; j < 256; j++) {
1341			int rj, gj, bj, d2;
1342			if(!all[j].flags)
1343				continue;	/* unavailable colour cell */
1344			rj = all[j].red >> 8;
1345			gj = all[j].green >> 8;
1346			bj = all[j].blue >> 8;
1347			d2 = COLOUR_DIST(ri, gi, bi, rj, gj, bj);
1348			if(d2 < mindist) {
1349				mindist = d2;
1350				best = j;
1351			}
1352		}
1353		if(SDL_XPixels[best])
1354			continue; /* already allocated, waste no more time */
1355		c = all + best;
1356		if(XAllocColor(GFX_Display, SDL_XColorMap, c)) {
1357			/* got it */
1358			colors[c->pixel].r = c->red >> 8;
1359			colors[c->pixel].g = c->green >> 8;
1360			colors[c->pixel].b = c->blue >> 8;
1361			++SDL_XPixels[c->pixel];
1362		} else {
1363			/*
1364			 * The colour couldn't be allocated, probably being
1365			 * owned as a r/w cell by another client. Flag it as
1366			 * unavailable and try again. The termination of the
1367			 * loop is guaranteed since at least black and white
1368			 * are always there.
1369			 */
1370			c->flags = 0;
1371			i--;
1372		}
1373	}
1374}
1375
1376int X11_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
1377{
1378	int nrej = 0;
1379
1380	/* Check to make sure we have a colormap allocated */
1381	if ( SDL_XPixels == NULL ) {
1382		return(0);
1383	}
1384	if ( (this->screen->flags & SDL_HWPALETTE) == SDL_HWPALETTE ) {
1385	        /* private writable colormap: just set the colours we need */
1386	        XColor  *xcmap;
1387		int i;
1388	        xcmap = SDL_stack_alloc(XColor, ncolors);
1389		if(xcmap == NULL)
1390		        return 0;
1391		for ( i=0; i<ncolors; ++i ) {
1392			xcmap[i].pixel = i + firstcolor;
1393			xcmap[i].red   = (colors[i].r<<8)|colors[i].r;
1394			xcmap[i].green = (colors[i].g<<8)|colors[i].g;
1395			xcmap[i].blue  = (colors[i].b<<8)|colors[i].b;
1396			xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1397		}
1398		XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1399		XSync(GFX_Display, False);
1400		SDL_stack_free(xcmap);
1401	} else {
1402	        /*
1403		 * Shared colormap: We only allocate read-only cells, which
1404		 * increases the likelyhood of colour sharing with other
1405		 * clients. The pixel values will almost certainly be
1406		 * different from the requested ones, so the user has to
1407		 * walk the colormap and see which index got what colour.
1408		 *
1409		 * We can work directly with the logical palette since it
1410		 * has already been set when we get here.
1411		 */
1412		SDL_Color *want, *reject;
1413	        unsigned long *freelist;
1414		int i;
1415		int nfree = 0;
1416		int nc = this->screen->format->palette->ncolors;
1417	        colors = this->screen->format->palette->colors;
1418		freelist = SDL_stack_alloc(unsigned long, nc);
1419		/* make sure multiple allocations of the same cell are freed */
1420	        for(i = 0; i < ncolors; i++) {
1421		        int pixel = firstcolor + i;
1422		        while(SDL_XPixels[pixel]) {
1423			        freelist[nfree++] = pixel;
1424				--SDL_XPixels[pixel];
1425			}
1426		}
1427		XFreeColors(GFX_Display, SDL_XColorMap, freelist, nfree, 0);
1428		SDL_stack_free(freelist);
1429
1430		want = SDL_stack_alloc(SDL_Color, ncolors);
1431		reject = SDL_stack_alloc(SDL_Color, ncolors);
1432		SDL_memcpy(want, colors + firstcolor, ncolors * sizeof(SDL_Color));
1433		/* make sure the user isn't fooled by her own wishes
1434		   (black is safe, always available in the default colormap) */
1435		SDL_memset(colors + firstcolor, 0, ncolors * sizeof(SDL_Color));
1436
1437		/* now try to allocate the colours */
1438		for(i = 0; i < ncolors; i++) {
1439		        XColor col;
1440			col.red = want[i].r << 8;
1441			col.green = want[i].g << 8;
1442			col.blue = want[i].b << 8;
1443			col.flags = DoRed | DoGreen | DoBlue;
1444			if(XAllocColor(GFX_Display, SDL_XColorMap, &col)) {
1445			        /* We got the colour, or at least the nearest
1446				   the hardware could get. */
1447			        colors[col.pixel].r = col.red >> 8;
1448				colors[col.pixel].g = col.green >> 8;
1449				colors[col.pixel].b = col.blue >> 8;
1450				++SDL_XPixels[col.pixel];
1451			} else {
1452				/*
1453				 * no more free cells, add it to the list
1454				 * of rejected colours
1455				 */
1456				reject[nrej++] = want[i];
1457			}
1458		}
1459		if(nrej)
1460			allocate_nearest(this, colors, reject, nrej);
1461		SDL_stack_free(reject);
1462		SDL_stack_free(want);
1463	}
1464	return nrej == 0;
1465}
1466
1467int X11_SetGammaRamp(_THIS, Uint16 *ramp)
1468{
1469	int i, ncolors;
1470	XColor xcmap[256];
1471
1472	/* See if actually setting the gamma is supported */
1473	if ( SDL_Visual->class != DirectColor ) {
1474	    SDL_SetError("Gamma correction not supported on this visual");
1475	    return(-1);
1476	}
1477
1478	/* Calculate the appropriate palette for the given gamma ramp */
1479	ncolors = SDL_Visual->map_entries;
1480	for ( i=0; i<ncolors; ++i ) {
1481		Uint8 c = (256 * i / ncolors);
1482		xcmap[i].pixel = SDL_MapRGB(this->screen->format, c, c, c);
1483		xcmap[i].red   = ramp[0*256+c];
1484		xcmap[i].green = ramp[1*256+c];
1485		xcmap[i].blue  = ramp[2*256+c];
1486		xcmap[i].flags = (DoRed|DoGreen|DoBlue);
1487	}
1488	XStoreColors(GFX_Display, SDL_XColorMap, xcmap, ncolors);
1489	XSync(GFX_Display, False);
1490	return(0);
1491}
1492
1493/* Note:  If we are terminated, this could be called in the middle of
1494   another SDL video routine -- notably UpdateRects.
1495*/
1496void X11_VideoQuit(_THIS)
1497{
1498	/* Shutdown everything that's still up */
1499	/* The event thread should be done, so we can touch SDL_Display */
1500	if ( SDL_Display != NULL ) {
1501		/* Flush any delayed updates */
1502		XSync(GFX_Display, False);
1503
1504		/* Close the connection with the IM server */
1505		#ifdef X_HAVE_UTF8_STRING
1506		if (SDL_IC != NULL) {
1507			XUnsetICFocus(SDL_IC);
1508			XDestroyIC(SDL_IC);
1509			SDL_IC = NULL;
1510		}
1511		if (SDL_IM != NULL) {
1512			XCloseIM(SDL_IM);
1513			SDL_IM = NULL;
1514		}
1515		#endif
1516
1517		/* Start shutting down the windows */
1518		X11_DestroyImage(this, this->screen);
1519		X11_DestroyWindow(this, this->screen);
1520		X11_FreeVideoModes(this);
1521		if ( SDL_XColorMap != SDL_DisplayColormap ) {
1522			XFreeColormap(SDL_Display, SDL_XColorMap);
1523		}
1524		if ( SDL_iconcolors ) {
1525			unsigned long pixel;
1526			Colormap dcmap = DefaultColormap(SDL_Display,
1527							 SDL_Screen);
1528			for(pixel = 0; pixel < 256; ++pixel) {
1529				while(SDL_iconcolors[pixel] > 0) {
1530					XFreeColors(GFX_Display,
1531						    dcmap, &pixel, 1, 0);
1532					--SDL_iconcolors[pixel];
1533				}
1534			}
1535			SDL_free(SDL_iconcolors);
1536			SDL_iconcolors = NULL;
1537		}
1538
1539		/* Restore gamma settings if they've changed */
1540		if ( SDL_GetAppState() & SDL_APPACTIVE ) {
1541			X11_SwapVidModeGamma(this);
1542		}
1543
1544		/* Free that blank cursor */
1545		if ( SDL_BlankCursor != NULL ) {
1546			this->FreeWMCursor(this, SDL_BlankCursor);
1547			SDL_BlankCursor = NULL;
1548		}
1549
1550		/* Close the X11 graphics connection */
1551		if ( GFX_Display != NULL ) {
1552			XCloseDisplay(GFX_Display);
1553			GFX_Display = NULL;
1554		}
1555
1556		/* Close the X11 display connection */
1557		XCloseDisplay(SDL_Display);
1558		SDL_Display = NULL;
1559
1560		/* Reset the X11 error handlers */
1561		if ( XIO_handler ) {
1562			XSetIOErrorHandler(XIO_handler);
1563		}
1564		if ( X_handler ) {
1565			XSetErrorHandler(X_handler);
1566		}
1567
1568		/* Unload GL library after X11 shuts down */
1569		X11_GL_UnloadLibrary(this);
1570	}
1571	if ( this->screen && (this->screen->flags & SDL_HWSURFACE) ) {
1572		/* Direct screen access, no memory buffer */
1573		this->screen->pixels = NULL;
1574	}
1575
1576#if SDL_VIDEO_DRIVER_X11_XME
1577    XiGMiscDestroy();
1578#endif
1579}
1580
1581