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