sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_x11modes.c (37671B)


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