src/video/x11/SDL_x11modes.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 03 Oct 2012 11:33:09 -0700
changeset 6537 85cf169db434
parent 6506 305f0fcc0e99
child 6538 05cf19d1eb53
permissions -rw-r--r--
Fixed XRandR code to change resolution on the correct monitor in a multi-monitor setup.
     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             displaydata->xinerama_screen = screen;
   221         }
   222         else displaydata->screen = screen;
   223 #else
   224         displaydata->screen = screen;
   225 #endif
   226         displaydata->visual = vinfo.visual;
   227         displaydata->depth = vinfo.depth;
   228 
   229         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   230         pixmapFormats = XListPixmapFormats(data->display, &n);
   231         if (pixmapFormats) {
   232             for (i = 0; i < n; ++i) {
   233                 if (pixmapFormats[i].depth == displaydata->depth) {
   234                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   235                     break;
   236                 }
   237             }
   238             XFree(pixmapFormats);
   239         }
   240 
   241         SDL_zero(display);
   242         display.desktop_mode = mode;
   243         display.current_mode = mode;
   244         display.driverdata = displaydata;
   245         SDL_AddVideoDisplay(&display);
   246     }
   247 
   248 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   249     if (xinerama) XFree(xinerama);
   250 #endif
   251 
   252     if (_this->num_displays == 0) {
   253         SDL_SetError("No available displays");
   254         return -1;
   255     }
   256     return 0;
   257 }
   258 
   259 /* Global for the error handler */
   260 int vm_event, vm_error = -1;
   261 
   262 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   263 static SDL_bool
   264 CheckXinerama(Display * display, int *major, int *minor)
   265 {
   266     int event_base = 0;
   267     int error_base = 0;
   268     const char *env;
   269 
   270     /* Default the extension not available */
   271     *major = *minor = 0;
   272 
   273     /* Allow environment override */
   274     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XINERAMA);
   275     if (env && !SDL_atoi(env)) {
   276 #ifdef X11MODES_DEBUG
   277         printf("Xinerama disabled due to hint\n");
   278 #endif
   279         return SDL_FALSE;
   280     }
   281 
   282     if (!SDL_X11_HAVE_XINERAMA) {
   283 #ifdef X11MODES_DEBUG
   284         printf("Xinerama support not available\n");
   285 #endif
   286         return SDL_FALSE;
   287     }
   288 
   289     /* Query the extension version */
   290     if (!XineramaQueryExtension(display, &event_base, &error_base) ||
   291         !XineramaQueryVersion(display, major, minor) ||
   292         !XineramaIsActive(display)) {
   293 #ifdef X11MODES_DEBUG
   294         printf("Xinerama not active on the display\n");
   295 #endif
   296         return SDL_FALSE;
   297     }
   298 #ifdef X11MODES_DEBUG
   299     printf("Xinerama available!\n");
   300 #endif
   301     return SDL_TRUE;
   302 }
   303 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   304 
   305 #if SDL_VIDEO_DRIVER_X11_XRANDR
   306 static SDL_bool
   307 CheckXRandR(Display * display, int *major, int *minor)
   308 {
   309     const char *env;
   310 
   311     /* Default the extension not available */
   312     *major = *minor = 0;
   313 
   314     /* Allow environment override */
   315     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XRANDR);
   316     if (env && !SDL_atoi(env)) {
   317 #ifdef X11MODES_DEBUG
   318         printf("XRandR disabled due to hint\n");
   319 #endif
   320         return SDL_FALSE;
   321     }
   322 
   323     if (!SDL_X11_HAVE_XRANDR) {
   324 #ifdef X11MODES_DEBUG
   325         printf("XRandR support not available\n");
   326 #endif
   327         return SDL_FALSE;
   328     }
   329 
   330     /* Query the extension version */
   331     if (!XRRQueryVersion(display, major, minor)) {
   332 #ifdef X11MODES_DEBUG
   333         printf("XRandR not active on the display\n");
   334 #endif
   335         return SDL_FALSE;
   336     }
   337 #ifdef X11MODES_DEBUG
   338     printf("XRandR available!\n");
   339 #endif
   340     return SDL_TRUE;
   341 }
   342 
   343 #define XRANDR_ROTATION_LEFT    (1 << 1)
   344 #define XRANDR_ROTATION_RIGHT   (1 << 3)
   345 
   346 static void
   347 get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h)
   348 {
   349     if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) {
   350         *w = mode->height;
   351         *h = mode->width;
   352     } else {
   353         *w = mode->width;
   354         *h = mode->height;
   355     }
   356 }
   357 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   358 
   359 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   360 static SDL_bool
   361 CheckVidMode(Display * display, int *major, int *minor)
   362 {
   363     const char *env;
   364 
   365     /* Default the extension not available */
   366     *major = *minor = 0;
   367 
   368     /* Allow environment override */
   369     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XVIDMODE);
   370     if (env && !SDL_atoi(env)) {
   371 #ifdef X11MODES_DEBUG
   372         printf("XVidMode disabled due to hint\n");
   373 #endif
   374         return SDL_FALSE;
   375     }
   376 
   377     if (!SDL_X11_HAVE_XVIDMODE) {
   378 #ifdef X11MODES_DEBUG
   379         printf("XVidMode support not available\n");
   380 #endif
   381         return SDL_FALSE;
   382     }
   383 
   384     /* Query the extension version */
   385     vm_error = -1;
   386     if (!XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   387         || !XF86VidModeQueryVersion(display, major, minor)) {
   388 #ifdef X11MODES_DEBUG
   389         printf("XVidMode not active on the display\n");
   390 #endif
   391         return SDL_FALSE;
   392     }
   393 #ifdef X11MODES_DEBUG
   394     printf("XVidMode available!\n");
   395 #endif
   396     return SDL_TRUE;
   397 }
   398 
   399 static
   400 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   401                                        XF86VidModeModeInfo* info)
   402 {
   403     Bool retval;
   404     int dotclock;
   405     XF86VidModeModeLine l;
   406     SDL_zerop(info);
   407     SDL_zero(l);
   408     retval = XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   409     info->dotclock = dotclock;
   410     info->hdisplay = l.hdisplay;
   411     info->hsyncstart = l.hsyncstart;
   412     info->hsyncend = l.hsyncend;
   413     info->htotal = l.htotal;
   414     info->hskew = l.hskew;
   415     info->vdisplay = l.vdisplay;
   416     info->vsyncstart = l.vsyncstart;
   417     info->vsyncend = l.vsyncend;
   418     info->vtotal = l.vtotal;
   419     info->flags = l.flags;
   420     info->privsize = l.privsize;
   421     info->private = l.private;
   422     return retval;
   423 }
   424 
   425 static int
   426 calculate_rate(XF86VidModeModeInfo * info)
   427 {
   428     return (info->htotal
   429             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   430                                                          info->vtotal)) : 0;
   431 }
   432 
   433 static void
   434 save_mode(Display * display, SDL_DisplayData * data)
   435 {
   436     XF86VidModeGetModeInfo(display, data->screen,
   437                                     &data->saved_mode);
   438     XF86VidModeGetViewPort(display, data->screen,
   439                                     &data->saved_view.x,
   440                                     &data->saved_view.y);
   441 }
   442 
   443 /*
   444 static void
   445 restore_mode(Display * display, SDL_DisplayData * data)
   446 {
   447     XF86VidModeModeInfo mode;
   448 
   449     if (XF86VidModeGetModeInfo(display, data->screen, &mode)) {
   450         if (SDL_memcmp(&mode, &data->saved_mode, sizeof(mode)) != 0) {
   451             XF86VidModeSwitchToMode(display, data->screen, &data->saved_mode);
   452         }
   453     }
   454     if ((data->saved_view.x != 0) || (data->saved_view.y != 0)) {
   455         XF86VidModeSetViewPort(display, data->screen,
   456                                         data->saved_view.x,
   457                                         data->saved_view.y);
   458     }
   459 }
   460 */
   461 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   462 
   463 void
   464 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   465 {
   466     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   467     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   468 #if SDL_VIDEO_DRIVER_X11_XRANDR
   469     int xrandr_major, xrandr_minor;
   470     int nsizes, nrates;
   471     XRRScreenSize *sizes;
   472     short *rates;
   473 #endif
   474 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   475     int vm_major, vm_minor;
   476     int nmodes;
   477     XF86VidModeModeInfo ** modes;
   478 #endif
   479     int screen_w;
   480     int screen_h;
   481     SDL_DisplayMode mode;
   482 
   483     /* Unfortunately X11 requires the window to be created with the correct
   484      * visual and depth ahead of time, but the SDL API allows you to create
   485      * a window before setting the fullscreen display mode.  This means that
   486      * we have to use the same format for all windows and all display modes.
   487      * (or support recreating the window with a new visual behind the scenes)
   488      */
   489     mode.format = sdl_display->current_mode.format;
   490     mode.driverdata = NULL;
   491 
   492     screen_w = DisplayWidth(display, data->screen);
   493     screen_h = DisplayHeight(display, data->screen);
   494 
   495 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   496     if (data->use_xinerama) {
   497         /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0 */
   498         if (!data->xinerama_info.x_org && !data->xinerama_info.y_org &&
   499            (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) {
   500             mode.w = screen_w;
   501             mode.h = screen_h;
   502             mode.refresh_rate = 0;
   503             SDL_AddDisplayMode(sdl_display, &mode);
   504         }
   505 
   506         /* Add the head xinerama mode */
   507         mode.w = data->xinerama_info.width;
   508         mode.h = data->xinerama_info.height;
   509         mode.refresh_rate = 0;
   510         SDL_AddDisplayMode(sdl_display, &mode);
   511     }
   512 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   513 
   514 #if SDL_VIDEO_DRIVER_X11_XRANDR
   515     /* XRandR */
   516     /* require at least XRandR v1.0 (arbitrary) */
   517     if (CheckXRandR(display, &xrandr_major, &xrandr_minor)
   518         && xrandr_major >= 1) {
   519 #ifdef X11MODES_DEBUG
   520         fprintf(stderr, "XRANDR: XRRQueryVersion: V%d.%d\n",
   521                 xrandr_major, xrandr_minor);
   522 #endif
   523 
   524         /* save the screen configuration since we must reference it
   525            each time we toggle modes.
   526          */
   527         data->screen_config =
   528             XRRGetScreenInfo(display, RootWindow(display, data->screen));
   529 
   530         /* retrieve the list of resolution */
   531         sizes = XRRConfigSizes(data->screen_config, &nsizes);
   532         if (nsizes > 0) {
   533             int i, j;
   534             for (i = 0; i < nsizes; i++) {
   535                 get_xrandr_mode_size(XRRModeInfo *mode, Rotation rotation, int *w, int *h)
   536                 mode.w = sizes[i].width;
   537                 mode.h = sizes[i].height;
   538 
   539                 rates = XRRConfigRates(data->screen_config, i, &nrates);
   540                 for (j = 0; j < nrates; ++j) {
   541                     mode.refresh_rate = rates[j];
   542 #ifdef X11MODES_DEBUG
   543                     fprintf(stderr,
   544                             "XRANDR: mode = %4d[%d], w = %4d, h = %4d, rate = %4d\n",
   545                             i, j, mode.w, mode.h, mode.refresh_rate);
   546 #endif
   547                     SDL_AddDisplayMode(sdl_display, &mode);
   548                 }
   549             }
   550 
   551             data->use_xrandr = xrandr_major * 100 + xrandr_minor;
   552             data->saved_size =
   553                 XRRConfigCurrentConfiguration(data->screen_config,
   554                                               &data->saved_rotation);
   555             data->saved_rate = XRRConfigCurrentRate(data->screen_config);
   556         }
   557     }
   558 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   559 
   560 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   561     /* XVidMode */
   562     if (!data->use_xrandr &&
   563 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   564         (!data->use_xinerama || data->xinerama_info.screen_number == 0) &&
   565 #endif
   566         CheckVidMode(display, &vm_major, &vm_minor) &&
   567         XF86VidModeGetAllModeLines(display, data->screen, &nmodes, &modes)) {
   568         int i;
   569 
   570 #ifdef X11MODES_DEBUG
   571         printf("VidMode modes: (unsorted)\n");
   572         for (i = 0; i < nmodes; ++i) {
   573             printf("Mode %d: %d x %d @ %d\n", i,
   574                    modes[i]->hdisplay, modes[i]->vdisplay,
   575                    calculate_rate(modes[i]));
   576         }
   577 #endif
   578         for (i = 0; i < nmodes; ++i) {
   579             mode.w = modes[i]->hdisplay;
   580             mode.h = modes[i]->vdisplay;
   581             mode.refresh_rate = calculate_rate(modes[i]);
   582             SDL_AddDisplayMode(sdl_display, &mode);
   583         }
   584         XFree(modes);
   585 
   586         data->use_vidmode = vm_major * 100 + vm_minor;
   587         save_mode(display, data);
   588     }
   589 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   590 
   591     if (!data->use_xrandr && !data->use_vidmode) {
   592         mode.w = screen_w;
   593         mode.h = screen_h;
   594         mode.refresh_rate = 0;
   595         SDL_AddDisplayMode(sdl_display, &mode);
   596     }
   597 #ifdef X11MODES_DEBUG
   598     if (data->use_xinerama) {
   599         printf("Xinerama is enabled\n");
   600     }
   601 
   602     if (data->use_xrandr) {
   603         printf("XRandR is enabled\n");
   604     }
   605 
   606     if (data->use_vidmode) {
   607         printf("VidMode is enabled\n");
   608     }
   609 #endif /* X11MODES_DEBUG */
   610 }
   611 
   612 static void
   613 get_real_resolution(Display * display, SDL_DisplayData * data, int *w, int *h,
   614                     int *rate)
   615 {
   616 #if SDL_VIDEO_DRIVER_X11_XRANDR
   617     if (data->use_xrandr) {
   618         int nsizes;
   619         XRRScreenSize *sizes;
   620 
   621         sizes = XRRConfigSizes(data->screen_config, &nsizes);
   622         if (nsizes > 0) {
   623             int cur_size;
   624             Rotation cur_rotation;
   625 
   626             cur_size =
   627                 XRRConfigCurrentConfiguration(data->screen_config,
   628                                               &cur_rotation);
   629             *w = sizes[cur_size].width;
   630             *h = sizes[cur_size].height;
   631             *rate = XRRConfigCurrentRate(data->screen_config);
   632 #ifdef X11MODES_DEBUG
   633             fprintf(stderr,
   634                     "XRANDR: get_real_resolution: w = %d, h = %d, rate = %d\n",
   635                     *w, *h, *rate);
   636 #endif
   637             return;
   638         }
   639     }
   640 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   641 
   642 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   643     if (data->use_vidmode) {
   644         XF86VidModeModeInfo mode;
   645 
   646         if (XF86VidModeGetModeInfo(display, data->screen, &mode)) {
   647             *w = mode.hdisplay;
   648             *h = mode.vdisplay;
   649             *rate = calculate_rate(&mode);
   650             return;
   651         }
   652     }
   653 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   654 
   655 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   656     if (data->use_xinerama) {
   657         int screencount;
   658         XineramaScreenInfo *xinerama;
   659 
   660         /* Update the current screen layout information */
   661         xinerama = XineramaQueryScreens(display, &screencount);
   662         if (xinerama && data->xinerama_screen < screencount) {
   663             data->xinerama_info = xinerama[data->xinerama_screen];
   664         }
   665         if (xinerama) XFree(xinerama);
   666 
   667         *w = data->xinerama_info.width;
   668         *h = data->xinerama_info.height;
   669         *rate = 0;
   670         return;
   671     }
   672 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   673 
   674     *w = DisplayWidth(display, data->screen);
   675     *h = DisplayHeight(display, data->screen);
   676     *rate = 0;
   677 }
   678 
   679 static void
   680 set_best_resolution(Display * display, SDL_DisplayData * data, int w, int h,
   681                     int rate)
   682 {
   683     int real_w, real_h, real_rate;
   684 
   685     /* check current mode so we can avoid uneccessary mode changes */
   686     get_real_resolution(display, data, &real_w, &real_h, &real_rate);
   687 
   688     if (w == real_w && h == real_h && (!rate || !real_rate || rate == real_rate)) {
   689         return;
   690     }
   691 
   692 #if SDL_VIDEO_DRIVER_X11_XRANDR
   693     if (data->use_xrandr) {
   694 #ifdef X11MODES_DEBUG
   695         fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n",
   696                 w, h);
   697 #endif
   698         int i, nsizes, nrates;
   699         int best;
   700         int best_rate;
   701         XRRScreenSize *sizes;
   702         short *rates;
   703 
   704 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   705         if (data->use_xrandr >= 102 && data->use_xinerama) {
   706             /* See http://cgit.freedesktop.org/xorg/app/xrandr/tree/xrandr.c for the rationale behind this */
   707             /* Note by Gabriel: the refresh rate is ignored in this code, and seeing that both xrandr
   708              * and nvidia-settings don't provide a way to set it when Xinerama is enabled, it may not be possible to set it */
   709             int screencount;
   710             XineramaScreenInfo * xinerama = NULL;
   711             XRRScreenResources  *res;
   712 
   713             /* Update the current screen layout information */
   714             xinerama = XineramaQueryScreens(display, &screencount);
   715             if (xinerama && data->xinerama_screen < screencount) {
   716                 data->xinerama_info = xinerama[data->xinerama_screen];
   717             }
   718             if (xinerama) XFree(xinerama);
   719 
   720             res = XRRGetScreenResources (display, RootWindow(display, data->screen));
   721             if (res) {
   722                 XRROutputInfo *output_info = NULL;
   723                 XRRCrtcInfo *crtc = NULL;
   724                 XRRModeInfo *mode_info, *best_mode_info = NULL;
   725                 int mode_index = 0, output_mode_index = 0, output;
   726                 int mode_w, mode_h, best_w = 0, best_h = 0;
   727 
   728                 for ( output = 0; output < res->noutput; output++) {
   729                     output_info = XRRGetOutputInfo (display, res, res->outputs[output]);
   730                     if (!output_info || !output_info->crtc) {
   731                         XRRFreeOutputInfo(output_info);
   732                         continue;
   733                     }
   734 
   735                     crtc = XRRGetCrtcInfo (display, res, output_info->crtc);
   736                     if (!crtc || data->xinerama_info.x_org != crtc->x || data->xinerama_info.y_org != crtc->y) {
   737                         XRRFreeCrtcInfo(crtc);
   738                         continue;
   739                     }
   740 
   741                     /* The CRT offset matches the Xinerama mode, let's hope it's the right one! */
   742 #ifdef X11MODES_DEBUG
   743                     fprintf(stderr, "XRANDR: set_best_resolution, matched Xinerama screen %d to CRT %d at %d,%d\n",
   744                         data->xinerama_info.screen_number, output_info->crtc, crtc->x, crtc->y);
   745 #endif
   746                     /* Find out the best mode we can use */
   747                     for (mode_index = 0; mode_index < res->nmode; mode_index++) {
   748                         mode_info = &res->modes[mode_index];
   749                         get_xrandr_mode_size(mode_info, crtc->rotation, &mode_w, &mode_h);
   750                         if (mode_w >= w && mode_h >= h) {
   751                             /* This may be a useful mode, check out if it belongs to the correct output */
   752 #ifdef X11MODES_DEBUG
   753                             fprintf(stderr, "Evaluating valid mode %d, w: %d, h: %d\n", mode_index, mode_info->width, mode_info->height);
   754 #endif
   755                             for ( output_mode_index = 0; output_mode_index < output_info->nmode; output_mode_index++) {
   756                                 if (output_info->modes[output_mode_index] == mode_info->id) {
   757                                     break;
   758                                 }
   759                             }
   760                             if (output_mode_index < output_info->nmode) {
   761 #ifdef X11MODES_DEBUG
   762                                 fprintf(stderr, "Mode belongs to the desired output %d w: %d, h: %d, rotation: %x\n", mode_index, mode_info->width, mode_info->height, crtc->rotation);
   763 #endif
   764                                 /* We have a mode that belongs to the right output and that can contain the w,h required, see if it's smaller than the current one */
   765                                 if ( !best_mode_info || mode_w < best_w || (mode_w == best_w && mode_h < best_h) ) {
   766                                     best_mode_info = mode_info;
   767                                     best_w = mode_w;
   768                                     best_h = mode_h;
   769                                 }
   770                             }
   771 #ifdef X11MODES_DEBUG
   772                             else {
   773                                 fprintf(stderr, "Discarding mode because it does not belong to the desired output %d, w: %d, h: %d, rotation: %d\n", mode_index, mode_info->width, mode_info->height, crtc->rotation);
   774                             }
   775 #endif
   776                         }
   777                     }
   778 
   779                     if (best_mode_info) {
   780 #ifdef X11MODES_DEBUG
   781                             fprintf(stderr, "XRANDR: set_best_resolution, setting mode w = %d, h = %d on output: %d, CRT with offsets: %d,%d for Xinerama screen: %d\n",
   782                                     best_mode_info->width, best_mode_info->height, output, crtc->x, crtc->y, data->xinerama_info.screen_number);
   783 #endif
   784                         XGrabServer (display);
   785                         XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime,
   786                           crtc->x, crtc->y, best_mode_info->id, crtc->rotation,
   787                           &res->outputs[output], 1);
   788                         /* TODO: Handle screen rotations, should we call XRRSetCrtcTransform if Xrandr >=1.3 ? */
   789                         XUngrabServer (display);
   790                     } else {
   791                         /* If we reach here, we have found the right screen but no valid best mode */
   792                         SDL_SetError("The selected screen can't support a resolution of the size desired");
   793                     }
   794                     XRRFreeCrtcInfo(crtc);
   795                     XRRFreeOutputInfo(output_info);
   796                     break;
   797                 }
   798                 XRRFreeScreenResources(res);
   799             }
   800         }
   801         else
   802 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   803         {
   804             /* Use older xrandr functions that don't play along very nicely with multi monitors setups */
   805             /* find the smallest resolution that is at least as big as the user requested */
   806             best = -1;
   807             sizes = XRRConfigSizes(data->screen_config, &nsizes);
   808             for (i = 0; i < nsizes; ++i) {
   809                 if (sizes[i].width < w || sizes[i].height < h) {
   810                     continue;
   811                 }
   812                 if (sizes[i].width == w && sizes[i].height == h) {
   813                     best = i;
   814                     break;
   815                 }
   816                 if (best == -1 ||
   817                     (sizes[i].width < sizes[best].width) ||
   818                     (sizes[i].width == sizes[best].width
   819                      && sizes[i].height < sizes[best].height)) {
   820                     best = i;
   821                 }
   822             }
   823 
   824             if (best >= 0) {
   825                 best_rate = 0;
   826                 rates = XRRConfigRates(data->screen_config, best, &nrates);
   827                 for (i = 0; i < nrates; ++i) {
   828                     if (rates[i] == rate) {
   829                         best_rate = rate;
   830                         break;
   831                     }
   832                     if (!rate) {
   833                         /* Higher is better, right? */
   834                         if (rates[i] > best_rate) {
   835                             best_rate = rates[i];
   836                         }
   837                     } else {
   838                         if (SDL_abs(rates[i] - rate) < SDL_abs(best_rate - rate)) {
   839                             best_rate = rates[i];
   840                         }
   841                     }
   842                 }
   843                 XRRSetScreenConfigAndRate(display, data->screen_config,
   844                                           RootWindow(display, data->screen), best,
   845                                           data->saved_rotation, best_rate,
   846                                           CurrentTime);
   847             }
   848         }
   849         return;
   850     }
   851 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   852 
   853 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   854     if (data->use_vidmode) {
   855         XF86VidModeModeInfo ** modes;
   856         int i, nmodes;
   857         int best;
   858 
   859         if (XF86VidModeGetAllModeLines(display, data->screen, &nmodes, &modes)) {
   860             best = -1;
   861             for (i = 0; i < nmodes; ++i) {
   862                 if (modes[i]->hdisplay < w || modes[i]->vdisplay < h) {
   863                     continue;
   864                 }
   865                 if (best == -1 ||
   866                     (modes[i]->hdisplay < modes[best]->hdisplay) ||
   867                     (modes[i]->hdisplay == modes[best]->hdisplay
   868                      && modes[i]->vdisplay < modes[best]->vdisplay)) {
   869                     best = i;
   870                     continue;
   871                 }
   872                 if ((modes[i]->hdisplay == modes[best]->hdisplay) &&
   873                     (modes[i]->vdisplay == modes[best]->vdisplay)) {
   874                     if (!rate) {
   875                         /* Higher is better, right? */
   876                         if (calculate_rate(modes[i]) >
   877                             calculate_rate(modes[best])) {
   878                             best = i;
   879                         }
   880                     } else {
   881                         if (SDL_abs(calculate_rate(modes[i]) - rate) <
   882                             SDL_abs(calculate_rate(modes[best]) - rate)) {
   883                             best = i;
   884                         }
   885                     }
   886                 }
   887             }
   888             if (best >= 0) {
   889 #ifdef X11MODES_DEBUG
   890                 printf("Best Mode %d: %d x %d @ %d\n", best,
   891                        modes[best]->hdisplay, modes[best]->vdisplay,
   892                        calculate_rate(modes[best]));
   893 #endif
   894                 XF86VidModeSwitchToMode(display, data->screen, modes[best]);
   895             }
   896             XFree(modes);
   897         }
   898         return;
   899     }
   900 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   901 }
   902 
   903 int
   904 X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode)
   905 {
   906     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   907     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   908 
   909     set_best_resolution(display, data, mode->w, mode->h, mode->refresh_rate);
   910     return 0;
   911 }
   912 
   913 void
   914 X11_QuitModes(_THIS)
   915 {
   916 }
   917 
   918 int
   919 X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
   920 {
   921     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   922     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   923     int real_rate;
   924 
   925     rect->x = 0;
   926     rect->y = 0;
   927     get_real_resolution(display, data, &rect->w, &rect->h, &real_rate);
   928 
   929 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   930     if (data->use_xinerama) {
   931         rect->x = data->xinerama_info.x_org;
   932         rect->y = data->xinerama_info.y_org;
   933     }
   934 #endif
   935     return 0;
   936 }
   937 
   938 #endif /* SDL_VIDEO_DRIVER_X11 */
   939 
   940 /* vi: set ts=4 sw=4 expandtab: */