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