src/video/x11/SDL_x11modes.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 28 Sep 2012 11:51:16 -0700
changeset 6502 f41a82de351e
parent 6482 94e3643928ed
child 6506 305f0fcc0e99
permissions -rw-r--r--
Fixed detection of display bounds after a mode switch when Xinerama is enabled.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_x11video.h"
    27 
    28 /*#define X11MODES_DEBUG*/
    29 
    30 static int
    31 get_visualinfo(Display * display, int screen, XVisualInfo * vinfo)
    32 {
    33     const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID");
    34     int depth;
    35 
    36     /* Look for an exact visual, if requested */
    37     if (visual_id) {
    38         XVisualInfo *vi, template;
    39         int nvis;
    40 
    41         SDL_zero(template);
    42         template.visualid = SDL_strtol(visual_id, NULL, 0);
    43         vi = XGetVisualInfo(display, VisualIDMask, &template, &nvis);
    44         if (vi) {
    45             *vinfo = *vi;
    46             XFree(vi);
    47             return 0;
    48         }
    49     }
    50 
    51     depth = DefaultDepth(display, screen);
    52     if ((X11_UseDirectColorVisuals() &&
    53          XMatchVisualInfo(display, screen, depth, DirectColor, vinfo)) ||
    54         XMatchVisualInfo(display, screen, depth, TrueColor, vinfo) ||
    55         XMatchVisualInfo(display, screen, depth, PseudoColor, vinfo) ||
    56         XMatchVisualInfo(display, screen, depth, StaticColor, vinfo)) {
    57         return 0;
    58     }
    59     return -1;
    60 }
    61 
    62 int
    63 X11_GetVisualInfoFromVisual(Display * display, Visual * visual, XVisualInfo * vinfo)
    64 {
    65     XVisualInfo *vi;
    66     int nvis;
    67 
    68     vinfo->visualid = XVisualIDFromVisual(visual);
    69     vi = XGetVisualInfo(display, VisualIDMask, vinfo, &nvis);
    70     if (vi) {
    71         *vinfo = *vi;
    72         XFree(vi);
    73         return 0;
    74     }
    75     return -1;
    76 }
    77 
    78 Uint32
    79 X11_GetPixelFormatFromVisualInfo(Display * display, XVisualInfo * vinfo)
    80 {
    81     if (vinfo->class == DirectColor || vinfo->class == TrueColor) {
    82         int bpp;
    83         Uint32 Rmask, Gmask, Bmask, Amask;
    84 
    85         Rmask = vinfo->visual->red_mask;
    86         Gmask = vinfo->visual->green_mask;
    87         Bmask = vinfo->visual->blue_mask;
    88         if (vinfo->depth == 32) {
    89             Amask = (0xFFFFFFFF & ~(Rmask | Gmask | Bmask));
    90         } else {
    91             Amask = 0;
    92         }
    93 
    94         bpp = vinfo->depth;
    95         if (bpp == 24) {
    96             int i, n;
    97             XPixmapFormatValues *p = XListPixmapFormats(display, &n);
    98             if (p) {
    99                 for (i = 0; i < n; ++i) {
   100                     if (p[i].depth == 24) {
   101                         bpp = p[i].bits_per_pixel;
   102                         break;
   103                     }
   104                 }
   105                 XFree(p);
   106             }
   107         }
   108 
   109         return SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
   110     }
   111 
   112     if (vinfo->class == PseudoColor || vinfo->class == StaticColor) {
   113         switch (vinfo->depth) {
   114         case 8:
   115             return SDL_PIXELTYPE_INDEX8;
   116         case 4:
   117             if (BitmapBitOrder(display) == LSBFirst) {
   118                 return SDL_PIXELFORMAT_INDEX4LSB;
   119             } else {
   120                 return SDL_PIXELFORMAT_INDEX4MSB;
   121             }
   122             break;
   123         case 1:
   124             if (BitmapBitOrder(display) == LSBFirst) {
   125                 return SDL_PIXELFORMAT_INDEX1LSB;
   126             } else {
   127                 return SDL_PIXELFORMAT_INDEX1MSB;
   128             }
   129             break;
   130         }
   131     }
   132 
   133     return SDL_PIXELFORMAT_UNKNOWN;
   134 }
   135 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   136 static SDL_bool CheckXinerama(Display * display, int *major, int *minor);
   137 #endif
   138 
   139 int
   140 X11_InitModes(_THIS)
   141 {
   142     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   143     int screen, screencount;
   144 
   145 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   146     int xinerama_major, xinerama_minor;
   147     XineramaScreenInfo * xinerama = NULL;
   148     /* Query Xinerama extention
   149      * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
   150      *       or newer of the Nvidia binary drivers
   151      */
   152     if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
   153         xinerama = XineramaQueryScreens(data->display, &screencount);
   154     }
   155     if (!xinerama) {
   156         screencount = ScreenCount(data->display);
   157     }
   158 #else
   159     screencount = ScreenCount(data->display);
   160 #endif
   161 
   162     for (screen = 0; screen < screencount; ++screen) {
   163         XVisualInfo vinfo;
   164         SDL_VideoDisplay display;
   165         SDL_DisplayData *displaydata;
   166         SDL_DisplayMode mode;
   167         XPixmapFormatValues *pixmapFormats;
   168         int i, n;
   169 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   170         if (xinerama) {
   171             if (get_visualinfo(data->display, 0, &vinfo) < 0) {
   172                 continue;
   173             }
   174         }
   175         else {
   176             if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   177                 continue;
   178             }
   179         }
   180 #else
   181         if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   182             continue;
   183         }
   184 #endif
   185 
   186         mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
   187         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   188             /* We don't support palettized modes now */
   189             continue;
   190         }
   191 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   192         if (xinerama) {
   193             mode.w = xinerama[screen].width;
   194             mode.h = xinerama[screen].height;
   195         }
   196         else {
   197             mode.w = DisplayWidth(data->display, screen);
   198             mode.h = DisplayHeight(data->display, screen);
   199         }
   200 #else
   201         mode.w = DisplayWidth(data->display, screen);
   202         mode.h = DisplayHeight(data->display, screen);
   203 #endif
   204         mode.refresh_rate = 0;
   205         mode.driverdata = NULL;
   206 
   207         displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   208         if (!displaydata) {
   209             continue;
   210         }
   211 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   212         /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
   213          * there's only one screen available. So we force the screen number to zero and
   214          * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
   215          */
   216         if (xinerama) {
   217             displaydata->screen = 0;
   218             displaydata->use_xinerama = xinerama_major * 100 + xinerama_minor;
   219             displaydata->xinerama_info = xinerama[screen];
   220         }
   221         else displaydata->screen = screen;
   222 #else
   223         displaydata->screen = screen;
   224 #endif
   225         displaydata->visual = vinfo.visual;
   226         displaydata->depth = vinfo.depth;
   227 
   228         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   229         pixmapFormats = XListPixmapFormats(data->display, &n);
   230         if (pixmapFormats) {
   231             for (i = 0; i < n; ++i) {
   232                 if (pixmapFormats[i].depth == displaydata->depth) {
   233                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   234                     break;
   235                 }
   236             }
   237             XFree(pixmapFormats);
   238         }
   239 
   240         SDL_zero(display);
   241         display.desktop_mode = mode;
   242         display.current_mode = mode;
   243         display.driverdata = displaydata;
   244         SDL_AddVideoDisplay(&display);
   245     }
   246 
   247 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   248     if (xinerama) XFree(xinerama);
   249 #endif
   250 
   251     if (_this->num_displays == 0) {
   252         SDL_SetError("No available displays");
   253         return -1;
   254     }
   255     return 0;
   256 }
   257 
   258 /* Global for the error handler */
   259 int vm_event, vm_error = -1;
   260 
   261 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   262 static SDL_bool
   263 CheckXinerama(Display * display, int *major, int *minor)
   264 {
   265     int event_base = 0;
   266     int error_base = 0;
   267     const char *env;
   268 
   269     /* Default the extension not available */
   270     *major = *minor = 0;
   271 
   272     /* Allow environment override */
   273     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XINERAMA);
   274     if (env && !SDL_atoi(env)) {
   275 #ifdef X11MODES_DEBUG
   276         printf("Xinerama disabled due to hint\n");
   277 #endif
   278         return SDL_FALSE;
   279     }
   280 
   281     if (!SDL_X11_HAVE_XINERAMA) {
   282 #ifdef X11MODES_DEBUG
   283         printf("Xinerama support not available\n");
   284 #endif
   285         return SDL_FALSE;
   286     }
   287 
   288     /* Query the extension version */
   289     if (!XineramaQueryExtension(display, &event_base, &error_base) ||
   290         !XineramaQueryVersion(display, major, minor) ||
   291         !XineramaIsActive(display)) {
   292 #ifdef X11MODES_DEBUG
   293         printf("Xinerama not active on the display\n");
   294 #endif
   295         return SDL_FALSE;
   296     }
   297 #ifdef X11MODES_DEBUG
   298     printf("Xinerama available!\n");
   299 #endif
   300     return SDL_TRUE;
   301 }
   302 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   303 
   304 #if SDL_VIDEO_DRIVER_X11_XRANDR
   305 static SDL_bool
   306 CheckXRandR(Display * display, int *major, int *minor)
   307 {
   308     const char *env;
   309 
   310     /* Default the extension not available */
   311     *major = *minor = 0;
   312 
   313     /* Allow environment override */
   314     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XRANDR);
   315     if (env && !SDL_atoi(env)) {
   316 #ifdef X11MODES_DEBUG
   317         printf("XRandR disabled due to hint\n");
   318 #endif
   319         return SDL_FALSE;
   320     }
   321 
   322     if (!SDL_X11_HAVE_XRANDR) {
   323 #ifdef X11MODES_DEBUG
   324         printf("XRandR support not available\n");
   325 #endif
   326         return SDL_FALSE;
   327     }
   328 
   329     /* Query the extension version */
   330     if (!XRRQueryVersion(display, major, minor)) {
   331 #ifdef X11MODES_DEBUG
   332         printf("XRandR not active on the display\n");
   333 #endif
   334         return SDL_FALSE;
   335     }
   336 #ifdef X11MODES_DEBUG
   337     printf("XRandR available!\n");
   338 #endif
   339     return SDL_TRUE;
   340 }
   341 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   342 
   343 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   344 static SDL_bool
   345 CheckVidMode(Display * display, int *major, int *minor)
   346 {
   347     const char *env;
   348 
   349     /* Default the extension not available */
   350     *major = *minor = 0;
   351 
   352     /* Allow environment override */
   353     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XVIDMODE);
   354     if (env && !SDL_atoi(env)) {
   355 #ifdef X11MODES_DEBUG
   356         printf("XVidMode disabled due to hint\n");
   357 #endif
   358         return SDL_FALSE;
   359     }
   360 
   361     if (!SDL_X11_HAVE_XVIDMODE) {
   362 #ifdef X11MODES_DEBUG
   363         printf("XVidMode support not available\n");
   364 #endif
   365         return SDL_FALSE;
   366     }
   367 
   368     /* Query the extension version */
   369     vm_error = -1;
   370     if (!XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   371         || !XF86VidModeQueryVersion(display, major, minor)) {
   372 #ifdef X11MODES_DEBUG
   373         printf("XVidMode not active on the display\n");
   374 #endif
   375         return SDL_FALSE;
   376     }
   377 #ifdef X11MODES_DEBUG
   378     printf("XVidMode available!\n");
   379 #endif
   380     return SDL_TRUE;
   381 }
   382 
   383 static
   384 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   385                                        XF86VidModeModeInfo* info)
   386 {
   387     Bool retval;
   388     int dotclock;
   389     XF86VidModeModeLine l;
   390     SDL_zerop(info);
   391     SDL_zero(l);
   392     retval = XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   393     info->dotclock = dotclock;
   394     info->hdisplay = l.hdisplay;
   395     info->hsyncstart = l.hsyncstart;
   396     info->hsyncend = l.hsyncend;
   397     info->htotal = l.htotal;
   398     info->hskew = l.hskew;
   399     info->vdisplay = l.vdisplay;
   400     info->vsyncstart = l.vsyncstart;
   401     info->vsyncend = l.vsyncend;
   402     info->vtotal = l.vtotal;
   403     info->flags = l.flags;
   404     info->privsize = l.privsize;
   405     info->private = l.private;
   406     return retval;
   407 }
   408 
   409 static int
   410 calculate_rate(XF86VidModeModeInfo * info)
   411 {
   412     return (info->htotal
   413             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   414                                                          info->vtotal)) : 0;
   415 }
   416 
   417 static void
   418 save_mode(Display * display, SDL_DisplayData * data)
   419 {
   420     XF86VidModeGetModeInfo(display, data->screen,
   421                                     &data->saved_mode);
   422     XF86VidModeGetViewPort(display, data->screen,
   423                                     &data->saved_view.x,
   424                                     &data->saved_view.y);
   425 }
   426 
   427 /*
   428 static void
   429 restore_mode(Display * display, SDL_DisplayData * data)
   430 {
   431     XF86VidModeModeInfo mode;
   432 
   433     if (XF86VidModeGetModeInfo(display, data->screen, &mode)) {
   434         if (SDL_memcmp(&mode, &data->saved_mode, sizeof(mode)) != 0) {
   435             XF86VidModeSwitchToMode(display, data->screen, &data->saved_mode);
   436         }
   437     }
   438     if ((data->saved_view.x != 0) || (data->saved_view.y != 0)) {
   439         XF86VidModeSetViewPort(display, data->screen,
   440                                         data->saved_view.x,
   441                                         data->saved_view.y);
   442     }
   443 }
   444 */
   445 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   446 
   447 void
   448 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   449 {
   450     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   451     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   452 #if SDL_VIDEO_DRIVER_X11_XRANDR
   453     int xrandr_major, xrandr_minor;
   454     int nsizes, nrates;
   455     XRRScreenSize *sizes;
   456     short *rates;
   457 #endif
   458 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   459     int vm_major, vm_minor;
   460     int nmodes;
   461     XF86VidModeModeInfo ** modes;
   462 #endif
   463     int screen_w;
   464     int screen_h;
   465     SDL_DisplayMode mode;
   466 
   467     /* Unfortunately X11 requires the window to be created with the correct
   468      * visual and depth ahead of time, but the SDL API allows you to create
   469      * a window before setting the fullscreen display mode.  This means that
   470      * we have to use the same format for all windows and all display modes.
   471      * (or support recreating the window with a new visual behind the scenes)
   472      */
   473     mode.format = sdl_display->current_mode.format;
   474     mode.driverdata = NULL;
   475 
   476     screen_w = DisplayWidth(display, data->screen);
   477     screen_h = DisplayHeight(display, data->screen);
   478 
   479 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   480     if (data->use_xinerama) {
   481         /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0 */
   482         if (!data->xinerama_info.x_org && !data->xinerama_info.y_org &&
   483            (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) {
   484             mode.w = screen_w;
   485             mode.h = screen_h;
   486             mode.refresh_rate = 0;
   487             SDL_AddDisplayMode(sdl_display, &mode);
   488         }
   489 
   490         /* Add the head xinerama mode */
   491         mode.w = data->xinerama_info.width;
   492         mode.h = data->xinerama_info.height;
   493         mode.refresh_rate = 0;
   494         SDL_AddDisplayMode(sdl_display, &mode);
   495     }
   496 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   497 
   498 #if SDL_VIDEO_DRIVER_X11_XRANDR
   499     /* XRandR */
   500     /* require at least XRandR v1.0 (arbitrary) */
   501     if (CheckXRandR(display, &xrandr_major, &xrandr_minor)
   502         && xrandr_major >= 1) {
   503 #ifdef X11MODES_DEBUG
   504         fprintf(stderr, "XRANDR: XRRQueryVersion: V%d.%d\n",
   505                 xrandr_major, xrandr_minor);
   506 #endif
   507 
   508         /* save the screen configuration since we must reference it
   509            each time we toggle modes.
   510          */
   511         data->screen_config =
   512             XRRGetScreenInfo(display, RootWindow(display, data->screen));
   513 
   514         /* retrieve the list of resolution */
   515         sizes = XRRConfigSizes(data->screen_config, &nsizes);
   516         if (nsizes > 0) {
   517             int i, j;
   518             for (i = 0; i < nsizes; i++) {
   519                 mode.w = sizes[i].width;
   520                 mode.h = sizes[i].height;
   521 
   522                 rates = XRRConfigRates(data->screen_config, i, &nrates);
   523                 for (j = 0; j < nrates; ++j) {
   524                     mode.refresh_rate = rates[j];
   525 #ifdef X11MODES_DEBUG
   526                     fprintf(stderr,
   527                             "XRANDR: mode = %4d[%d], w = %4d, h = %4d, rate = %4d\n",
   528                             i, j, mode.w, mode.h, mode.refresh_rate);
   529 #endif
   530                     SDL_AddDisplayMode(sdl_display, &mode);
   531                 }
   532             }
   533 
   534             data->use_xrandr = xrandr_major * 100 + xrandr_minor;
   535             data->saved_size =
   536                 XRRConfigCurrentConfiguration(data->screen_config,
   537                                               &data->saved_rotation);
   538             data->saved_rate = XRRConfigCurrentRate(data->screen_config);
   539         }
   540     }
   541 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   542 
   543 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   544     /* XVidMode */
   545     if (!data->use_xrandr &&
   546 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   547         (!data->use_xinerama || data->xinerama_info.screen_number == 0) &&
   548 #endif
   549         CheckVidMode(display, &vm_major, &vm_minor) &&
   550         XF86VidModeGetAllModeLines(display, data->screen, &nmodes, &modes)) {
   551         int i;
   552 
   553 #ifdef X11MODES_DEBUG
   554         printf("VidMode modes: (unsorted)\n");
   555         for (i = 0; i < nmodes; ++i) {
   556             printf("Mode %d: %d x %d @ %d\n", i,
   557                    modes[i]->hdisplay, modes[i]->vdisplay,
   558                    calculate_rate(modes[i]));
   559         }
   560 #endif
   561         for (i = 0; i < nmodes; ++i) {
   562             mode.w = modes[i]->hdisplay;
   563             mode.h = modes[i]->vdisplay;
   564             mode.refresh_rate = calculate_rate(modes[i]);
   565             SDL_AddDisplayMode(sdl_display, &mode);
   566         }
   567         XFree(modes);
   568 
   569         data->use_vidmode = vm_major * 100 + vm_minor;
   570         save_mode(display, data);
   571     }
   572 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   573 
   574     if (!data->use_xrandr && !data->use_vidmode) {
   575         mode.w = screen_w;
   576         mode.h = screen_h;
   577         mode.refresh_rate = 0;
   578         SDL_AddDisplayMode(sdl_display, &mode);
   579     }
   580 #ifdef X11MODES_DEBUG
   581     if (data->use_xinerama) {
   582         printf("Xinerama is enabled\n");
   583     }
   584 
   585     if (data->use_xrandr) {
   586         printf("XRandR is enabled\n");
   587     }
   588 
   589     if (data->use_vidmode) {
   590         printf("VidMode is enabled\n");
   591     }
   592 #endif /* X11MODES_DEBUG */
   593 }
   594 
   595 static void
   596 get_real_resolution(Display * display, SDL_DisplayData * data, int *w, int *h,
   597                     int *rate)
   598 {
   599 #if SDL_VIDEO_DRIVER_X11_XRANDR
   600     if (data->use_xrandr) {
   601         int nsizes;
   602         XRRScreenSize *sizes;
   603 
   604         sizes = XRRConfigSizes(data->screen_config, &nsizes);
   605         if (nsizes > 0) {
   606             int cur_size;
   607             Rotation cur_rotation;
   608 
   609             cur_size =
   610                 XRRConfigCurrentConfiguration(data->screen_config,
   611                                               &cur_rotation);
   612             *w = sizes[cur_size].width;
   613             *h = sizes[cur_size].height;
   614             *rate = XRRConfigCurrentRate(data->screen_config);
   615 #ifdef X11MODES_DEBUG
   616             fprintf(stderr,
   617                     "XRANDR: get_real_resolution: w = %d, h = %d, rate = %d\n",
   618                     *w, *h, *rate);
   619 #endif
   620             return;
   621         }
   622     }
   623 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   624 
   625 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   626     if (data->use_vidmode) {
   627         XF86VidModeModeInfo mode;
   628 
   629         if (XF86VidModeGetModeInfo(display, data->screen, &mode)) {
   630             *w = mode.hdisplay;
   631             *h = mode.vdisplay;
   632             *rate = calculate_rate(&mode);
   633             return;
   634         }
   635     }
   636 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   637 
   638 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   639     if (data->use_xinerama) {
   640         int screencount;
   641         XineramaScreenInfo *xinerama;
   642 
   643         /* Update the current screen layout information */
   644         xinerama = XineramaQueryScreens(display, &screencount);
   645         if (xinerama && data->screen < screencount) {
   646             data->xinerama_info = xinerama[data->screen];
   647         }
   648         if (xinerama) XFree(xinerama);
   649 
   650         *w = data->xinerama_info.width;
   651         *h = data->xinerama_info.height;
   652         *rate = 0;
   653         return;
   654     }
   655 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   656 
   657     *w = DisplayWidth(display, data->screen);
   658     *h = DisplayHeight(display, data->screen);
   659     *rate = 0;
   660 }
   661 
   662 static void
   663 set_best_resolution(Display * display, SDL_DisplayData * data, int w, int h,
   664                     int rate)
   665 {
   666     int real_w, real_h, real_rate;
   667 
   668     /* check current mode so we can avoid uneccessary mode changes */
   669     get_real_resolution(display, data, &real_w, &real_h, &real_rate);
   670 
   671     if (w == real_w && h == real_h && (!rate || !real_rate || rate == real_rate)) {
   672         return;
   673     }
   674 
   675 #if SDL_VIDEO_DRIVER_X11_XRANDR
   676     if (data->use_xrandr) {
   677 #ifdef X11MODES_DEBUG
   678         fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n",
   679                 w, h);
   680 #endif
   681         int i, nsizes, nrates;
   682         int best;
   683         int best_rate;
   684         XRRScreenSize *sizes;
   685         short *rates;
   686 
   687         /* find the smallest resolution that is at least as big as the user requested */
   688         best = -1;
   689         sizes = XRRConfigSizes(data->screen_config, &nsizes);
   690         for (i = 0; i < nsizes; ++i) {
   691             if (sizes[i].width < w || sizes[i].height < h) {
   692                 continue;
   693             }
   694             if (sizes[i].width == w && sizes[i].height == h) {
   695                 best = i;
   696                 break;
   697             }
   698             if (best == -1 ||
   699                 (sizes[i].width < sizes[best].width) ||
   700                 (sizes[i].width == sizes[best].width
   701                  && sizes[i].height < sizes[best].height)) {
   702                 best = i;
   703             }
   704         }
   705 
   706         if (best >= 0) {
   707             best_rate = 0;
   708             rates = XRRConfigRates(data->screen_config, best, &nrates);
   709             for (i = 0; i < nrates; ++i) {
   710                 if (rates[i] == rate) {
   711                     best_rate = rate;
   712                     break;
   713                 }
   714                 if (!rate) {
   715                     /* Higher is better, right? */
   716                     if (rates[i] > best_rate) {
   717                         best_rate = rates[i];
   718                     }
   719                 } else {
   720                     if (SDL_abs(rates[i] - rate) < SDL_abs(best_rate - rate)) {
   721                         best_rate = rates[i];
   722                     }
   723                 }
   724             }
   725             XRRSetScreenConfigAndRate(display, data->screen_config,
   726                                       RootWindow(display, data->screen), best,
   727                                       data->saved_rotation, best_rate,
   728                                       CurrentTime);
   729         }
   730         return;
   731     }
   732 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   733 
   734 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   735     if (data->use_vidmode) {
   736         XF86VidModeModeInfo ** modes;
   737         int i, nmodes;
   738         int best;
   739 
   740         if (XF86VidModeGetAllModeLines(display, data->screen, &nmodes, &modes)) {
   741             best = -1;
   742             for (i = 0; i < nmodes; ++i) {
   743                 if (modes[i]->hdisplay < w || modes[i]->vdisplay < h) {
   744                     continue;
   745                 }
   746                 if (best == -1 ||
   747                     (modes[i]->hdisplay < modes[best]->hdisplay) ||
   748                     (modes[i]->hdisplay == modes[best]->hdisplay
   749                      && modes[i]->vdisplay < modes[best]->vdisplay)) {
   750                     best = i;
   751                     continue;
   752                 }
   753                 if ((modes[i]->hdisplay == modes[best]->hdisplay) &&
   754                     (modes[i]->vdisplay == modes[best]->vdisplay)) {
   755                     if (!rate) {
   756                         /* Higher is better, right? */
   757                         if (calculate_rate(modes[i]) >
   758                             calculate_rate(modes[best])) {
   759                             best = i;
   760                         }
   761                     } else {
   762                         if (SDL_abs(calculate_rate(modes[i]) - rate) <
   763                             SDL_abs(calculate_rate(modes[best]) - rate)) {
   764                             best = i;
   765                         }
   766                     }
   767                 }
   768             }
   769             if (best >= 0) {
   770 #ifdef X11MODES_DEBUG
   771                 printf("Best Mode %d: %d x %d @ %d\n", best,
   772                        modes[best]->hdisplay, modes[best]->vdisplay,
   773                        calculate_rate(modes[best]));
   774 #endif
   775                 XF86VidModeSwitchToMode(display, data->screen, modes[best]);
   776             }
   777             XFree(modes);
   778         }
   779         return;
   780     }
   781 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   782 }
   783 
   784 int
   785 X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode)
   786 {
   787     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   788     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   789 
   790     set_best_resolution(display, data, mode->w, mode->h, mode->refresh_rate);
   791     return 0;
   792 }
   793 
   794 void
   795 X11_QuitModes(_THIS)
   796 {
   797 }
   798 
   799 int
   800 X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
   801 {
   802     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   803     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   804     int real_rate;
   805 
   806     rect->x = 0;
   807     rect->y = 0;
   808     get_real_resolution(display, data, &rect->w, &rect->h, &real_rate);
   809 
   810 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   811     if (data->use_xinerama) {
   812         rect->x = data->xinerama_info.x_org;
   813         rect->y = data->xinerama_info.y_org;
   814     }
   815 #endif
   816     return 0;
   817 }
   818 
   819 #endif /* SDL_VIDEO_DRIVER_X11 */
   820 
   821 /* vi: set ts=4 sw=4 expandtab: */