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