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