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