src/video/x11/SDL_x11modes.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 06 Nov 2016 09:30:06 -0800
changeset 10586 c8ca00149330
parent 10499 363c1c7e7a41
child 10604 27d0fb08d755
permissions -rw-r--r--
Fixed bug 3476 - round() needs _GNU_SOURCE on some old systems

Ozkan Sezer

On systems with old glibc, such mine with glibc-2.8, the following warning
is issued and is fixed easily by defining _GNU_SOURCE:

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