src/video/x11/SDL_x11modes.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 02 Aug 2017 10:28:13 -0700
changeset 11177 393fa47cb702
parent 10780 4ea5472ed455
child 11344 039ef928e200
permissions -rw-r--r--
Fixed bug 3722 - Fall back to xinerama/xvidmode if xrandr modes initialization fails

Levi Bard

In some environments, xrandr modes initialization can fail even though xrandr support is present and of a sufficient version.
(The one I encountered was an AWS instance running a virtual display)

The attached patch allows SDL to keep trying other methods if xrandr modes initialization fails (still subject to SDL_VIDEO_X11_REQUIRE_XRANDR).
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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; -Wunreachable-code-break */
   138         case 1:
   139             if (BitmapBitOrder(display) == LSBFirst) {
   140                 return SDL_PIXELFORMAT_INDEX1LSB;
   141             } else {
   142                 return SDL_PIXELFORMAT_INDEX1MSB;
   143             }
   144             /* break; -Wunreachable-code-break */
   145         }
   146     }
   147 
   148     return SDL_PIXELFORMAT_UNKNOWN;
   149 }
   150 
   151 /* Global for the error handler */
   152 static 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 
   161     /* Default the extension not available */
   162     *major = *minor = 0;
   163 
   164     /* Allow environment override */
   165     if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XINERAMA, SDL_TRUE)) {
   166 #ifdef X11MODES_DEBUG
   167         printf("Xinerama disabled due to hint\n");
   168 #endif
   169         return SDL_FALSE;
   170     }
   171 
   172     if (!SDL_X11_HAVE_XINERAMA) {
   173 #ifdef X11MODES_DEBUG
   174         printf("Xinerama support not available\n");
   175 #endif
   176         return SDL_FALSE;
   177     }
   178 
   179     /* Query the extension version */
   180     if (!X11_XineramaQueryExtension(display, &event_base, &error_base) ||
   181         !X11_XineramaQueryVersion(display, major, minor) ||
   182         !X11_XineramaIsActive(display)) {
   183 #ifdef X11MODES_DEBUG
   184         printf("Xinerama not active on the display\n");
   185 #endif
   186         return SDL_FALSE;
   187     }
   188 #ifdef X11MODES_DEBUG
   189     printf("Xinerama available at version %d.%d!\n", *major, *minor);
   190 #endif
   191     return SDL_TRUE;
   192 }
   193 
   194 /* !!! FIXME: remove this later. */
   195 /* we have a weird bug where XineramaQueryScreens() throws an X error, so this
   196    is here to help track it down (and not crash, too!). */
   197 static SDL_bool xinerama_triggered_error = SDL_FALSE;
   198 static int
   199 X11_XineramaFailed(Display * d, XErrorEvent * e)
   200 {
   201     xinerama_triggered_error = SDL_TRUE;
   202     fprintf(stderr, "XINERAMA X ERROR: type=%d serial=%lu err=%u req=%u minor=%u\n",
   203             e->type, e->serial, (unsigned int) e->error_code,
   204             (unsigned int) e->request_code, (unsigned int) e->minor_code);
   205     fflush(stderr);
   206     return 0;
   207 }
   208 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   209 
   210 #if SDL_VIDEO_DRIVER_X11_XRANDR
   211 static SDL_bool
   212 CheckXRandR(Display * display, int *major, int *minor)
   213 {
   214     /* Default the extension not available */
   215     *major = *minor = 0;
   216 
   217     /* Allow environment override */
   218 #ifdef XRANDR_DISABLED_BY_DEFAULT
   219     if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, SDL_FALSE)) {
   220 #ifdef X11MODES_DEBUG
   221         printf("XRandR disabled by default due to window manager issues\n");
   222 #endif
   223         return SDL_FALSE;
   224     }
   225 #else
   226     if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, SDL_TRUE)) {
   227 #ifdef X11MODES_DEBUG
   228         printf("XRandR disabled due to hint\n");
   229 #endif
   230         return SDL_FALSE;
   231     }
   232 #endif /* XRANDR_ENABLED_BY_DEFAULT */
   233 
   234     if (!SDL_X11_HAVE_XRANDR) {
   235 #ifdef X11MODES_DEBUG
   236         printf("XRandR support not available\n");
   237 #endif
   238         return SDL_FALSE;
   239     }
   240 
   241     /* Query the extension version */
   242     *major = 1; *minor = 3;  /* we want 1.3 */
   243     if (!X11_XRRQueryVersion(display, major, minor)) {
   244 #ifdef X11MODES_DEBUG
   245         printf("XRandR not active on the display\n");
   246 #endif
   247         *major = *minor = 0;
   248         return SDL_FALSE;
   249     }
   250 #ifdef X11MODES_DEBUG
   251     printf("XRandR available at version %d.%d!\n", *major, *minor);
   252 #endif
   253     return SDL_TRUE;
   254 }
   255 
   256 #define XRANDR_ROTATION_LEFT    (1 << 1)
   257 #define XRANDR_ROTATION_RIGHT   (1 << 3)
   258 
   259 static int
   260 CalculateXRandRRefreshRate(const XRRModeInfo *info)
   261 {
   262     return (info->hTotal && info->vTotal) ?
   263         round(((double)info->dotClock / (double)(info->hTotal * info->vTotal))) : 0;
   264 }
   265 
   266 static SDL_bool
   267 SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc crtc,
   268                   RRMode modeID, SDL_DisplayMode *mode)
   269 {
   270     int i;
   271     for (i = 0; i < res->nmode; ++i) {
   272         const XRRModeInfo *info = &res->modes[i];
   273         if (info->id == modeID) {
   274             XRRCrtcInfo *crtcinfo;
   275             Rotation rotation = 0;
   276 
   277             crtcinfo = X11_XRRGetCrtcInfo(display, res, crtc);
   278             if (crtcinfo) {
   279                 rotation = crtcinfo->rotation;
   280                 X11_XRRFreeCrtcInfo(crtcinfo);
   281             }
   282 
   283             if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) {
   284                 mode->w = info->height;
   285                 mode->h = info->width;
   286             } else {
   287                 mode->w = info->width;
   288                 mode->h = info->height;
   289             }
   290             mode->refresh_rate = CalculateXRandRRefreshRate(info);
   291             ((SDL_DisplayModeData*)mode->driverdata)->xrandr_mode = modeID;
   292 #ifdef X11MODES_DEBUG
   293             printf("XRandR mode %d: %dx%d@%dHz\n", (int) modeID, mode->w, mode->h, mode->refresh_rate);
   294 #endif
   295             return SDL_TRUE;
   296         }
   297     }
   298     return SDL_FALSE;
   299 }
   300 
   301 static void
   302 SetXRandRDisplayName(Display *dpy, Atom EDID, char *name, const size_t namelen, RROutput output, const unsigned long widthmm, const unsigned long heightmm)
   303 {
   304     /* See if we can get the EDID data for the real monitor name */
   305     int inches;
   306     int nprop;
   307     Atom *props = X11_XRRListOutputProperties(dpy, output, &nprop);
   308     int i;
   309 
   310     for (i = 0; i < nprop; ++i) {
   311         unsigned char *prop;
   312         int actual_format;
   313         unsigned long nitems, bytes_after;
   314         Atom actual_type;
   315 
   316         if (props[i] == EDID) {
   317             if (X11_XRRGetOutputProperty(dpy, output, props[i], 0, 100, False,
   318                                          False, AnyPropertyType, &actual_type,
   319                                          &actual_format, &nitems, &bytes_after,
   320                                          &prop) == Success) {
   321                 MonitorInfo *info = decode_edid(prop);
   322                 if (info) {
   323 #ifdef X11MODES_DEBUG
   324                     printf("Found EDID data for %s\n", name);
   325                     dump_monitor_info(info);
   326 #endif
   327                     SDL_strlcpy(name, info->dsc_product_name, namelen);
   328                     free(info);
   329                 }
   330                 X11_XFree(prop);
   331             }
   332             break;
   333         }
   334     }
   335 
   336     if (props) {
   337         X11_XFree(props);
   338     }
   339 
   340     inches = (int)((SDL_sqrtf(widthmm * widthmm + heightmm * heightmm) / 25.4f) + 0.5f);
   341     if (*name && inches) {
   342         const size_t len = SDL_strlen(name);
   343         SDL_snprintf(&name[len], namelen-len, " %d\"", inches);
   344     }
   345 
   346 #ifdef X11MODES_DEBUG
   347     printf("Display name: %s\n", name);
   348 #endif
   349 }
   350 
   351 
   352 static int
   353 X11_InitModes_XRandR(_THIS)
   354 {
   355     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   356     Display *dpy = data->display;
   357     const int screencount = ScreenCount(dpy);
   358     const int default_screen = DefaultScreen(dpy);
   359     RROutput primary = X11_XRRGetOutputPrimary(dpy, RootWindow(dpy, default_screen));
   360     Atom EDID = X11_XInternAtom(dpy, "EDID", False);
   361     XRRScreenResources *res = NULL;
   362     Uint32 pixelformat;
   363     XVisualInfo vinfo;
   364     XPixmapFormatValues *pixmapformats;
   365     int looking_for_primary;
   366     int scanline_pad;
   367     int output;
   368     int screen, i, n;
   369 
   370     for (looking_for_primary = 1; looking_for_primary >= 0; looking_for_primary--) {
   371         for (screen = 0; screen < screencount; screen++) {
   372 
   373             /* we want the primary output first, and then skipped later. */
   374             if (looking_for_primary && (screen != default_screen)) {
   375                 continue;
   376             }
   377 
   378             if (get_visualinfo(dpy, screen, &vinfo) < 0) {
   379                 continue;  /* uh, skip this screen? */
   380             }
   381 
   382             pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
   383             if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
   384                 continue;  /* Palettized video modes are no longer supported */
   385             }
   386 
   387             scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
   388             pixmapformats = X11_XListPixmapFormats(dpy, &n);
   389             if (pixmapformats) {
   390                 for (i = 0; i < n; ++i) {
   391                     if (pixmapformats[i].depth == vinfo.depth) {
   392                         scanline_pad = pixmapformats[i].scanline_pad;
   393                         break;
   394                     }
   395                 }
   396                 X11_XFree(pixmapformats);
   397             }
   398 
   399             res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen));
   400             if (!res) {
   401                 continue;
   402             }
   403 
   404             for (output = 0; output < res->noutput; output++) {
   405                 XRROutputInfo *output_info;
   406                 int display_x, display_y;
   407                 unsigned long display_mm_width, display_mm_height;
   408                 SDL_DisplayData *displaydata;
   409                 char display_name[128];
   410                 SDL_DisplayMode mode;
   411                 SDL_DisplayModeData *modedata;
   412                 SDL_VideoDisplay display;
   413                 RRMode modeID;
   414                 RRCrtc output_crtc;
   415                 XRRCrtcInfo *crtc;
   416 
   417                 /* The primary output _should_ always be sorted first, but just in case... */
   418                 if ((looking_for_primary && (res->outputs[output] != primary)) ||
   419                     (!looking_for_primary && (screen == default_screen) && (res->outputs[output] == primary))) {
   420                     continue;
   421                 }
   422 
   423                 output_info = X11_XRRGetOutputInfo(dpy, res, res->outputs[output]);
   424                 if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) {
   425                     X11_XRRFreeOutputInfo(output_info);
   426                     continue;
   427                 }
   428 
   429                 SDL_strlcpy(display_name, output_info->name, sizeof(display_name));
   430                 display_mm_width = output_info->mm_width;
   431                 display_mm_height = output_info->mm_height;
   432                 output_crtc = output_info->crtc;
   433                 X11_XRRFreeOutputInfo(output_info);
   434 
   435                 crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc);
   436                 if (!crtc) {
   437                     continue;
   438                 }
   439 
   440                 SDL_zero(mode);
   441                 modeID = crtc->mode;
   442                 mode.w = crtc->width;
   443                 mode.h = crtc->height;
   444                 mode.format = pixelformat;
   445 
   446                 display_x = crtc->x;
   447                 display_y = crtc->y;
   448 
   449                 X11_XRRFreeCrtcInfo(crtc);
   450 
   451                 displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   452                 if (!displaydata) {
   453                     return SDL_OutOfMemory();
   454                 }
   455 
   456                 modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   457                 if (!modedata) {
   458                     SDL_free(displaydata);
   459                     return SDL_OutOfMemory();
   460                 }
   461                 modedata->xrandr_mode = modeID;
   462                 mode.driverdata = modedata;
   463 
   464                 displaydata->screen = screen;
   465                 displaydata->visual = vinfo.visual;
   466                 displaydata->depth = vinfo.depth;
   467                 displaydata->hdpi = display_mm_width ? (((float) mode.w) * 25.4f / display_mm_width) : 0.0f;
   468                 displaydata->vdpi = display_mm_height ? (((float) mode.h) * 25.4f / display_mm_height) : 0.0f;
   469                 displaydata->ddpi = SDL_ComputeDiagonalDPI(mode.w, mode.h, ((float) display_mm_width) / 25.4f,((float) display_mm_height) / 25.4f);
   470                 displaydata->scanline_pad = scanline_pad;
   471                 displaydata->x = display_x;
   472                 displaydata->y = display_y;
   473                 displaydata->use_xrandr = 1;
   474                 displaydata->xrandr_output = res->outputs[output];
   475 
   476                 SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode);
   477                 SetXRandRDisplayName(dpy, EDID, display_name, sizeof (display_name), res->outputs[output], display_mm_width, display_mm_height);
   478 
   479                 SDL_zero(display);
   480                 if (*display_name) {
   481                     display.name = display_name;
   482                 }
   483                 display.desktop_mode = mode;
   484                 display.current_mode = mode;
   485                 display.driverdata = displaydata;
   486                 SDL_AddVideoDisplay(&display);
   487             }
   488 
   489             X11_XRRFreeScreenResources(res);
   490         }
   491     }
   492 
   493     if (_this->num_displays == 0) {
   494         return SDL_SetError("No available displays");
   495     }
   496 
   497     return 0;
   498 }
   499 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   500 
   501 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   502 static SDL_bool
   503 CheckVidMode(Display * display, int *major, int *minor)
   504 {
   505     /* Default the extension not available */
   506     *major = *minor = 0;
   507 
   508     /* Allow environment override */
   509     if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XVIDMODE, SDL_TRUE)) {
   510 #ifdef X11MODES_DEBUG
   511         printf("XVidMode disabled due to hint\n");
   512 #endif
   513         return SDL_FALSE;
   514     }
   515 
   516     if (!SDL_X11_HAVE_XVIDMODE) {
   517 #ifdef X11MODES_DEBUG
   518         printf("XVidMode support not available\n");
   519 #endif
   520         return SDL_FALSE;
   521     }
   522 
   523     /* Query the extension version */
   524     vm_error = -1;
   525     if (!X11_XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   526         || !X11_XF86VidModeQueryVersion(display, major, minor)) {
   527 #ifdef X11MODES_DEBUG
   528         printf("XVidMode not active on the display\n");
   529 #endif
   530         return SDL_FALSE;
   531     }
   532 #ifdef X11MODES_DEBUG
   533     printf("XVidMode available at version %d.%d!\n", *major, *minor);
   534 #endif
   535     return SDL_TRUE;
   536 }
   537 
   538 static
   539 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   540                                        XF86VidModeModeInfo* info)
   541 {
   542     Bool retval;
   543     int dotclock;
   544     XF86VidModeModeLine l;
   545     SDL_zerop(info);
   546     SDL_zero(l);
   547     retval = X11_XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   548     info->dotclock = dotclock;
   549     info->hdisplay = l.hdisplay;
   550     info->hsyncstart = l.hsyncstart;
   551     info->hsyncend = l.hsyncend;
   552     info->htotal = l.htotal;
   553     info->hskew = l.hskew;
   554     info->vdisplay = l.vdisplay;
   555     info->vsyncstart = l.vsyncstart;
   556     info->vsyncend = l.vsyncend;
   557     info->vtotal = l.vtotal;
   558     info->flags = l.flags;
   559     info->privsize = l.privsize;
   560     info->private = l.private;
   561     return retval;
   562 }
   563 
   564 static int
   565 CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info)
   566 {
   567     return (info->htotal
   568             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   569                                                          info->vtotal)) : 0;
   570 }
   571 
   572 static SDL_bool
   573 SetXVidModeModeInfo(const XF86VidModeModeInfo *info, SDL_DisplayMode *mode)
   574 {
   575     mode->w = info->hdisplay;
   576     mode->h = info->vdisplay;
   577     mode->refresh_rate = CalculateXVidModeRefreshRate(info);
   578     ((SDL_DisplayModeData*)mode->driverdata)->vm_mode = *info;
   579     return SDL_TRUE;
   580 }
   581 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   582 
   583 int
   584 X11_InitModes(_THIS)
   585 {
   586     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   587     int snum, screen, screencount = 0;
   588 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   589     int xinerama_major, xinerama_minor;
   590     int use_xinerama = 0;
   591     XineramaScreenInfo *xinerama = NULL;
   592 #endif
   593 #if SDL_VIDEO_DRIVER_X11_XRANDR
   594     int xrandr_major, xrandr_minor;
   595 #endif
   596 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   597     int vm_major, vm_minor;
   598     int use_vidmode = 0;
   599 #endif
   600 
   601 /* XRandR is the One True Modern Way to do this on X11. If it's enabled and
   602    available, don't even look at other ways of doing things. */
   603 #if SDL_VIDEO_DRIVER_X11_XRANDR
   604     /* require at least XRandR v1.3 */
   605     if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
   606         (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 3))) {
   607         if (X11_InitModes_XRandR(_this) == 0)
   608             return 0;
   609     }
   610 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   611 
   612 /* !!! FIXME: eventually remove support for Xinerama and XVidMode (everything below here). */
   613 
   614     /* This is a workaround for some apps (UnrealEngine4, for example) until
   615        we sort out the ramifications of removing XVidMode support outright.
   616        This block should be removed with the XVidMode support. */
   617     {
   618         if (SDL_GetHintBoolean("SDL_VIDEO_X11_REQUIRE_XRANDR", SDL_FALSE)) {
   619             #if SDL_VIDEO_DRIVER_X11_XRANDR
   620             return SDL_SetError("XRandR support is required but not available");
   621             #else
   622             return SDL_SetError("XRandR support is required but not built into SDL!");
   623             #endif
   624         }
   625     }
   626 
   627 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   628     /* Query Xinerama extention
   629      * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
   630      *       or newer of the Nvidia binary drivers
   631      */
   632     if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
   633         int (*handler) (Display *, XErrorEvent *);
   634         X11_XSync(data->display, False);
   635         handler = X11_XSetErrorHandler(X11_XineramaFailed);
   636         xinerama = X11_XineramaQueryScreens(data->display, &screencount);
   637         X11_XSync(data->display, False);
   638         X11_XSetErrorHandler(handler);
   639         if (xinerama_triggered_error) {
   640             xinerama = 0;
   641         }
   642         if (xinerama) {
   643             use_xinerama = xinerama_major * 100 + xinerama_minor;
   644         }
   645     }
   646     if (!xinerama) {
   647         screencount = ScreenCount(data->display);
   648     }
   649 #else
   650     screencount = ScreenCount(data->display);
   651 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   652 
   653 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   654     if (CheckVidMode(data->display, &vm_major, &vm_minor)) {
   655         use_vidmode = vm_major * 100 + vm_minor;
   656     }
   657 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   658 
   659     for (snum = 0; snum < screencount; ++snum) {
   660         XVisualInfo vinfo;
   661         SDL_VideoDisplay display;
   662         SDL_DisplayData *displaydata;
   663         SDL_DisplayMode mode;
   664         SDL_DisplayModeData *modedata;
   665         XPixmapFormatValues *pixmapFormats;
   666         char display_name[128];
   667         int i, n;
   668 
   669         /* Re-order screens to always put default screen first */
   670         if (snum == 0) {
   671             screen = DefaultScreen(data->display);
   672         } else if (snum == DefaultScreen(data->display)) {
   673             screen = 0;
   674         } else {
   675             screen = snum;
   676         }
   677 
   678 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   679         if (xinerama) {
   680             if (get_visualinfo(data->display, 0, &vinfo) < 0) {
   681                 continue;
   682             }
   683         } else {
   684             if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   685                 continue;
   686             }
   687         }
   688 #else
   689         if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   690             continue;
   691         }
   692 #endif
   693 
   694         displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   695         if (!displaydata) {
   696             continue;
   697         }
   698         display_name[0] = '\0';
   699 
   700         mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
   701         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   702             /* We don't support palettized modes now */
   703             SDL_free(displaydata);
   704             continue;
   705         }
   706 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   707         if (xinerama) {
   708             mode.w = xinerama[screen].width;
   709             mode.h = xinerama[screen].height;
   710         } else {
   711             mode.w = DisplayWidth(data->display, screen);
   712             mode.h = DisplayHeight(data->display, screen);
   713         }
   714 #else
   715         mode.w = DisplayWidth(data->display, screen);
   716         mode.h = DisplayHeight(data->display, screen);
   717 #endif
   718         mode.refresh_rate = 0;
   719 
   720         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   721         if (!modedata) {
   722             SDL_free(displaydata);
   723             continue;
   724         }
   725         mode.driverdata = modedata;
   726 
   727 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   728         /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
   729          * there's only one screen available. So we force the screen number to zero and
   730          * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
   731          */
   732         if (use_xinerama) {
   733             displaydata->screen = 0;
   734             displaydata->use_xinerama = use_xinerama;
   735             displaydata->xinerama_info = xinerama[screen];
   736             displaydata->xinerama_screen = screen;
   737         }
   738         else displaydata->screen = screen;
   739 #else
   740         displaydata->screen = screen;
   741 #endif
   742         displaydata->visual = vinfo.visual;
   743         displaydata->depth = vinfo.depth;
   744 
   745         /* We use the displaydata screen index here so that this works
   746            for both the Xinerama case, where we get the overall DPI,
   747            and the regular X11 screen info case. */
   748         displaydata->hdpi = (float)DisplayWidth(data->display, displaydata->screen) * 25.4f /
   749             DisplayWidthMM(data->display, displaydata->screen);
   750         displaydata->vdpi = (float)DisplayHeight(data->display, displaydata->screen) * 25.4f /
   751             DisplayHeightMM(data->display, displaydata->screen);
   752         displaydata->ddpi = SDL_ComputeDiagonalDPI(DisplayWidth(data->display, displaydata->screen),
   753                                                    DisplayHeight(data->display, displaydata->screen),
   754                                                    (float)DisplayWidthMM(data->display, displaydata->screen) / 25.4f,
   755                                                    (float)DisplayHeightMM(data->display, displaydata->screen) / 25.4f);
   756 
   757         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   758         pixmapFormats = X11_XListPixmapFormats(data->display, &n);
   759         if (pixmapFormats) {
   760             for (i = 0; i < n; ++i) {
   761                 if (pixmapFormats[i].depth == displaydata->depth) {
   762                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   763                     break;
   764                 }
   765             }
   766             X11_XFree(pixmapFormats);
   767         }
   768 
   769 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   770         if (use_xinerama) {
   771             displaydata->x = xinerama[screen].x_org;
   772             displaydata->y = xinerama[screen].y_org;
   773         }
   774         else
   775 #endif
   776         {
   777             displaydata->x = 0;
   778             displaydata->y = 0;
   779         }
   780 
   781 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   782         if (!displaydata->use_xrandr &&
   783 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   784             /* XVidMode only works on the screen at the origin */
   785             (!displaydata->use_xinerama ||
   786              (displaydata->x == 0 && displaydata->y == 0)) &&
   787 #endif
   788             use_vidmode) {
   789             displaydata->use_vidmode = use_vidmode;
   790             if (displaydata->use_xinerama) {
   791                 displaydata->vidmode_screen = 0;
   792             } else {
   793                 displaydata->vidmode_screen = screen;
   794             }
   795             XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode);
   796         }
   797 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   798 
   799         SDL_zero(display);
   800         if (*display_name) {
   801             display.name = display_name;
   802         }
   803         display.desktop_mode = mode;
   804         display.current_mode = mode;
   805         display.driverdata = displaydata;
   806         SDL_AddVideoDisplay(&display);
   807     }
   808 
   809 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   810     if (xinerama) X11_XFree(xinerama);
   811 #endif
   812 
   813     if (_this->num_displays == 0) {
   814         return SDL_SetError("No available displays");
   815     }
   816     return 0;
   817 }
   818 
   819 void
   820 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   821 {
   822     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   823     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   824 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   825     int nmodes;
   826     XF86VidModeModeInfo ** modes;
   827 #endif
   828     int screen_w;
   829     int screen_h;
   830     SDL_DisplayMode mode;
   831 
   832     /* Unfortunately X11 requires the window to be created with the correct
   833      * visual and depth ahead of time, but the SDL API allows you to create
   834      * a window before setting the fullscreen display mode.  This means that
   835      * we have to use the same format for all windows and all display modes.
   836      * (or support recreating the window with a new visual behind the scenes)
   837      */
   838     mode.format = sdl_display->current_mode.format;
   839     mode.driverdata = NULL;
   840 
   841     screen_w = DisplayWidth(display, data->screen);
   842     screen_h = DisplayHeight(display, data->screen);
   843 
   844 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   845     if (data->use_xinerama) {
   846         if (data->use_vidmode && !data->xinerama_info.x_org && !data->xinerama_info.y_org &&
   847            (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) {
   848             SDL_DisplayModeData *modedata;
   849             /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0
   850              * if we're using vidmode.
   851              */
   852             mode.w = screen_w;
   853             mode.h = screen_h;
   854             mode.refresh_rate = 0;
   855             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   856             if (modedata) {
   857                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   858             }
   859             mode.driverdata = modedata;
   860             if (!SDL_AddDisplayMode(sdl_display, &mode)) {
   861                 SDL_free(modedata);
   862             }
   863         }
   864         else if (!data->use_xrandr)
   865         {
   866             SDL_DisplayModeData *modedata;
   867             /* Add the current mode of each monitor otherwise if we can't get them from xrandr */
   868             mode.w = data->xinerama_info.width;
   869             mode.h = data->xinerama_info.height;
   870             mode.refresh_rate = 0;
   871             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   872             if (modedata) {
   873                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   874             }
   875             mode.driverdata = modedata;
   876             if (!SDL_AddDisplayMode(sdl_display, &mode)) {
   877                 SDL_free(modedata);
   878             }
   879         }
   880 
   881     }
   882 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   883 
   884 #if SDL_VIDEO_DRIVER_X11_XRANDR
   885     if (data->use_xrandr) {
   886         XRRScreenResources *res;
   887 
   888         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   889         if (res) {
   890             SDL_DisplayModeData *modedata;
   891             XRROutputInfo *output_info;
   892             int i;
   893 
   894             output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   895             if (output_info && output_info->connection != RR_Disconnected) {
   896                 for (i = 0; i < output_info->nmode; ++i) {
   897                     modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   898                     if (!modedata) {
   899                         continue;
   900                     }
   901                     mode.driverdata = modedata;
   902 
   903                     if (!SetXRandRModeInfo(display, res, output_info->crtc, output_info->modes[i], &mode) ||
   904                         !SDL_AddDisplayMode(sdl_display, &mode)) {
   905                         SDL_free(modedata);
   906                     }
   907                 }
   908             }
   909             X11_XRRFreeOutputInfo(output_info);
   910             X11_XRRFreeScreenResources(res);
   911         }
   912         return;
   913     }
   914 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   915 
   916 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   917     if (data->use_vidmode &&
   918         X11_XF86VidModeGetAllModeLines(display, data->vidmode_screen, &nmodes, &modes)) {
   919         int i;
   920         SDL_DisplayModeData *modedata;
   921 
   922 #ifdef X11MODES_DEBUG
   923         printf("VidMode modes: (unsorted)\n");
   924         for (i = 0; i < nmodes; ++i) {
   925             printf("Mode %d: %d x %d @ %d, flags: 0x%x\n", i,
   926                    modes[i]->hdisplay, modes[i]->vdisplay,
   927                    CalculateXVidModeRefreshRate(modes[i]), modes[i]->flags);
   928         }
   929 #endif
   930         for (i = 0; i < nmodes; ++i) {
   931             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   932             if (!modedata) {
   933                 continue;
   934             }
   935             mode.driverdata = modedata;
   936 
   937             if (!SetXVidModeModeInfo(modes[i], &mode) || !SDL_AddDisplayMode(sdl_display, &mode)) {
   938                 SDL_free(modedata);
   939             }
   940         }
   941         X11_XFree(modes);
   942         return;
   943     }
   944 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   945 
   946     if (!data->use_xrandr && !data->use_vidmode) {
   947         SDL_DisplayModeData *modedata;
   948         /* Add the desktop mode */
   949         mode = sdl_display->desktop_mode;
   950         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   951         if (modedata) {
   952             *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   953         }
   954         mode.driverdata = modedata;
   955         if (!SDL_AddDisplayMode(sdl_display, &mode)) {
   956             SDL_free(modedata);
   957         }
   958     }
   959 }
   960 
   961 int
   962 X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode)
   963 {
   964     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   965     Display *display = viddata->display;
   966     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   967     SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   968 
   969     viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
   970 
   971 #if SDL_VIDEO_DRIVER_X11_XRANDR
   972     if (data->use_xrandr) {
   973         XRRScreenResources *res;
   974         XRROutputInfo *output_info;
   975         XRRCrtcInfo *crtc;
   976         Status status;
   977 
   978         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   979         if (!res) {
   980             return SDL_SetError("Couldn't get XRandR screen resources");
   981         }
   982 
   983         output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   984         if (!output_info || output_info->connection == RR_Disconnected) {
   985             X11_XRRFreeScreenResources(res);
   986             return SDL_SetError("Couldn't get XRandR output info");
   987         }
   988 
   989         crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
   990         if (!crtc) {
   991             X11_XRRFreeOutputInfo(output_info);
   992             X11_XRRFreeScreenResources(res);
   993             return SDL_SetError("Couldn't get XRandR crtc info");
   994         }
   995 
   996         status = X11_XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime,
   997           crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation,
   998           &data->xrandr_output, 1);
   999 
  1000         X11_XRRFreeCrtcInfo(crtc);
  1001         X11_XRRFreeOutputInfo(output_info);
  1002         X11_XRRFreeScreenResources(res);
  1003 
  1004         if (status != Success) {
  1005             return SDL_SetError("X11_XRRSetCrtcConfig failed");
  1006         }
  1007     }
  1008 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
  1009 
  1010 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
  1011     if (data->use_vidmode) {
  1012         X11_XF86VidModeSwitchToMode(display, data->vidmode_screen, &modedata->vm_mode);
  1013     }
  1014 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
  1015 
  1016     return 0;
  1017 }
  1018 
  1019 void
  1020 X11_QuitModes(_THIS)
  1021 {
  1022 }
  1023 
  1024 int
  1025 X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
  1026 {
  1027     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
  1028 
  1029     rect->x = data->x;
  1030     rect->y = data->y;
  1031     rect->w = sdl_display->current_mode.w;
  1032     rect->h = sdl_display->current_mode.h;
  1033 
  1034 #if SDL_VIDEO_DRIVER_X11_XINERAMA
  1035     /* Get the real current bounds of the display */
  1036     if (data->use_xinerama) {
  1037         Display *display = ((SDL_VideoData *) _this->driverdata)->display;
  1038         int screencount;
  1039         XineramaScreenInfo *xinerama = X11_XineramaQueryScreens(display, &screencount);
  1040         if (xinerama) {
  1041             rect->x = xinerama[data->xinerama_screen].x_org;
  1042             rect->y = xinerama[data->xinerama_screen].y_org;
  1043             X11_XFree(xinerama);
  1044         }
  1045     }
  1046 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
  1047     return 0;
  1048 }
  1049 
  1050 int
  1051 X11_GetDisplayDPI(_THIS, SDL_VideoDisplay * sdl_display, float * ddpi, float * hdpi, float * vdpi)
  1052 {
  1053     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
  1054 
  1055     if (ddpi) {
  1056         *ddpi = data->ddpi;
  1057     }
  1058     if (hdpi) {
  1059         *hdpi = data->hdpi;
  1060     }
  1061     if (vdpi) {
  1062         *vdpi = data->vdpi;
  1063     }
  1064 
  1065     return data->ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
  1066 }
  1067 
  1068 int
  1069 X11_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
  1070 {
  1071     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
  1072     Display *display = data->display;
  1073     Atom _NET_WORKAREA;
  1074     int status, real_format;
  1075     int retval = -1;
  1076     Atom real_type;
  1077     unsigned long items_read = 0, items_left = 0;
  1078     unsigned char *propdata = NULL;
  1079 
  1080     if (X11_GetDisplayBounds(_this, sdl_display, rect) < 0) {
  1081         return -1;
  1082     }
  1083 
  1084     _NET_WORKAREA = X11_XInternAtom(display, "_NET_WORKAREA", False);
  1085     status = X11_XGetWindowProperty(display, DefaultRootWindow(display),
  1086                                     _NET_WORKAREA, 0L, 4L, False, XA_CARDINAL,
  1087                                     &real_type, &real_format, &items_read,
  1088                                     &items_left, &propdata);
  1089     if ((status == Success) && (items_read >= 4)) {
  1090         const long *p = (long*) propdata;
  1091         const SDL_Rect usable = { (int)p[0], (int)p[1], (int)p[2], (int)p[3] };
  1092         retval = 0;
  1093         if (!SDL_IntersectRect(rect, &usable, rect)) {
  1094             SDL_zerop(rect);
  1095         }
  1096     }
  1097 
  1098     if (propdata) {
  1099         X11_XFree(propdata);
  1100     }
  1101 
  1102     return retval;
  1103 }
  1104 
  1105 #endif /* SDL_VIDEO_DRIVER_X11 */
  1106 
  1107 /* vi: set ts=4 sw=4 expandtab: */