src/video/x11/SDL_x11modes.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. XVidMode
  2. save_mode
  3. restore_mode
  4. cmpmodes
  5. set_best_resolution
  6. get_real_resolution
  7. X11_WaitMapped
  8. X11_WaitUnmapped
  9. move_cursor_to
  10. add_visual
  11. add_visual_byid
  12. X11_GetVideoModes
  13. X11_SupportedVisual
  14. X11_ListModes
  15. X11_FreeVideoModes
  16. X11_ResizeFullScreen
  17. X11_QueueEnterFullScreen
  18. X11_EnterFullScreen
  19. X11_LeaveFullScreen

   1 /*
   2     SDL - Simple DirectMedia Layer
   3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
   4 
   5     This library is free software; you can redistribute it and/or
   6     modify it under the terms of the GNU Library General Public
   7     License as published by the Free Software Foundation; either
   8     version 2 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     Library General Public License for more details.
  14 
  15     You should have received a copy of the GNU Library General Public
  16     License along with this library; if not, write to the Free
  17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 
  19     Sam Lantinga
  20     slouken@devolution.com
  21 */
  22 
  23 #ifdef SAVE_RCSID
  24 static char rcsid =
  25  "@(#) $Id: SDL_x11modes.c,v 1.1.2.28 2001/02/10 07:20:15 hercules Exp $";
  26 #endif
  27 
  28 /* Utilities for getting and setting the X display mode */
  29 
  30 #include <stdlib.h>
  31 #include <string.h>
  32 
  33 #include "SDL_timer.h"
  34 #include "SDL_error.h"
  35 #include "SDL_events.h"
  36 #include "SDL_events_c.h"
  37 #include "SDL_x11video.h"
  38 #include "SDL_x11wm_c.h"
  39 #include "SDL_x11modes_c.h"
  40 
  41 
  42 #ifdef XFREE86_VM
  43 Bool XVidMode(GetModeInfo, (Display *dpy, int scr, XF86VidModeModeInfo *info))
     /* [<][>][^][v][top][bottom][index][help] */
  44 {
  45     XF86VidModeModeLine *l = (XF86VidModeModeLine*)((char*)info + sizeof info->dotclock);
  46     return XVidMode(GetModeLine, (dpy, scr, &info->dotclock, l));
  47 }
  48 #endif /* XFREE86_VM */
  49 
  50 #ifdef XFREE86_VM
  51 static void save_mode(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
  52 {
  53     memset(&saved_mode, 0, sizeof(saved_mode));
  54     XVidMode(GetModeInfo, (SDL_Display,SDL_Screen,&saved_mode));
  55     XVidMode(GetViewPort, (SDL_Display,SDL_Screen,&saved_view.x,&saved_view.y));
  56 }
  57 #endif
  58 
  59 #ifdef XFREE86_VM
  60 static void restore_mode(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
  61 {
  62     XF86VidModeModeLine mode;
  63     int unused;
  64 
  65     if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &unused, &mode)) ) {
  66         if ( (saved_mode.hdisplay != mode.hdisplay) ||
  67              (saved_mode.vdisplay != mode.vdisplay) ) {
  68             XVidMode(SwitchToMode, (SDL_Display, SDL_Screen, &saved_mode));
  69         }
  70     }
  71     if ( (saved_view.x != 0) || (saved_view.y != 0) ) {
  72         XVidMode(SetViewPort, (SDL_Display, SDL_Screen, saved_view.x, saved_view.y));
  73     }
  74 }
  75 #endif
  76 
  77 #ifdef XFREE86_VM
  78 static int cmpmodes(const void *va, const void *vb)
     /* [<][>][^][v][top][bottom][index][help] */
  79 {
  80     XF86VidModeModeInfo *a = *(XF86VidModeModeInfo**)va;
  81     XF86VidModeModeInfo *b = *(XF86VidModeModeInfo**)vb;
  82     if(a->hdisplay > b->hdisplay)
  83         return -1;
  84     return b->vdisplay - a->vdisplay;
  85 }
  86 #endif
  87 
  88 static void set_best_resolution(_THIS, int width, int height)
     /* [<][>][^][v][top][bottom][index][help] */
  89 {
  90 #ifdef XFREE86_VM
  91     if ( use_vidmode ) {
  92         XF86VidModeModeLine mode;
  93         XF86VidModeModeInfo **modes;
  94         int i;
  95         int nmodes;
  96 
  97         if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &i, &mode)) &&
  98              XVidMode(GetAllModeLines, (SDL_Display,SDL_Screen,&nmodes,&modes))){
  99             qsort(modes, nmodes, sizeof *modes, cmpmodes);
 100 #ifdef XFREE86_DEBUG
 101   printf("Available modes:\n");
 102   for ( i = 0; i < nmodes; ++i ) {
 103     printf("Mode %d: %dx%d\n", i, modes[i]->hdisplay, modes[i]->vdisplay);
 104   }
 105 #endif
 106             for ( i = nmodes-1; i > 0 ; --i ) {
 107                 if ( (modes[i]->hdisplay >= width) &&
 108                      (modes[i]->vdisplay >= height) )
 109                     break;
 110             }
 111             if ( (modes[i]->hdisplay != mode.hdisplay) ||
 112                  (modes[i]->vdisplay != mode.vdisplay) ) {
 113                 XVidMode(SwitchToMode, (SDL_Display, SDL_Screen, modes[i]));
 114             }
 115             XFree(modes);
 116         }
 117     }
 118 #endif /* XFREE86_VM */
 119 }
 120 
 121 static void get_real_resolution(_THIS, int* w, int* h)
     /* [<][>][^][v][top][bottom][index][help] */
 122 {
 123 #ifdef XFREE86_VM
 124     if ( use_vidmode ) {
 125         XF86VidModeModeLine mode;
 126         int unused;
 127 
 128         if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &unused, &mode)) ) {
 129             *w = mode.hdisplay;
 130             *h = mode.vdisplay;
 131             return;
 132         }
 133     }
 134 #endif
 135     *w = DisplayWidth(SDL_Display, SDL_Screen);
 136     *h = DisplayHeight(SDL_Display, SDL_Screen);
 137 }
 138 
 139 /* Called after mapping a window - waits until the window is mapped */
 140 void X11_WaitMapped(_THIS, Window win)
     /* [<][>][^][v][top][bottom][index][help] */
 141 {
 142     XEvent event;
 143     do {
 144         XMaskEvent(SDL_Display, StructureNotifyMask, &event);
 145     } while ( (event.type != MapNotify) || (event.xmap.event != win) );
 146 }
 147 
 148 /* Called after unmapping a window - waits until the window is unmapped */
 149 void X11_WaitUnmapped(_THIS, Window win)
     /* [<][>][^][v][top][bottom][index][help] */
 150 {
 151     XEvent event;
 152     do {
 153         XMaskEvent(SDL_Display, StructureNotifyMask, &event);
 154     } while ( (event.type != UnmapNotify) || (event.xunmap.event != win) );
 155 }
 156 
 157 static void move_cursor_to(_THIS, int x, int y)
     /* [<][>][^][v][top][bottom][index][help] */
 158 {
 159     XWarpPointer(SDL_Display, None, SDL_Root, 0, 0, 0, 0, x, y);
 160 }
 161 
 162 static int add_visual(_THIS, int depth, int class)
     /* [<][>][^][v][top][bottom][index][help] */
 163 {
 164     XVisualInfo vi;
 165     if(XMatchVisualInfo(SDL_Display, SDL_Screen, depth, class, &vi)) {
 166         int n = this->hidden->nvisuals;
 167         this->hidden->visuals[n].depth = vi.depth;
 168         this->hidden->visuals[n].visual = vi.visual;
 169         this->hidden->nvisuals++;
 170     }
 171     return(this->hidden->nvisuals);
 172 }
 173 static int add_visual_byid(_THIS, const char *visual_id)
     /* [<][>][^][v][top][bottom][index][help] */
 174 {
 175     XVisualInfo *vi, template;
 176     int nvis;
 177 
 178     if ( visual_id ) {
 179         memset(&template, 0, (sizeof template));
 180         template.visualid = strtol(visual_id, NULL, 0);
 181         vi = XGetVisualInfo(SDL_Display, VisualIDMask, &template, &nvis);
 182         if ( vi ) {
 183             int n = this->hidden->nvisuals;
 184             this->hidden->visuals[n].depth = vi->depth;
 185             this->hidden->visuals[n].visual = vi->visual;
 186             this->hidden->nvisuals++;
 187             XFree(vi);
 188         }
 189     }
 190     return(this->hidden->nvisuals);
 191 }
 192 
 193 /* Global for the error handler */
 194 int vm_event, vm_error = -1;
 195 
 196 int X11_GetVideoModes(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 197 {
 198 #ifdef XFREE86_VM
 199     int buggy_X11;
 200     int vm_major, vm_minor;
 201     int nmodes;
 202     XF86VidModeModeInfo **modes;
 203 #endif
 204     int i;
 205 
 206     vm_error = -1;
 207     use_vidmode = 0;
 208 #ifdef XFREE86_VM
 209     /* Metro-X 4.3.0 and earlier has a broken implementation of
 210        XF86VidModeGetAllModeLines() - it hangs the client.
 211      */
 212     buggy_X11 = 0;
 213     if ( strcmp(ServerVendor(SDL_Display), "Metro Link Incorporated") == 0 ) {
 214         FILE *metro_fp;
 215 
 216         metro_fp = fopen("/usr/X11R6/lib/X11/Metro/.version", "r");
 217         if ( metro_fp != NULL ) {
 218             int major, minor, patch, version;
 219             major = 0; minor = 0; patch = 0;
 220             fscanf(metro_fp, "%d.%d.%d", &major, &minor, &patch);
 221             version = major*100+minor*10+patch;
 222             if ( version < 431 ) {
 223                 buggy_X11 = 1;
 224             }
 225             fclose(metro_fp);
 226         }
 227     }
 228 #if defined(__alpha__) || defined(__powerpc__)
 229     /* The alpha and PPC XFree86 servers are also buggy */
 230     buggy_X11 = 1;
 231 #endif
 232     /* Enumerate the available fullscreen modes */
 233     if ( ! buggy_X11 ) {
 234         if ( XVidMode(QueryExtension, (SDL_Display, &vm_event, &vm_error)) &&
 235               XVidMode(QueryVersion, (SDL_Display, &vm_major, &vm_minor)) ) {
 236 #ifdef BROKEN_XFREE86_4001
 237 #ifdef X_XF86VidModeGetDotClocks  /* Compiled under XFree86 4.0 */
 238                 /* Earlier X servers hang when doing vidmode */
 239                 if ( vm_major < 2 ) {
 240 #ifdef DEBUG_XF86
 241   printf("Compiled under XFree86 4.0, server is XFree86 3.X\n");
 242 #endif
 243                     buggy_X11 = 1;
 244                 }
 245 #else
 246                 /* XFree86 3.X code works with XFree86 4.0 servers */;
 247 #endif /* XFree86 4.0 */
 248 #endif /* XFree86 4.02 and newer are fixed wrt backwards compatibility */
 249         } else {
 250             buggy_X11 = 1;
 251         }
 252     }
 253     if ( ! buggy_X11 &&
 254          XVidMode(GetAllModeLines, (SDL_Display, SDL_Screen,&nmodes,&modes)) ) {
 255 
 256         qsort(modes, nmodes, sizeof *modes, cmpmodes);
 257         SDL_modelist = (SDL_Rect **)malloc((nmodes+1)*sizeof(SDL_Rect *));
 258         if ( SDL_modelist ) {
 259             for ( i=0; i<nmodes; ++i ) {
 260                 SDL_modelist[i] = (SDL_Rect *)malloc(sizeof(SDL_Rect));
 261                 if ( SDL_modelist[i] == NULL ) {
 262                     break;
 263                 }
 264                 SDL_modelist[i]->x = 0;
 265                 SDL_modelist[i]->y = 0;
 266                 SDL_modelist[i]->w = modes[i]->hdisplay;
 267                 SDL_modelist[i]->h = modes[i]->vdisplay;
 268             }
 269             SDL_modelist[i] = NULL;
 270         }
 271         XFree(modes);
 272 
 273         use_vidmode = 1;
 274         save_mode(this);
 275     }
 276 #endif /* XFREE86_VM */
 277 
 278     {
 279         static int depth_list[] = { 32, 24, 16, 15, 8 };
 280         int j, np;
 281         int use_directcolor = 1;
 282         XPixmapFormatValues *pf;
 283 
 284         /* Search for the visuals in deepest-first order, so that the first
 285            will be the richest one */
 286         if ( getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) {
 287                 use_directcolor = 0;
 288         }
 289         this->hidden->nvisuals = 0;
 290         if ( ! add_visual_byid(this, getenv("SDL_VIDEO_X11_VISUALID")) ) {
 291                 for ( i=0; i<SDL_TABLESIZE(depth_list); ++i ) {
 292                         if ( depth_list[i] > 8 ) {
 293                                 if ( use_directcolor ) {
 294                                         add_visual(this, depth_list[i], DirectColor);
 295                                 }
 296                                 add_visual(this, depth_list[i], TrueColor);
 297                         } else {
 298                                 add_visual(this, depth_list[i], PseudoColor);
 299                                 add_visual(this, depth_list[i], StaticColor);
 300                         }
 301                 }
 302         }
 303         if ( this->hidden->nvisuals == 0 ) {
 304             SDL_SetError("Found no sufficiently capable X11 visuals");
 305             return -1;
 306         }
 307             
 308         /* look up the pixel quantum for each depth */
 309         pf = XListPixmapFormats(SDL_Display, &np);
 310         for(i = 0; i < this->hidden->nvisuals; i++) {
 311             int d = this->hidden->visuals[i].depth;
 312             for(j = 0; j < np; j++)
 313                 if(pf[j].depth == d)
 314                     break;
 315             this->hidden->visuals[i].bpp = j < np ? pf[j].bits_per_pixel : d;
 316         }
 317 
 318         XFree(pf);
 319     }
 320 
 321     if ( SDL_modelist == NULL ) {
 322         SDL_modelist = (SDL_Rect **)malloc((1+1)*sizeof(SDL_Rect *));
 323         i = 0;
 324         if ( SDL_modelist ) {
 325             SDL_modelist[i] = (SDL_Rect *)malloc(sizeof(SDL_Rect));
 326             if ( SDL_modelist[i] ) {
 327                 SDL_modelist[i]->x = 0;
 328                 SDL_modelist[i]->y = 0;
 329                 SDL_modelist[i]->w = DisplayWidth(SDL_Display, SDL_Screen);
 330                 SDL_modelist[i]->h = DisplayHeight(SDL_Display, SDL_Screen);
 331                 ++i;
 332             }
 333             SDL_modelist[i] = NULL;
 334         }
 335     }
 336 
 337 #ifdef DEBUG_XF86
 338     if ( use_vidmode ) {
 339         fprintf(stderr, "XFree86 VidMode is enabled\n");
 340     }
 341     if ( SDL_modelist ) {
 342         fprintf(stderr, "X11 video mode list:\n");
 343         for ( i=0; SDL_modelist[i]; ++i ) {
 344             fprintf(stderr, "\t%dx%d\n",
 345                 SDL_modelist[i]->w, SDL_modelist[i]->h);
 346         }
 347     }
 348 #endif /* DEBUG_XF86 */
 349     return 0;
 350 }
 351 
 352 int X11_SupportedVisual(_THIS, SDL_PixelFormat *format)
     /* [<][>][^][v][top][bottom][index][help] */
 353 {
 354     int i;
 355     for(i = 0; i < this->hidden->nvisuals; i++)
 356         if(this->hidden->visuals[i].bpp == format->BitsPerPixel)
 357             return 1;
 358     return 0;
 359 }
 360 
 361 SDL_Rect **X11_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
     /* [<][>][^][v][top][bottom][index][help] */
 362 {
 363     if ( X11_SupportedVisual(this, format) ) {
 364         if ( flags & SDL_FULLSCREEN ) {
 365             return(SDL_modelist);
 366         } else {
 367             return((SDL_Rect **)-1);
 368         }
 369     } else {
 370         return((SDL_Rect **)0);
 371     }
 372 }
 373 
 374 void X11_FreeVideoModes(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 375 {
 376     int i;
 377 
 378     if ( SDL_modelist ) {
 379         for ( i=0; SDL_modelist[i]; ++i ) {
 380             free(SDL_modelist[i]);
 381         }
 382         free(SDL_modelist);
 383         SDL_modelist = NULL;
 384     }
 385 }
 386 
 387 int X11_ResizeFullScreen(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 388 {
 389     int x, y;
 390     int real_w, real_h;
 391 
 392     if ( currently_fullscreen ) {
 393         /* Switch resolution and cover it with the FSwindow */
 394         move_cursor_to(this, 0, 0);
 395         set_best_resolution(this, current_w, current_h);
 396         move_cursor_to(this, 0, 0);
 397         get_real_resolution(this, &real_w, &real_h);
 398         XResizeWindow(SDL_Display, FSwindow, real_w, real_h);
 399         move_cursor_to(this, real_w/2, real_h/2);
 400 
 401         /* Center and reparent the drawing window */
 402         x = (real_w - current_w)/2;
 403         y = (real_h - current_h)/2;
 404         XReparentWindow(SDL_Display, SDL_Window, FSwindow, x, y);
 405         /* FIXME: move the mouse to the old relative location */
 406         XSync(SDL_Display, True);   /* Flush spurious mode change events */
 407     }
 408     return(1);
 409 }
 410 
 411 void X11_QueueEnterFullScreen(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 412 {
 413     switch_waiting = 0x01 | SDL_FULLSCREEN;
 414     switch_time = SDL_GetTicks() + 1500;
 415 #if 0 /* This causes a BadMatch error if the window is iconified (not needed) */
 416     XSetInputFocus(SDL_Display, WMwindow, RevertToNone, CurrentTime);
 417 #endif
 418 }
 419 
 420 int X11_EnterFullScreen(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 421 {
 422     int okay;
 423 #if 0
 424     Window tmpwin, *windows;
 425     int i, nwindows;
 426 #endif
 427 
 428     okay = 1;
 429     if ( ! currently_fullscreen ) {
 430         int real_w, real_h;
 431 
 432         /* Map the fullscreen window to blank the screen */
 433         get_real_resolution(this, &real_w, &real_h);
 434         XResizeWindow(SDL_Display, FSwindow, real_w, real_h);
 435         XMapRaised(SDL_Display, FSwindow);
 436         X11_WaitMapped(this, FSwindow);
 437 
 438 #if 0 /* This seems to break WindowMaker in focus-follows-mouse mode */
 439         /* Make sure we got to the top of the window stack */
 440         if ( XQueryTree(SDL_Display, SDL_Root, &tmpwin, &tmpwin,
 441                                 &windows, &nwindows) && windows ) {
 442             /* If not, try to put us there - if fail... oh well */
 443             if ( windows[nwindows-1] != FSwindow ) {
 444                 tmpwin = windows[nwindows-1];
 445                 for ( i=0; i<nwindows; ++i ) {
 446                     if ( windows[i] == FSwindow ) {
 447                         memcpy(&windows[i], &windows[i+1],
 448                                (nwindows-i-1)*sizeof(windows[i]));
 449                         break;
 450                     }
 451                 }
 452                 windows[nwindows-1] = FSwindow;
 453                 XRestackWindows(SDL_Display, windows, nwindows);
 454                 XSync(SDL_Display, False);
 455             }
 456             XFree(windows);
 457         }
 458 #else
 459         XRaiseWindow(SDL_Display, FSwindow);
 460 #endif
 461 
 462         /* Grab the mouse on the fullscreen window
 463            The event handling will know when we become active, and then
 464            enter fullscreen mode if we can't grab the mouse this time.
 465          */
 466 #ifdef GRAB_FULLSCREEN
 467         if ( (XGrabPointer(SDL_Display, FSwindow, True, 0,
 468                           GrabModeAsync, GrabModeAsync,
 469                           FSwindow, None, CurrentTime) != GrabSuccess) ||
 470              (XGrabKeyboard(SDL_Display, WMwindow, True,
 471                           GrabModeAsync, GrabModeAsync, CurrentTime) != 0) ) {
 472 #else
 473         if ( XGrabPointer(SDL_Display, FSwindow, True, 0,
 474                           GrabModeAsync, GrabModeAsync,
 475                           FSwindow, None, CurrentTime) != GrabSuccess ) {
 476 #endif
 477             /* We lost the grab, so try again later */
 478             XUnmapWindow(SDL_Display, FSwindow);
 479             X11_WaitUnmapped(this, FSwindow);
 480             X11_QueueEnterFullScreen(this);
 481             return(0);
 482         }
 483 #ifdef GRAB_FULLSCREEN
 484         SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS);
 485 #endif
 486 
 487 #ifdef XFREE86_VM
 488         /* Save the current video mode */
 489         if ( use_vidmode ) {
 490             XVidMode(LockModeSwitch, (SDL_Display, SDL_Screen, True));
 491         }
 492 #endif
 493         currently_fullscreen = 1;
 494 
 495         /* Set the new resolution */
 496         okay = X11_ResizeFullScreen(this);
 497         if ( ! okay ) {
 498             X11_LeaveFullScreen(this);
 499         }
 500         /* Set the colormap */
 501         if ( SDL_XColorMap ) {
 502                 XInstallColormap(SDL_Display, SDL_XColorMap);
 503         }
 504     }
 505     X11_GrabInputNoLock(this, this->input_grab | SDL_GRAB_FULLSCREEN);
 506     return(okay);
 507 }
 508 
 509 int X11_LeaveFullScreen(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 510 {
 511     if ( currently_fullscreen ) {
 512         XReparentWindow(SDL_Display, SDL_Window, WMwindow, 0, 0);
 513 #ifdef XFREE86_VM
 514         if ( use_vidmode ) {
 515             restore_mode(this);
 516             XVidMode(LockModeSwitch, (SDL_Display, SDL_Screen, False));
 517         }
 518 #endif
 519         XUnmapWindow(SDL_Display, FSwindow);
 520         X11_WaitUnmapped(this, FSwindow);
 521 #ifdef GRAB_FULLSCREEN
 522         XUngrabKeyboard(SDL_Display, CurrentTime);
 523 #endif
 524         XSync(SDL_Display, True);   /* Flush spurious mode change events */
 525         currently_fullscreen = 0;
 526     }
 527     /* If we get popped out of fullscreen mode for some reason, input_grab
 528        will still have the SDL_GRAB_FULLSCREEN flag set, since this is only
 529        temporary.  In this case, release the grab unless the input has been
 530        explicitly grabbed.
 531      */
 532     X11_GrabInputNoLock(this, this->input_grab & ~SDL_GRAB_FULLSCREEN);
 533     return(0);
 534 }

/* [<][>][^][v][top][bottom][index][help] */