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