src/video/x11/SDL_x11modes.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 26 Jan 2015 17:46:39 -0500
changeset 9319 ad4f430cc9f5
parent 9167 3d2c0f659ad3
child 9619 b94b6d0bff0f
permissions -rw-r--r--
X11: Don't delay delivery of focus events unless we just changed vidmodes.

Normally there's a 200 millisecond delay on all focus events in case there
was a vidmode change, now we note the last vidmode change and only impose this
delay if a change happened extremely recently.

Thanks to Epic Games for reporting this issue.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_X11
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_x11video.h"
    27 #include "SDL_timer.h"
    28 #include "edid.h"
    29 
    30 /* #define X11MODES_DEBUG */
    31 
    32 /* I'm becoming more and more convinced that the application should never
    33  * use XRandR, and it's the window manager's responsibility to track and
    34  * manage display modes for fullscreen windows.  Right now XRandR is completely
    35  * broken with respect to window manager behavior on every window manager that
    36  * I can find.  For example, on Unity 3D if you show a fullscreen window while
    37  * the resolution is changing (within ~250 ms) your window will retain the
    38  * fullscreen state hint but be decorated and windowed.
    39  *
    40  * However, many people swear by it, so let them swear at it. :)
    41 */
    42 /* #define XRANDR_DISABLED_BY_DEFAULT */
    43 
    44 
    45 static int
    46 get_visualinfo(Display * display, int screen, XVisualInfo * vinfo)
    47 {
    48     const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID");
    49     int depth;
    50 
    51     /* Look for an exact visual, if requested */
    52     if (visual_id) {
    53         XVisualInfo *vi, template;
    54         int nvis;
    55 
    56         SDL_zero(template);
    57         template.visualid = SDL_strtol(visual_id, NULL, 0);
    58         vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis);
    59         if (vi) {
    60             *vinfo = *vi;
    61             X11_XFree(vi);
    62             return 0;
    63         }
    64     }
    65 
    66     depth = DefaultDepth(display, screen);
    67     if ((X11_UseDirectColorVisuals() &&
    68          X11_XMatchVisualInfo(display, screen, depth, DirectColor, vinfo)) ||
    69         X11_XMatchVisualInfo(display, screen, depth, TrueColor, vinfo) ||
    70         X11_XMatchVisualInfo(display, screen, depth, PseudoColor, vinfo) ||
    71         X11_XMatchVisualInfo(display, screen, depth, StaticColor, vinfo)) {
    72         return 0;
    73     }
    74     return -1;
    75 }
    76 
    77 int
    78 X11_GetVisualInfoFromVisual(Display * display, Visual * visual, XVisualInfo * vinfo)
    79 {
    80     XVisualInfo *vi;
    81     int nvis;
    82 
    83     vinfo->visualid = X11_XVisualIDFromVisual(visual);
    84     vi = X11_XGetVisualInfo(display, VisualIDMask, vinfo, &nvis);
    85     if (vi) {
    86         *vinfo = *vi;
    87         X11_XFree(vi);
    88         return 0;
    89     }
    90     return -1;
    91 }
    92 
    93 Uint32
    94 X11_GetPixelFormatFromVisualInfo(Display * display, XVisualInfo * vinfo)
    95 {
    96     if (vinfo->class == DirectColor || vinfo->class == TrueColor) {
    97         int bpp;
    98         Uint32 Rmask, Gmask, Bmask, Amask;
    99 
   100         Rmask = vinfo->visual->red_mask;
   101         Gmask = vinfo->visual->green_mask;
   102         Bmask = vinfo->visual->blue_mask;
   103         if (vinfo->depth == 32) {
   104             Amask = (0xFFFFFFFF & ~(Rmask | Gmask | Bmask));
   105         } else {
   106             Amask = 0;
   107         }
   108 
   109         bpp = vinfo->depth;
   110         if (bpp == 24) {
   111             int i, n;
   112             XPixmapFormatValues *p = X11_XListPixmapFormats(display, &n);
   113             if (p) {
   114                 for (i = 0; i < n; ++i) {
   115                     if (p[i].depth == 24) {
   116                         bpp = p[i].bits_per_pixel;
   117                         break;
   118                     }
   119                 }
   120                 X11_XFree(p);
   121             }
   122         }
   123 
   124         return SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
   125     }
   126 
   127     if (vinfo->class == PseudoColor || vinfo->class == StaticColor) {
   128         switch (vinfo->depth) {
   129         case 8:
   130             return SDL_PIXELTYPE_INDEX8;
   131         case 4:
   132             if (BitmapBitOrder(display) == LSBFirst) {
   133                 return SDL_PIXELFORMAT_INDEX4LSB;
   134             } else {
   135                 return SDL_PIXELFORMAT_INDEX4MSB;
   136             }
   137             break;
   138         case 1:
   139             if (BitmapBitOrder(display) == LSBFirst) {
   140                 return SDL_PIXELFORMAT_INDEX1LSB;
   141             } else {
   142                 return SDL_PIXELFORMAT_INDEX1MSB;
   143             }
   144             break;
   145         }
   146     }
   147 
   148     return SDL_PIXELFORMAT_UNKNOWN;
   149 }
   150 
   151 /* Global for the error handler */
   152 int vm_event, vm_error = -1;
   153 
   154 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   155 static SDL_bool
   156 CheckXinerama(Display * display, int *major, int *minor)
   157 {
   158     int event_base = 0;
   159     int error_base = 0;
   160     const char *env;
   161 
   162     /* Default the extension not available */
   163     *major = *minor = 0;
   164 
   165     /* Allow environment override */
   166     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XINERAMA);
   167     if (env && !SDL_atoi(env)) {
   168 #ifdef X11MODES_DEBUG
   169         printf("Xinerama disabled due to hint\n");
   170 #endif
   171         return SDL_FALSE;
   172     }
   173 
   174     if (!SDL_X11_HAVE_XINERAMA) {
   175 #ifdef X11MODES_DEBUG
   176         printf("Xinerama support not available\n");
   177 #endif
   178         return SDL_FALSE;
   179     }
   180 
   181     /* Query the extension version */
   182     if (!X11_XineramaQueryExtension(display, &event_base, &error_base) ||
   183         !X11_XineramaQueryVersion(display, major, minor) ||
   184         !X11_XineramaIsActive(display)) {
   185 #ifdef X11MODES_DEBUG
   186         printf("Xinerama not active on the display\n");
   187 #endif
   188         return SDL_FALSE;
   189     }
   190 #ifdef X11MODES_DEBUG
   191     printf("Xinerama available at version %d.%d!\n", *major, *minor);
   192 #endif
   193     return SDL_TRUE;
   194 }
   195 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   196 
   197 #if SDL_VIDEO_DRIVER_X11_XRANDR
   198 static SDL_bool
   199 CheckXRandR(Display * display, int *major, int *minor)
   200 {
   201     const char *env;
   202 
   203     /* Default the extension not available */
   204     *major = *minor = 0;
   205 
   206     /* Allow environment override */
   207     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XRANDR);
   208 #ifdef XRANDR_DISABLED_BY_DEFAULT
   209     if (!env || !SDL_atoi(env)) {
   210 #ifdef X11MODES_DEBUG
   211         printf("XRandR disabled by default due to window manager issues\n");
   212 #endif
   213         return SDL_FALSE;
   214     }
   215 #else
   216     if (env && !SDL_atoi(env)) {
   217 #ifdef X11MODES_DEBUG
   218         printf("XRandR disabled due to hint\n");
   219 #endif
   220         return SDL_FALSE;
   221     }
   222 #endif /* XRANDR_ENABLED_BY_DEFAULT */
   223 
   224     if (!SDL_X11_HAVE_XRANDR) {
   225 #ifdef X11MODES_DEBUG
   226         printf("XRandR support not available\n");
   227 #endif
   228         return SDL_FALSE;
   229     }
   230 
   231     /* Query the extension version */
   232     if (!X11_XRRQueryVersion(display, major, minor)) {
   233 #ifdef X11MODES_DEBUG
   234         printf("XRandR not active on the display\n");
   235 #endif
   236         return SDL_FALSE;
   237     }
   238 #ifdef X11MODES_DEBUG
   239     printf("XRandR available at version %d.%d!\n", *major, *minor);
   240 #endif
   241     return SDL_TRUE;
   242 }
   243 
   244 #define XRANDR_ROTATION_LEFT    (1 << 1)
   245 #define XRANDR_ROTATION_RIGHT   (1 << 3)
   246 
   247 static int
   248 CalculateXRandRRefreshRate(const XRRModeInfo *info)
   249 {
   250     return (info->hTotal
   251             && info->vTotal) ? (info->dotClock / (info->hTotal * info->vTotal)) : 0;
   252 }
   253 
   254 static SDL_bool
   255 SetXRandRModeInfo(Display *display, XRRScreenResources *res, XRROutputInfo *output_info,
   256                   RRMode modeID, SDL_DisplayMode *mode)
   257 {
   258     int i;
   259     for (i = 0; i < res->nmode; ++i) {
   260         if (res->modes[i].id == modeID) {
   261             XRRCrtcInfo *crtc;
   262             Rotation rotation = 0;
   263             const XRRModeInfo *info = &res->modes[i];
   264 
   265             crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
   266             if (crtc) {
   267                 rotation = crtc->rotation;
   268                 X11_XRRFreeCrtcInfo(crtc);
   269             }
   270 
   271             if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) {
   272                 mode->w = info->height;
   273                 mode->h = info->width;
   274             } else {
   275                 mode->w = info->width;
   276                 mode->h = info->height;
   277             }
   278             mode->refresh_rate = CalculateXRandRRefreshRate(info);
   279             ((SDL_DisplayModeData*)mode->driverdata)->xrandr_mode = modeID;
   280 #ifdef X11MODES_DEBUG
   281             printf("XRandR mode %d: %dx%d@%dHz\n", (int) modeID, mode->w, mode->h, mode->refresh_rate);
   282 #endif
   283             return SDL_TRUE;
   284         }
   285     }
   286     return SDL_FALSE;
   287 }
   288 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   289 
   290 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   291 static SDL_bool
   292 CheckVidMode(Display * display, int *major, int *minor)
   293 {
   294     const char *env;
   295 
   296     /* Default the extension not available */
   297     *major = *minor = 0;
   298 
   299     /* Allow environment override */
   300     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XVIDMODE);
   301     if (env && !SDL_atoi(env)) {
   302 #ifdef X11MODES_DEBUG
   303         printf("XVidMode disabled due to hint\n");
   304 #endif
   305         return SDL_FALSE;
   306     }
   307 
   308     if (!SDL_X11_HAVE_XVIDMODE) {
   309 #ifdef X11MODES_DEBUG
   310         printf("XVidMode support not available\n");
   311 #endif
   312         return SDL_FALSE;
   313     }
   314 
   315     /* Query the extension version */
   316     vm_error = -1;
   317     if (!X11_XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   318         || !X11_XF86VidModeQueryVersion(display, major, minor)) {
   319 #ifdef X11MODES_DEBUG
   320         printf("XVidMode not active on the display\n");
   321 #endif
   322         return SDL_FALSE;
   323     }
   324 #ifdef X11MODES_DEBUG
   325     printf("XVidMode available at version %d.%d!\n", *major, *minor);
   326 #endif
   327     return SDL_TRUE;
   328 }
   329 
   330 static
   331 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   332                                        XF86VidModeModeInfo* info)
   333 {
   334     Bool retval;
   335     int dotclock;
   336     XF86VidModeModeLine l;
   337     SDL_zerop(info);
   338     SDL_zero(l);
   339     retval = X11_XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   340     info->dotclock = dotclock;
   341     info->hdisplay = l.hdisplay;
   342     info->hsyncstart = l.hsyncstart;
   343     info->hsyncend = l.hsyncend;
   344     info->htotal = l.htotal;
   345     info->hskew = l.hskew;
   346     info->vdisplay = l.vdisplay;
   347     info->vsyncstart = l.vsyncstart;
   348     info->vsyncend = l.vsyncend;
   349     info->vtotal = l.vtotal;
   350     info->flags = l.flags;
   351     info->privsize = l.privsize;
   352     info->private = l.private;
   353     return retval;
   354 }
   355 
   356 static int
   357 CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info)
   358 {
   359     return (info->htotal
   360             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   361                                                          info->vtotal)) : 0;
   362 }
   363 
   364 SDL_bool
   365 SetXVidModeModeInfo(const XF86VidModeModeInfo *info, SDL_DisplayMode *mode)
   366 {
   367     mode->w = info->hdisplay;
   368     mode->h = info->vdisplay;
   369     mode->refresh_rate = CalculateXVidModeRefreshRate(info);
   370     ((SDL_DisplayModeData*)mode->driverdata)->vm_mode = *info;
   371     return SDL_TRUE;
   372 }
   373 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   374 
   375 int
   376 X11_InitModes(_THIS)
   377 {
   378     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   379     int snum, screen, screencount;
   380 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   381     int xinerama_major, xinerama_minor;
   382     int use_xinerama = 0;
   383     XineramaScreenInfo *xinerama = NULL;
   384 #endif
   385 #if SDL_VIDEO_DRIVER_X11_XRANDR
   386     int xrandr_major, xrandr_minor;
   387     int use_xrandr = 0;
   388     XRRScreenResources *res = NULL;
   389 #endif
   390 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   391     int vm_major, vm_minor;
   392     int use_vidmode = 0;
   393 #endif
   394 
   395 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   396     /* Query Xinerama extention
   397      * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
   398      *       or newer of the Nvidia binary drivers
   399      */
   400     if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
   401         xinerama = X11_XineramaQueryScreens(data->display, &screencount);
   402         if (xinerama) {
   403             use_xinerama = xinerama_major * 100 + xinerama_minor;
   404         }
   405     }
   406     if (!xinerama) {
   407         screencount = ScreenCount(data->display);
   408     }
   409 #else
   410     screencount = ScreenCount(data->display);
   411 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   412 
   413 #if SDL_VIDEO_DRIVER_X11_XRANDR
   414     /* require at least XRandR v1.2 */
   415     if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
   416         (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 2))) {
   417         use_xrandr = xrandr_major * 100 + xrandr_minor;
   418     }
   419 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   420 
   421 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   422     if (CheckVidMode(data->display, &vm_major, &vm_minor)) {
   423         use_vidmode = vm_major * 100 + vm_minor;
   424     }
   425 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   426 
   427     for (snum = 0; snum < screencount; ++snum) {
   428         XVisualInfo vinfo;
   429         SDL_VideoDisplay display;
   430         SDL_DisplayData *displaydata;
   431         SDL_DisplayMode mode;
   432         SDL_DisplayModeData *modedata;
   433         XPixmapFormatValues *pixmapFormats;
   434         char display_name[128];
   435         int i, n;
   436 
   437         /* Re-order screens to always put default screen first */
   438         if (snum == 0) {
   439             screen = DefaultScreen(data->display);
   440         } else if (snum == DefaultScreen(data->display)) {
   441             screen = 0;
   442         } else {
   443             screen = snum;
   444         }
   445 
   446 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   447         if (xinerama) {
   448             if (get_visualinfo(data->display, 0, &vinfo) < 0) {
   449                 continue;
   450             }
   451         } else {
   452             if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   453                 continue;
   454             }
   455         }
   456 #else
   457         if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   458             continue;
   459         }
   460 #endif
   461 
   462         displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   463         if (!displaydata) {
   464             continue;
   465         }
   466         display_name[0] = '\0';
   467 
   468         mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
   469         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   470             /* We don't support palettized modes now */
   471             SDL_free(displaydata);
   472             continue;
   473         }
   474 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   475         if (xinerama) {
   476             mode.w = xinerama[screen].width;
   477             mode.h = xinerama[screen].height;
   478         } else {
   479             mode.w = DisplayWidth(data->display, screen);
   480             mode.h = DisplayHeight(data->display, screen);
   481         }
   482 #else
   483         mode.w = DisplayWidth(data->display, screen);
   484         mode.h = DisplayHeight(data->display, screen);
   485 #endif
   486         mode.refresh_rate = 0;
   487 
   488         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   489         if (!modedata) {
   490             SDL_free(displaydata);
   491             continue;
   492         }
   493         mode.driverdata = modedata;
   494 
   495 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   496         /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
   497          * there's only one screen available. So we force the screen number to zero and
   498          * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
   499          */
   500         if (use_xinerama) {
   501             displaydata->screen = 0;
   502             displaydata->use_xinerama = use_xinerama;
   503             displaydata->xinerama_info = xinerama[screen];
   504             displaydata->xinerama_screen = screen;
   505         }
   506         else displaydata->screen = screen;
   507 #else
   508         displaydata->screen = screen;
   509 #endif
   510         displaydata->visual = vinfo.visual;
   511         displaydata->depth = vinfo.depth;
   512 
   513         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   514         pixmapFormats = X11_XListPixmapFormats(data->display, &n);
   515         if (pixmapFormats) {
   516             for (i = 0; i < n; ++i) {
   517                 if (pixmapFormats[i].depth == displaydata->depth) {
   518                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   519                     break;
   520                 }
   521             }
   522             X11_XFree(pixmapFormats);
   523         }
   524 
   525 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   526         if (use_xinerama) {
   527             displaydata->x = xinerama[screen].x_org;
   528             displaydata->y = xinerama[screen].y_org;
   529         }
   530         else
   531 #endif
   532         {
   533             displaydata->x = 0;
   534             displaydata->y = 0;
   535         }
   536 
   537 #if SDL_VIDEO_DRIVER_X11_XRANDR
   538         if (use_xrandr) {
   539             res = X11_XRRGetScreenResources(data->display, RootWindow(data->display, displaydata->screen));
   540         }
   541         if (res) {
   542             XRROutputInfo *output_info;
   543             XRRCrtcInfo *crtc;
   544             int output;
   545             Atom EDID = X11_XInternAtom(data->display, "EDID", False);
   546             Atom *props;
   547             int nprop;
   548             unsigned long width_mm;
   549             unsigned long height_mm;
   550             int inches = 0;
   551 
   552             for (output = 0; output < res->noutput; output++) {
   553                 output_info = X11_XRRGetOutputInfo(data->display, res, res->outputs[output]);
   554                 if (!output_info || !output_info->crtc ||
   555                     output_info->connection == RR_Disconnected) {
   556                     X11_XRRFreeOutputInfo(output_info);
   557                     continue;
   558                 }
   559 
   560                 /* Is this the output that corresponds to the current screen?
   561                    We're checking the crtc position, but that may not be a valid test
   562                    in all cases.  Anybody want to give this some love?
   563                  */
   564                 crtc = X11_XRRGetCrtcInfo(data->display, res, output_info->crtc);
   565                 if (!crtc || crtc->x != displaydata->x || crtc->y != displaydata->y ||
   566                     crtc->width != mode.w || crtc->height != mode.h) {
   567                     X11_XRRFreeOutputInfo(output_info);
   568                     X11_XRRFreeCrtcInfo(crtc);
   569                     continue;
   570                 }
   571 
   572                 displaydata->use_xrandr = use_xrandr;
   573                 displaydata->xrandr_output = res->outputs[output];
   574                 SetXRandRModeInfo(data->display, res, output_info, crtc->mode, &mode);
   575 
   576                 /* Get the name of this display */
   577                 width_mm = output_info->mm_width;
   578                 height_mm = output_info->mm_height;
   579                 inches = (int)((sqrt(width_mm * width_mm +
   580                                      height_mm * height_mm) / 25.4f) + 0.5f);
   581                 SDL_strlcpy(display_name, output_info->name, sizeof(display_name));
   582 
   583                 /* See if we can get the EDID data for the real monitor name */
   584                 props = X11_XRRListOutputProperties(data->display, res->outputs[output], &nprop);
   585                 for (i = 0; i < nprop; ++i) {
   586                     unsigned char *prop;
   587                     int actual_format;
   588                     unsigned long nitems, bytes_after;
   589                     Atom actual_type;
   590 
   591                     if (props[i] == EDID) {
   592                         if (X11_XRRGetOutputProperty(data->display,
   593                                                  res->outputs[output], props[i],
   594                                                  0, 100, False, False,
   595                                                  AnyPropertyType,
   596                                                  &actual_type, &actual_format,
   597                                                  &nitems, &bytes_after, &prop) == Success ) {
   598                             MonitorInfo *info = decode_edid(prop);
   599                             if (info) {
   600     #ifdef X11MODES_DEBUG
   601                                 printf("Found EDID data for %s\n", output_info->name);
   602                                 dump_monitor_info(info);
   603     #endif
   604                                 SDL_strlcpy(display_name, info->dsc_product_name, sizeof(display_name));
   605                                 free(info);
   606                             }
   607                             X11_XFree(prop);
   608                         }
   609                         break;
   610                     }
   611                 }
   612                 if (props) {
   613                     X11_XFree(props);
   614                 }
   615 
   616                 if (*display_name && inches) {
   617                     size_t len = SDL_strlen(display_name);
   618                     SDL_snprintf(&display_name[len], sizeof(display_name)-len, " %d\"", inches);
   619                 }
   620 #ifdef X11MODES_DEBUG
   621                 printf("Display name: %s\n", display_name);
   622 #endif
   623 
   624                 X11_XRRFreeOutputInfo(output_info);
   625                 X11_XRRFreeCrtcInfo(crtc);
   626                 break;
   627             }
   628 #ifdef X11MODES_DEBUG
   629             if (output == res->noutput) {
   630                 printf("Couldn't find XRandR CRTC at %d,%d\n", displaydata->x, displaydata->y);
   631             }
   632 #endif
   633             X11_XRRFreeScreenResources(res);
   634         }
   635 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   636 
   637 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   638         if (!displaydata->use_xrandr &&
   639 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   640             /* XVidMode only works on the screen at the origin */
   641             (!displaydata->use_xinerama ||
   642              (displaydata->x == 0 && displaydata->y == 0)) &&
   643 #endif
   644             use_vidmode) {
   645             displaydata->use_vidmode = use_vidmode;
   646             if (displaydata->use_xinerama) {
   647                 displaydata->vidmode_screen = 0;
   648             } else {
   649                 displaydata->vidmode_screen = screen;
   650             }
   651             XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode);
   652         }
   653 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   654 
   655         SDL_zero(display);
   656         if (*display_name) {
   657             display.name = display_name;
   658         }
   659         display.desktop_mode = mode;
   660         display.current_mode = mode;
   661         display.driverdata = displaydata;
   662         SDL_AddVideoDisplay(&display);
   663     }
   664 
   665 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   666     if (xinerama) X11_XFree(xinerama);
   667 #endif
   668 
   669     if (_this->num_displays == 0) {
   670         return SDL_SetError("No available displays");
   671     }
   672     return 0;
   673 }
   674 
   675 void
   676 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   677 {
   678     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   679     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   680 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   681     int nmodes;
   682     XF86VidModeModeInfo ** modes;
   683 #endif
   684     int screen_w;
   685     int screen_h;
   686     SDL_DisplayMode mode;
   687 
   688     /* Unfortunately X11 requires the window to be created with the correct
   689      * visual and depth ahead of time, but the SDL API allows you to create
   690      * a window before setting the fullscreen display mode.  This means that
   691      * we have to use the same format for all windows and all display modes.
   692      * (or support recreating the window with a new visual behind the scenes)
   693      */
   694     mode.format = sdl_display->current_mode.format;
   695     mode.driverdata = NULL;
   696 
   697     screen_w = DisplayWidth(display, data->screen);
   698     screen_h = DisplayHeight(display, data->screen);
   699 
   700 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   701     if (data->use_xinerama) {
   702         if (data->use_vidmode && !data->xinerama_info.x_org && !data->xinerama_info.y_org &&
   703            (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) {
   704             SDL_DisplayModeData *modedata;
   705             /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0
   706              * if we're using vidmode.
   707              */
   708             mode.w = screen_w;
   709             mode.h = screen_h;
   710             mode.refresh_rate = 0;
   711             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   712             if (modedata) {
   713                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   714             }
   715             mode.driverdata = modedata;
   716             SDL_AddDisplayMode(sdl_display, &mode);
   717         }
   718         else if (!data->use_xrandr)
   719         {
   720             SDL_DisplayModeData *modedata;
   721             /* Add the current mode of each monitor otherwise if we can't get them from xrandr */
   722             mode.w = data->xinerama_info.width;
   723             mode.h = data->xinerama_info.height;
   724             mode.refresh_rate = 0;
   725             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   726             if (modedata) {
   727                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   728             }
   729             mode.driverdata = modedata;
   730             SDL_AddDisplayMode(sdl_display, &mode);
   731         }
   732 
   733     }
   734 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   735 
   736 #if SDL_VIDEO_DRIVER_X11_XRANDR
   737     if (data->use_xrandr) {
   738         XRRScreenResources *res;
   739 
   740         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   741         if (res) {
   742             SDL_DisplayModeData *modedata;
   743             XRROutputInfo *output_info;
   744             int i;
   745 
   746             output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   747             if (output_info && output_info->connection != RR_Disconnected) {
   748                 for (i = 0; i < output_info->nmode; ++i) {
   749                     modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   750                     if (!modedata) {
   751                         continue;
   752                     }
   753                     mode.driverdata = modedata;
   754 
   755                     if (SetXRandRModeInfo(display, res, output_info, output_info->modes[i], &mode)) {
   756                         SDL_AddDisplayMode(sdl_display, &mode);
   757                     } else {
   758                         SDL_free(modedata);
   759                     }
   760                 }
   761             }
   762             X11_XRRFreeOutputInfo(output_info);
   763             X11_XRRFreeScreenResources(res);
   764         }
   765         return;
   766     }
   767 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   768 
   769 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   770     if (data->use_vidmode &&
   771         X11_XF86VidModeGetAllModeLines(display, data->vidmode_screen, &nmodes, &modes)) {
   772         int i;
   773         SDL_DisplayModeData *modedata;
   774 
   775 #ifdef X11MODES_DEBUG
   776         printf("VidMode modes: (unsorted)\n");
   777         for (i = 0; i < nmodes; ++i) {
   778             printf("Mode %d: %d x %d @ %d, flags: 0x%x\n", i,
   779                    modes[i]->hdisplay, modes[i]->vdisplay,
   780                    CalculateXVidModeRefreshRate(modes[i]), modes[i]->flags);
   781         }
   782 #endif
   783         for (i = 0; i < nmodes; ++i) {
   784             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   785             if (!modedata) {
   786                 continue;
   787             }
   788             mode.driverdata = modedata;
   789 
   790             if (SetXVidModeModeInfo(modes[i], &mode)) {
   791                 SDL_AddDisplayMode(sdl_display, &mode);
   792             } else {
   793                 SDL_free(modedata);
   794             }
   795         }
   796         X11_XFree(modes);
   797         return;
   798     }
   799 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   800 
   801     if (!data->use_xrandr && !data->use_vidmode) {
   802         SDL_DisplayModeData *modedata;
   803         /* Add the desktop mode */
   804         mode = sdl_display->desktop_mode;
   805         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   806         if (modedata) {
   807             *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   808         }
   809         mode.driverdata = modedata;
   810         SDL_AddDisplayMode(sdl_display, &mode);
   811     }
   812 }
   813 
   814 int
   815 X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode)
   816 {
   817     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   818     Display *display = viddata->display;
   819     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   820     SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   821 
   822     viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
   823 
   824 #if SDL_VIDEO_DRIVER_X11_XRANDR
   825     if (data->use_xrandr) {
   826         XRRScreenResources *res;
   827         XRROutputInfo *output_info;
   828         XRRCrtcInfo *crtc;
   829         Status status;
   830 
   831         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   832         if (!res) {
   833             return SDL_SetError("Couldn't get XRandR screen resources");
   834         }
   835 
   836         output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   837         if (!output_info || output_info->connection == RR_Disconnected) {
   838             X11_XRRFreeScreenResources(res);
   839             return SDL_SetError("Couldn't get XRandR output info");
   840         }
   841 
   842         crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
   843         if (!crtc) {
   844             X11_XRRFreeOutputInfo(output_info);
   845             X11_XRRFreeScreenResources(res);
   846             return SDL_SetError("Couldn't get XRandR crtc info");
   847         }
   848 
   849         status = X11_XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime,
   850           crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation,
   851           &data->xrandr_output, 1);
   852 
   853         X11_XRRFreeCrtcInfo(crtc);
   854         X11_XRRFreeOutputInfo(output_info);
   855         X11_XRRFreeScreenResources(res);
   856 
   857         if (status != Success) {
   858             return SDL_SetError("X11_XRRSetCrtcConfig failed");
   859         }
   860     }
   861 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   862 
   863 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   864     if (data->use_vidmode) {
   865         X11_XF86VidModeSwitchToMode(display, data->vidmode_screen, &modedata->vm_mode);
   866     }
   867 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   868 
   869     return 0;
   870 }
   871 
   872 void
   873 X11_QuitModes(_THIS)
   874 {
   875 }
   876 
   877 int
   878 X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
   879 {
   880     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   881 
   882     rect->x = data->x;
   883     rect->y = data->y;
   884     rect->w = sdl_display->current_mode.w;
   885     rect->h = sdl_display->current_mode.h;
   886 
   887 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   888     /* Get the real current bounds of the display */
   889     if (data->use_xinerama) {
   890         Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   891         int screencount;
   892         XineramaScreenInfo *xinerama = X11_XineramaQueryScreens(display, &screencount);
   893         if (xinerama) {
   894             rect->x = xinerama[data->xinerama_screen].x_org;
   895             rect->y = xinerama[data->xinerama_screen].y_org;
   896             X11_XFree(xinerama);
   897         }
   898     }
   899 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   900     return 0;
   901 }
   902 
   903 #endif /* SDL_VIDEO_DRIVER_X11 */
   904 
   905 /* vi: set ts=4 sw=4 expandtab: */