src/video/x11/SDL_x11modes.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 05 Sep 2017 08:24:38 -0700
changeset 11454 f6b9e7bd038a
parent 11344 039ef928e200
child 11689 64ec7aae69e3
permissions -rw-r--r--
Fixed bug 3273 - Fix for slow video subsystem initialization when using XRandR.

Martín Golini

I'm having a very slow initialization of the video subsystem that locks the window creation for about 500 ms ( tested in at least 4 different systems ). What i found is that X11_InitModes_XRandR is using XRRGetScreenResources, that explicitly ask to poll the hardware for changes. This is not really necessary since if the data is already available you can use XRRGetScreenResourcesCurrent.
I attached a tentative patch that fix this issue. With the patch there's no lock when the subsystem is initialized and the window creation is instant in my applications. The patch only uses XRRGetScreenResourcesCurrent in X11_InitModes_XRandR but it could be potentially used in X11_GetDisplayModes and X11_SetDisplayMode.
     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_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen));
   400             if (!res || res->noutput == 0) {
   401                 if (res) {
   402                     X11_XRRFreeScreenResources(res);
   403                 }
   404 
   405                 res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen));
   406                 if (!res) {
   407                     continue;
   408                 }
   409             }
   410 
   411             for (output = 0; output < res->noutput; output++) {
   412                 XRROutputInfo *output_info;
   413                 int display_x, display_y;
   414                 unsigned long display_mm_width, display_mm_height;
   415                 SDL_DisplayData *displaydata;
   416                 char display_name[128];
   417                 SDL_DisplayMode mode;
   418                 SDL_DisplayModeData *modedata;
   419                 SDL_VideoDisplay display;
   420                 RRMode modeID;
   421                 RRCrtc output_crtc;
   422                 XRRCrtcInfo *crtc;
   423 
   424                 /* The primary output _should_ always be sorted first, but just in case... */
   425                 if ((looking_for_primary && (res->outputs[output] != primary)) ||
   426                     (!looking_for_primary && (screen == default_screen) && (res->outputs[output] == primary))) {
   427                     continue;
   428                 }
   429 
   430                 output_info = X11_XRRGetOutputInfo(dpy, res, res->outputs[output]);
   431                 if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) {
   432                     X11_XRRFreeOutputInfo(output_info);
   433                     continue;
   434                 }
   435 
   436                 SDL_strlcpy(display_name, output_info->name, sizeof(display_name));
   437                 display_mm_width = output_info->mm_width;
   438                 display_mm_height = output_info->mm_height;
   439                 output_crtc = output_info->crtc;
   440                 X11_XRRFreeOutputInfo(output_info);
   441 
   442                 crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc);
   443                 if (!crtc) {
   444                     continue;
   445                 }
   446 
   447                 SDL_zero(mode);
   448                 modeID = crtc->mode;
   449                 mode.w = crtc->width;
   450                 mode.h = crtc->height;
   451                 mode.format = pixelformat;
   452 
   453                 display_x = crtc->x;
   454                 display_y = crtc->y;
   455 
   456                 X11_XRRFreeCrtcInfo(crtc);
   457 
   458                 displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   459                 if (!displaydata) {
   460                     return SDL_OutOfMemory();
   461                 }
   462 
   463                 modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   464                 if (!modedata) {
   465                     SDL_free(displaydata);
   466                     return SDL_OutOfMemory();
   467                 }
   468                 modedata->xrandr_mode = modeID;
   469                 mode.driverdata = modedata;
   470 
   471                 displaydata->screen = screen;
   472                 displaydata->visual = vinfo.visual;
   473                 displaydata->depth = vinfo.depth;
   474                 displaydata->hdpi = display_mm_width ? (((float) mode.w) * 25.4f / display_mm_width) : 0.0f;
   475                 displaydata->vdpi = display_mm_height ? (((float) mode.h) * 25.4f / display_mm_height) : 0.0f;
   476                 displaydata->ddpi = SDL_ComputeDiagonalDPI(mode.w, mode.h, ((float) display_mm_width) / 25.4f,((float) display_mm_height) / 25.4f);
   477                 displaydata->scanline_pad = scanline_pad;
   478                 displaydata->x = display_x;
   479                 displaydata->y = display_y;
   480                 displaydata->use_xrandr = 1;
   481                 displaydata->xrandr_output = res->outputs[output];
   482 
   483                 SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode);
   484                 SetXRandRDisplayName(dpy, EDID, display_name, sizeof (display_name), res->outputs[output], display_mm_width, display_mm_height);
   485 
   486                 SDL_zero(display);
   487                 if (*display_name) {
   488                     display.name = display_name;
   489                 }
   490                 display.desktop_mode = mode;
   491                 display.current_mode = mode;
   492                 display.driverdata = displaydata;
   493                 SDL_AddVideoDisplay(&display);
   494             }
   495 
   496             X11_XRRFreeScreenResources(res);
   497         }
   498     }
   499 
   500     if (_this->num_displays == 0) {
   501         return SDL_SetError("No available displays");
   502     }
   503 
   504     return 0;
   505 }
   506 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   507 
   508 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   509 static SDL_bool
   510 CheckVidMode(Display * display, int *major, int *minor)
   511 {
   512     /* Default the extension not available */
   513     *major = *minor = 0;
   514 
   515     /* Allow environment override */
   516     if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XVIDMODE, SDL_TRUE)) {
   517 #ifdef X11MODES_DEBUG
   518         printf("XVidMode disabled due to hint\n");
   519 #endif
   520         return SDL_FALSE;
   521     }
   522 
   523     if (!SDL_X11_HAVE_XVIDMODE) {
   524 #ifdef X11MODES_DEBUG
   525         printf("XVidMode support not available\n");
   526 #endif
   527         return SDL_FALSE;
   528     }
   529 
   530     /* Query the extension version */
   531     vm_error = -1;
   532     if (!X11_XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   533         || !X11_XF86VidModeQueryVersion(display, major, minor)) {
   534 #ifdef X11MODES_DEBUG
   535         printf("XVidMode not active on the display\n");
   536 #endif
   537         return SDL_FALSE;
   538     }
   539 #ifdef X11MODES_DEBUG
   540     printf("XVidMode available at version %d.%d!\n", *major, *minor);
   541 #endif
   542     return SDL_TRUE;
   543 }
   544 
   545 static
   546 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   547                                        XF86VidModeModeInfo* info)
   548 {
   549     Bool retval;
   550     int dotclock;
   551     XF86VidModeModeLine l;
   552     SDL_zerop(info);
   553     SDL_zero(l);
   554     retval = X11_XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   555     info->dotclock = dotclock;
   556     info->hdisplay = l.hdisplay;
   557     info->hsyncstart = l.hsyncstart;
   558     info->hsyncend = l.hsyncend;
   559     info->htotal = l.htotal;
   560     info->hskew = l.hskew;
   561     info->vdisplay = l.vdisplay;
   562     info->vsyncstart = l.vsyncstart;
   563     info->vsyncend = l.vsyncend;
   564     info->vtotal = l.vtotal;
   565     info->flags = l.flags;
   566     info->privsize = l.privsize;
   567     info->private = l.private;
   568     return retval;
   569 }
   570 
   571 static int
   572 CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info)
   573 {
   574     return (info->htotal
   575             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   576                                                          info->vtotal)) : 0;
   577 }
   578 
   579 static SDL_bool
   580 SetXVidModeModeInfo(const XF86VidModeModeInfo *info, SDL_DisplayMode *mode)
   581 {
   582     mode->w = info->hdisplay;
   583     mode->h = info->vdisplay;
   584     mode->refresh_rate = CalculateXVidModeRefreshRate(info);
   585     ((SDL_DisplayModeData*)mode->driverdata)->vm_mode = *info;
   586     return SDL_TRUE;
   587 }
   588 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   589 
   590 int
   591 X11_InitModes(_THIS)
   592 {
   593     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   594     int snum, screen, screencount = 0;
   595 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   596     int xinerama_major, xinerama_minor;
   597     int use_xinerama = 0;
   598     XineramaScreenInfo *xinerama = NULL;
   599 #endif
   600 #if SDL_VIDEO_DRIVER_X11_XRANDR
   601     int xrandr_major, xrandr_minor;
   602 #endif
   603 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   604     int vm_major, vm_minor;
   605     int use_vidmode = 0;
   606 #endif
   607 
   608 /* XRandR is the One True Modern Way to do this on X11. If it's enabled and
   609    available, don't even look at other ways of doing things. */
   610 #if SDL_VIDEO_DRIVER_X11_XRANDR
   611     /* require at least XRandR v1.3 */
   612     if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
   613         (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 3))) {
   614         if (X11_InitModes_XRandR(_this) == 0)
   615             return 0;
   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         if (SDL_GetHintBoolean("SDL_VIDEO_X11_REQUIRE_XRANDR", SDL_FALSE)) {
   626             #if SDL_VIDEO_DRIVER_X11_XRANDR
   627             return SDL_SetError("XRandR support is required but not available");
   628             #else
   629             return SDL_SetError("XRandR support is required but not built into SDL!");
   630             #endif
   631         }
   632     }
   633 
   634 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   635     /* Query Xinerama extention
   636      * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
   637      *       or newer of the Nvidia binary drivers
   638      */
   639     if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
   640         int (*handler) (Display *, XErrorEvent *);
   641         X11_XSync(data->display, False);
   642         handler = X11_XSetErrorHandler(X11_XineramaFailed);
   643         xinerama = X11_XineramaQueryScreens(data->display, &screencount);
   644         X11_XSync(data->display, False);
   645         X11_XSetErrorHandler(handler);
   646         if (xinerama_triggered_error) {
   647             xinerama = 0;
   648         }
   649         if (xinerama) {
   650             use_xinerama = xinerama_major * 100 + xinerama_minor;
   651         }
   652     }
   653     if (!xinerama) {
   654         screencount = ScreenCount(data->display);
   655     }
   656 #else
   657     screencount = ScreenCount(data->display);
   658 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   659 
   660 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   661     if (CheckVidMode(data->display, &vm_major, &vm_minor)) {
   662         use_vidmode = vm_major * 100 + vm_minor;
   663     }
   664 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   665 
   666     for (snum = 0; snum < screencount; ++snum) {
   667         XVisualInfo vinfo;
   668         SDL_VideoDisplay display;
   669         SDL_DisplayData *displaydata;
   670         SDL_DisplayMode mode;
   671         SDL_DisplayModeData *modedata;
   672         XPixmapFormatValues *pixmapFormats;
   673         char display_name[128];
   674         int i, n;
   675 
   676         /* Re-order screens to always put default screen first */
   677         if (snum == 0) {
   678             screen = DefaultScreen(data->display);
   679         } else if (snum == DefaultScreen(data->display)) {
   680             screen = 0;
   681         } else {
   682             screen = snum;
   683         }
   684 
   685 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   686         if (xinerama) {
   687             if (get_visualinfo(data->display, 0, &vinfo) < 0) {
   688                 continue;
   689             }
   690         } else {
   691             if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   692                 continue;
   693             }
   694         }
   695 #else
   696         if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   697             continue;
   698         }
   699 #endif
   700 
   701         displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   702         if (!displaydata) {
   703             continue;
   704         }
   705         display_name[0] = '\0';
   706 
   707         mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
   708         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   709             /* We don't support palettized modes now */
   710             SDL_free(displaydata);
   711             continue;
   712         }
   713 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   714         if (xinerama) {
   715             mode.w = xinerama[screen].width;
   716             mode.h = xinerama[screen].height;
   717         } else {
   718             mode.w = DisplayWidth(data->display, screen);
   719             mode.h = DisplayHeight(data->display, screen);
   720         }
   721 #else
   722         mode.w = DisplayWidth(data->display, screen);
   723         mode.h = DisplayHeight(data->display, screen);
   724 #endif
   725         mode.refresh_rate = 0;
   726 
   727         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   728         if (!modedata) {
   729             SDL_free(displaydata);
   730             continue;
   731         }
   732         mode.driverdata = modedata;
   733 
   734 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   735         /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
   736          * there's only one screen available. So we force the screen number to zero and
   737          * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
   738          */
   739         if (use_xinerama) {
   740             displaydata->screen = 0;
   741             displaydata->use_xinerama = use_xinerama;
   742             displaydata->xinerama_info = xinerama[screen];
   743             displaydata->xinerama_screen = screen;
   744         }
   745         else displaydata->screen = screen;
   746 #else
   747         displaydata->screen = screen;
   748 #endif
   749         displaydata->visual = vinfo.visual;
   750         displaydata->depth = vinfo.depth;
   751 
   752         /* We use the displaydata screen index here so that this works
   753            for both the Xinerama case, where we get the overall DPI,
   754            and the regular X11 screen info case. */
   755         displaydata->hdpi = (float)DisplayWidth(data->display, displaydata->screen) * 25.4f /
   756             DisplayWidthMM(data->display, displaydata->screen);
   757         displaydata->vdpi = (float)DisplayHeight(data->display, displaydata->screen) * 25.4f /
   758             DisplayHeightMM(data->display, displaydata->screen);
   759         displaydata->ddpi = SDL_ComputeDiagonalDPI(DisplayWidth(data->display, displaydata->screen),
   760                                                    DisplayHeight(data->display, displaydata->screen),
   761                                                    (float)DisplayWidthMM(data->display, displaydata->screen) / 25.4f,
   762                                                    (float)DisplayHeightMM(data->display, displaydata->screen) / 25.4f);
   763 
   764         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   765         pixmapFormats = X11_XListPixmapFormats(data->display, &n);
   766         if (pixmapFormats) {
   767             for (i = 0; i < n; ++i) {
   768                 if (pixmapFormats[i].depth == displaydata->depth) {
   769                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   770                     break;
   771                 }
   772             }
   773             X11_XFree(pixmapFormats);
   774         }
   775 
   776 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   777         if (use_xinerama) {
   778             displaydata->x = xinerama[screen].x_org;
   779             displaydata->y = xinerama[screen].y_org;
   780         }
   781         else
   782 #endif
   783         {
   784             displaydata->x = 0;
   785             displaydata->y = 0;
   786         }
   787 
   788 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   789         if (!displaydata->use_xrandr &&
   790 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   791             /* XVidMode only works on the screen at the origin */
   792             (!displaydata->use_xinerama ||
   793              (displaydata->x == 0 && displaydata->y == 0)) &&
   794 #endif
   795             use_vidmode) {
   796             displaydata->use_vidmode = use_vidmode;
   797             if (displaydata->use_xinerama) {
   798                 displaydata->vidmode_screen = 0;
   799             } else {
   800                 displaydata->vidmode_screen = screen;
   801             }
   802             XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode);
   803         }
   804 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   805 
   806         SDL_zero(display);
   807         if (*display_name) {
   808             display.name = display_name;
   809         }
   810         display.desktop_mode = mode;
   811         display.current_mode = mode;
   812         display.driverdata = displaydata;
   813         SDL_AddVideoDisplay(&display);
   814     }
   815 
   816 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   817     if (xinerama) X11_XFree(xinerama);
   818 #endif
   819 
   820     if (_this->num_displays == 0) {
   821         return SDL_SetError("No available displays");
   822     }
   823     return 0;
   824 }
   825 
   826 void
   827 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   828 {
   829     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   830     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   831 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   832     int nmodes;
   833     XF86VidModeModeInfo ** modes;
   834 #endif
   835     SDL_DisplayMode mode;
   836 
   837     /* Unfortunately X11 requires the window to be created with the correct
   838      * visual and depth ahead of time, but the SDL API allows you to create
   839      * a window before setting the fullscreen display mode.  This means that
   840      * we have to use the same format for all windows and all display modes.
   841      * (or support recreating the window with a new visual behind the scenes)
   842      */
   843     mode.format = sdl_display->current_mode.format;
   844     mode.driverdata = NULL;
   845 
   846 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   847     if (data->use_xinerama) {
   848         int screen_w;
   849         int screen_h;
   850 
   851         screen_w = DisplayWidth(display, data->screen);
   852         screen_h = DisplayHeight(display, data->screen);
   853 
   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 : SDL_SetError("Couldn't get DPI");
  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         const long *p = (long*) propdata;
  1099         const SDL_Rect usable = { (int)p[0], (int)p[1], (int)p[2], (int)p[3] };
  1100         retval = 0;
  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: */