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