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