src/video/x11/SDL_x11modes.c
author Alfred Reynolds <alfred@valvesoftware.com>
Wed, 29 Jul 2015 17:19:02 -0700
changeset 9814 4df28b087060
parent 9789 137993bad5c7
child 9815 1db11af8159e
permissions -rw-r--r--
Add X11 implementation of SDL_GetDisplayDPI.
     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     if (!X11_XRRQueryVersion(display, major, minor)) {
   248 #ifdef X11MODES_DEBUG
   249         printf("XRandR not active on the display\n");
   250 #endif
   251         return SDL_FALSE;
   252     }
   253 #ifdef X11MODES_DEBUG
   254     printf("XRandR available at version %d.%d!\n", *major, *minor);
   255 #endif
   256     return SDL_TRUE;
   257 }
   258 
   259 #define XRANDR_ROTATION_LEFT    (1 << 1)
   260 #define XRANDR_ROTATION_RIGHT   (1 << 3)
   261 
   262 static int
   263 CalculateXRandRRefreshRate(const XRRModeInfo *info)
   264 {
   265     return (info->hTotal
   266             && info->vTotal) ? (info->dotClock / (info->hTotal * info->vTotal)) : 0;
   267 }
   268 
   269 static SDL_bool
   270 SetXRandRModeInfo(Display *display, XRRScreenResources *res, XRROutputInfo *output_info,
   271                   RRMode modeID, SDL_DisplayMode *mode)
   272 {
   273     int i;
   274     for (i = 0; i < res->nmode; ++i) {
   275         if (res->modes[i].id == modeID) {
   276             XRRCrtcInfo *crtc;
   277             Rotation rotation = 0;
   278             const XRRModeInfo *info = &res->modes[i];
   279 
   280             crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
   281             if (crtc) {
   282                 rotation = crtc->rotation;
   283                 X11_XRRFreeCrtcInfo(crtc);
   284             }
   285 
   286             if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) {
   287                 mode->w = info->height;
   288                 mode->h = info->width;
   289             } else {
   290                 mode->w = info->width;
   291                 mode->h = info->height;
   292             }
   293             mode->refresh_rate = CalculateXRandRRefreshRate(info);
   294             ((SDL_DisplayModeData*)mode->driverdata)->xrandr_mode = modeID;
   295 #ifdef X11MODES_DEBUG
   296             printf("XRandR mode %d: %dx%d@%dHz\n", (int) modeID, mode->w, mode->h, mode->refresh_rate);
   297 #endif
   298             return SDL_TRUE;
   299         }
   300     }
   301     return SDL_FALSE;
   302 }
   303 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   304 
   305 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   306 static SDL_bool
   307 CheckVidMode(Display * display, int *major, int *minor)
   308 {
   309     const char *env;
   310 
   311     /* Default the extension not available */
   312     *major = *minor = 0;
   313 
   314     /* Allow environment override */
   315     env = SDL_GetHint(SDL_HINT_VIDEO_X11_XVIDMODE);
   316     if (env && !SDL_atoi(env)) {
   317 #ifdef X11MODES_DEBUG
   318         printf("XVidMode disabled due to hint\n");
   319 #endif
   320         return SDL_FALSE;
   321     }
   322 
   323     if (!SDL_X11_HAVE_XVIDMODE) {
   324 #ifdef X11MODES_DEBUG
   325         printf("XVidMode support not available\n");
   326 #endif
   327         return SDL_FALSE;
   328     }
   329 
   330     /* Query the extension version */
   331     vm_error = -1;
   332     if (!X11_XF86VidModeQueryExtension(display, &vm_event, &vm_error)
   333         || !X11_XF86VidModeQueryVersion(display, major, minor)) {
   334 #ifdef X11MODES_DEBUG
   335         printf("XVidMode not active on the display\n");
   336 #endif
   337         return SDL_FALSE;
   338     }
   339 #ifdef X11MODES_DEBUG
   340     printf("XVidMode available at version %d.%d!\n", *major, *minor);
   341 #endif
   342     return SDL_TRUE;
   343 }
   344 
   345 static
   346 Bool XF86VidModeGetModeInfo(Display * dpy, int scr,
   347                                        XF86VidModeModeInfo* info)
   348 {
   349     Bool retval;
   350     int dotclock;
   351     XF86VidModeModeLine l;
   352     SDL_zerop(info);
   353     SDL_zero(l);
   354     retval = X11_XF86VidModeGetModeLine(dpy, scr, &dotclock, &l);
   355     info->dotclock = dotclock;
   356     info->hdisplay = l.hdisplay;
   357     info->hsyncstart = l.hsyncstart;
   358     info->hsyncend = l.hsyncend;
   359     info->htotal = l.htotal;
   360     info->hskew = l.hskew;
   361     info->vdisplay = l.vdisplay;
   362     info->vsyncstart = l.vsyncstart;
   363     info->vsyncend = l.vsyncend;
   364     info->vtotal = l.vtotal;
   365     info->flags = l.flags;
   366     info->privsize = l.privsize;
   367     info->private = l.private;
   368     return retval;
   369 }
   370 
   371 static int
   372 CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info)
   373 {
   374     return (info->htotal
   375             && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
   376                                                          info->vtotal)) : 0;
   377 }
   378 
   379 SDL_bool
   380 SetXVidModeModeInfo(const XF86VidModeModeInfo *info, SDL_DisplayMode *mode)
   381 {
   382     mode->w = info->hdisplay;
   383     mode->h = info->vdisplay;
   384     mode->refresh_rate = CalculateXVidModeRefreshRate(info);
   385     ((SDL_DisplayModeData*)mode->driverdata)->vm_mode = *info;
   386     return SDL_TRUE;
   387 }
   388 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   389 
   390 int
   391 X11_InitModes(_THIS)
   392 {
   393     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
   394     int snum, screen, screencount;
   395 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   396     int xinerama_major, xinerama_minor;
   397     int use_xinerama = 0;
   398     XineramaScreenInfo *xinerama = NULL;
   399 #endif
   400 #if SDL_VIDEO_DRIVER_X11_XRANDR
   401     int xrandr_major, xrandr_minor;
   402     int use_xrandr = 0;
   403     XRRScreenResources *res = NULL;
   404 #endif
   405 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   406     int vm_major, vm_minor;
   407     int use_vidmode = 0;
   408 #endif
   409 
   410 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   411     /* Query Xinerama extention
   412      * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
   413      *       or newer of the Nvidia binary drivers
   414      */
   415     if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
   416         int (*handler) (Display *, XErrorEvent *);
   417         X11_XSync(data->display, False);
   418         handler = X11_XSetErrorHandler(X11_XineramaFailed);
   419         xinerama = X11_XineramaQueryScreens(data->display, &screencount);
   420         X11_XSync(data->display, False);
   421         X11_XSetErrorHandler(handler);
   422         if (xinerama_triggered_error) {
   423             xinerama = 0;
   424         }
   425         if (xinerama) {
   426             use_xinerama = xinerama_major * 100 + xinerama_minor;
   427         }
   428     }
   429     if (!xinerama) {
   430         screencount = ScreenCount(data->display);
   431     }
   432 #else
   433     screencount = ScreenCount(data->display);
   434 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   435 
   436 #if SDL_VIDEO_DRIVER_X11_XRANDR
   437     /* require at least XRandR v1.2 */
   438     if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
   439         (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 2))) {
   440         use_xrandr = xrandr_major * 100 + xrandr_minor;
   441     }
   442 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   443 
   444 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   445     if (CheckVidMode(data->display, &vm_major, &vm_minor)) {
   446         use_vidmode = vm_major * 100 + vm_minor;
   447     }
   448 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   449 
   450     for (snum = 0; snum < screencount; ++snum) {
   451         XVisualInfo vinfo;
   452         SDL_VideoDisplay display;
   453         SDL_DisplayData *displaydata;
   454         SDL_DisplayMode mode;
   455         SDL_DisplayModeData *modedata;
   456         XPixmapFormatValues *pixmapFormats;
   457         char display_name[128];
   458         int i, n;
   459 
   460         /* Re-order screens to always put default screen first */
   461         if (snum == 0) {
   462             screen = DefaultScreen(data->display);
   463         } else if (snum == DefaultScreen(data->display)) {
   464             screen = 0;
   465         } else {
   466             screen = snum;
   467         }
   468 
   469 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   470         if (xinerama) {
   471             if (get_visualinfo(data->display, 0, &vinfo) < 0) {
   472                 continue;
   473             }
   474         } else {
   475             if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   476                 continue;
   477             }
   478         }
   479 #else
   480         if (get_visualinfo(data->display, screen, &vinfo) < 0) {
   481             continue;
   482         }
   483 #endif
   484 
   485         displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
   486         if (!displaydata) {
   487             continue;
   488         }
   489         display_name[0] = '\0';
   490 
   491         mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
   492         if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
   493             /* We don't support palettized modes now */
   494             SDL_free(displaydata);
   495             continue;
   496         }
   497 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   498         if (xinerama) {
   499             mode.w = xinerama[screen].width;
   500             mode.h = xinerama[screen].height;
   501         } else {
   502             mode.w = DisplayWidth(data->display, screen);
   503             mode.h = DisplayHeight(data->display, screen);
   504         }
   505 #else
   506         mode.w = DisplayWidth(data->display, screen);
   507         mode.h = DisplayHeight(data->display, screen);
   508 #endif
   509         mode.refresh_rate = 0;
   510 
   511         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   512         if (!modedata) {
   513             SDL_free(displaydata);
   514             continue;
   515         }
   516         mode.driverdata = modedata;
   517 
   518 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   519         /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
   520          * there's only one screen available. So we force the screen number to zero and
   521          * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
   522          */
   523         if (use_xinerama) {
   524             displaydata->screen = 0;
   525             displaydata->use_xinerama = use_xinerama;
   526             displaydata->xinerama_info = xinerama[screen];
   527             displaydata->xinerama_screen = screen;
   528         }
   529         else displaydata->screen = screen;
   530 #else
   531         displaydata->screen = screen;
   532 #endif
   533         displaydata->visual = vinfo.visual;
   534         displaydata->depth = vinfo.depth;
   535 
   536         // We use the displaydata screen index here so that this works
   537         // for both the Xinerama case, where we get the overall DPI,
   538         // and the regular X11 screen info case.
   539         displaydata->hdpi = (float)DisplayWidth(data->display, displaydata->screen) * 25.4f /
   540             DisplayWidthMM(data->display, displaydata->screen);
   541         displaydata->vdpi = (float)DisplayHeight(data->display, displaydata->screen) * 25.4f /
   542             DisplayHeightMM(data->display, displaydata->screen);
   543         displaydata->ddpi = SDL_ComputeDiagonalDPI(DisplayWidth(data->display, displaydata->screen),
   544                                                    DisplayHeight(data->display, displaydata->screen),
   545                                                    (float)DisplayWidthMM(data->display, displaydata->screen) / 25.4f,
   546                                                    (float)DisplayHeightMM(data->display, displaydata->screen) / 25.4f);
   547 
   548         displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
   549         pixmapFormats = X11_XListPixmapFormats(data->display, &n);
   550         if (pixmapFormats) {
   551             for (i = 0; i < n; ++i) {
   552                 if (pixmapFormats[i].depth == displaydata->depth) {
   553                     displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
   554                     break;
   555                 }
   556             }
   557             X11_XFree(pixmapFormats);
   558         }
   559 
   560 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   561         if (use_xinerama) {
   562             displaydata->x = xinerama[screen].x_org;
   563             displaydata->y = xinerama[screen].y_org;
   564         }
   565         else
   566 #endif
   567         {
   568             displaydata->x = 0;
   569             displaydata->y = 0;
   570         }
   571 
   572 #if SDL_VIDEO_DRIVER_X11_XRANDR
   573         if (use_xrandr) {
   574             res = X11_XRRGetScreenResources(data->display, RootWindow(data->display, displaydata->screen));
   575         }
   576         if (res) {
   577             XRROutputInfo *output_info;
   578             XRRCrtcInfo *crtc;
   579             int output;
   580             Atom EDID = X11_XInternAtom(data->display, "EDID", False);
   581             Atom *props;
   582             int nprop;
   583             unsigned long width_mm;
   584             unsigned long height_mm;
   585             int inches = 0;
   586 
   587             for (output = 0; output < res->noutput; output++) {
   588                 output_info = X11_XRRGetOutputInfo(data->display, res, res->outputs[output]);
   589                 if (!output_info || !output_info->crtc ||
   590                     output_info->connection == RR_Disconnected) {
   591                     X11_XRRFreeOutputInfo(output_info);
   592                     continue;
   593                 }
   594 
   595                 /* Is this the output that corresponds to the current screen?
   596                    We're checking the crtc position, but that may not be a valid test
   597                    in all cases.  Anybody want to give this some love?
   598                  */
   599                 crtc = X11_XRRGetCrtcInfo(data->display, res, output_info->crtc);
   600                 if (!crtc || crtc->x != displaydata->x || crtc->y != displaydata->y ||
   601                     crtc->width != mode.w || crtc->height != mode.h) {
   602                     X11_XRRFreeOutputInfo(output_info);
   603                     X11_XRRFreeCrtcInfo(crtc);
   604                     continue;
   605                 }
   606 
   607                 displaydata->use_xrandr = use_xrandr;
   608                 displaydata->xrandr_output = res->outputs[output];
   609                 SetXRandRModeInfo(data->display, res, output_info, crtc->mode, &mode);
   610 
   611                 /* Get the name of this display */
   612                 width_mm = output_info->mm_width;
   613                 height_mm = output_info->mm_height;
   614                 inches = (int)((sqrt(width_mm * width_mm +
   615                                      height_mm * height_mm) / 25.4f) + 0.5f);
   616                 SDL_strlcpy(display_name, output_info->name, sizeof(display_name));
   617 
   618                 /* See if we can get the EDID data for the real monitor name */
   619                 props = X11_XRRListOutputProperties(data->display, res->outputs[output], &nprop);
   620                 for (i = 0; i < nprop; ++i) {
   621                     unsigned char *prop;
   622                     int actual_format;
   623                     unsigned long nitems, bytes_after;
   624                     Atom actual_type;
   625 
   626                     if (props[i] == EDID) {
   627                         if (X11_XRRGetOutputProperty(data->display,
   628                                                  res->outputs[output], props[i],
   629                                                  0, 100, False, False,
   630                                                  AnyPropertyType,
   631                                                  &actual_type, &actual_format,
   632                                                  &nitems, &bytes_after, &prop) == Success ) {
   633                             MonitorInfo *info = decode_edid(prop);
   634                             if (info) {
   635     #ifdef X11MODES_DEBUG
   636                                 printf("Found EDID data for %s\n", output_info->name);
   637                                 dump_monitor_info(info);
   638     #endif
   639                                 SDL_strlcpy(display_name, info->dsc_product_name, sizeof(display_name));
   640                                 free(info);
   641                             }
   642                             X11_XFree(prop);
   643                         }
   644                         break;
   645                     }
   646                 }
   647                 if (props) {
   648                     X11_XFree(props);
   649                 }
   650 
   651                 if (*display_name && inches) {
   652                     size_t len = SDL_strlen(display_name);
   653                     SDL_snprintf(&display_name[len], sizeof(display_name)-len, " %d\"", inches);
   654                 }
   655 #ifdef X11MODES_DEBUG
   656                 printf("Display name: %s\n", display_name);
   657 #endif
   658 
   659                 X11_XRRFreeOutputInfo(output_info);
   660                 X11_XRRFreeCrtcInfo(crtc);
   661                 break;
   662             }
   663 #ifdef X11MODES_DEBUG
   664             if (output == res->noutput) {
   665                 printf("Couldn't find XRandR CRTC at %d,%d\n", displaydata->x, displaydata->y);
   666             }
   667 #endif
   668             X11_XRRFreeScreenResources(res);
   669         }
   670 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   671 
   672 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   673         if (!displaydata->use_xrandr &&
   674 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   675             /* XVidMode only works on the screen at the origin */
   676             (!displaydata->use_xinerama ||
   677              (displaydata->x == 0 && displaydata->y == 0)) &&
   678 #endif
   679             use_vidmode) {
   680             displaydata->use_vidmode = use_vidmode;
   681             if (displaydata->use_xinerama) {
   682                 displaydata->vidmode_screen = 0;
   683             } else {
   684                 displaydata->vidmode_screen = screen;
   685             }
   686             XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode);
   687         }
   688 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   689 
   690         SDL_zero(display);
   691         if (*display_name) {
   692             display.name = display_name;
   693         }
   694         display.desktop_mode = mode;
   695         display.current_mode = mode;
   696         display.driverdata = displaydata;
   697         SDL_AddVideoDisplay(&display);
   698     }
   699 
   700 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   701     if (xinerama) X11_XFree(xinerama);
   702 #endif
   703 
   704     if (_this->num_displays == 0) {
   705         return SDL_SetError("No available displays");
   706     }
   707     return 0;
   708 }
   709 
   710 void
   711 X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display)
   712 {
   713     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   714     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   715 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   716     int nmodes;
   717     XF86VidModeModeInfo ** modes;
   718 #endif
   719     int screen_w;
   720     int screen_h;
   721     SDL_DisplayMode mode;
   722 
   723     /* Unfortunately X11 requires the window to be created with the correct
   724      * visual and depth ahead of time, but the SDL API allows you to create
   725      * a window before setting the fullscreen display mode.  This means that
   726      * we have to use the same format for all windows and all display modes.
   727      * (or support recreating the window with a new visual behind the scenes)
   728      */
   729     mode.format = sdl_display->current_mode.format;
   730     mode.driverdata = NULL;
   731 
   732     screen_w = DisplayWidth(display, data->screen);
   733     screen_h = DisplayHeight(display, data->screen);
   734 
   735 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   736     if (data->use_xinerama) {
   737         if (data->use_vidmode && !data->xinerama_info.x_org && !data->xinerama_info.y_org &&
   738            (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) {
   739             SDL_DisplayModeData *modedata;
   740             /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0
   741              * if we're using vidmode.
   742              */
   743             mode.w = screen_w;
   744             mode.h = screen_h;
   745             mode.refresh_rate = 0;
   746             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   747             if (modedata) {
   748                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   749             }
   750             mode.driverdata = modedata;
   751             SDL_AddDisplayMode(sdl_display, &mode);
   752         }
   753         else if (!data->use_xrandr)
   754         {
   755             SDL_DisplayModeData *modedata;
   756             /* Add the current mode of each monitor otherwise if we can't get them from xrandr */
   757             mode.w = data->xinerama_info.width;
   758             mode.h = data->xinerama_info.height;
   759             mode.refresh_rate = 0;
   760             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   761             if (modedata) {
   762                 *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   763             }
   764             mode.driverdata = modedata;
   765             SDL_AddDisplayMode(sdl_display, &mode);
   766         }
   767 
   768     }
   769 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   770 
   771 #if SDL_VIDEO_DRIVER_X11_XRANDR
   772     if (data->use_xrandr) {
   773         XRRScreenResources *res;
   774 
   775         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   776         if (res) {
   777             SDL_DisplayModeData *modedata;
   778             XRROutputInfo *output_info;
   779             int i;
   780 
   781             output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   782             if (output_info && output_info->connection != RR_Disconnected) {
   783                 for (i = 0; i < output_info->nmode; ++i) {
   784                     modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   785                     if (!modedata) {
   786                         continue;
   787                     }
   788                     mode.driverdata = modedata;
   789 
   790                     if (SetXRandRModeInfo(display, res, output_info, output_info->modes[i], &mode)) {
   791                         SDL_AddDisplayMode(sdl_display, &mode);
   792                     } else {
   793                         SDL_free(modedata);
   794                     }
   795                 }
   796             }
   797             X11_XRRFreeOutputInfo(output_info);
   798             X11_XRRFreeScreenResources(res);
   799         }
   800         return;
   801     }
   802 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   803 
   804 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   805     if (data->use_vidmode &&
   806         X11_XF86VidModeGetAllModeLines(display, data->vidmode_screen, &nmodes, &modes)) {
   807         int i;
   808         SDL_DisplayModeData *modedata;
   809 
   810 #ifdef X11MODES_DEBUG
   811         printf("VidMode modes: (unsorted)\n");
   812         for (i = 0; i < nmodes; ++i) {
   813             printf("Mode %d: %d x %d @ %d, flags: 0x%x\n", i,
   814                    modes[i]->hdisplay, modes[i]->vdisplay,
   815                    CalculateXVidModeRefreshRate(modes[i]), modes[i]->flags);
   816         }
   817 #endif
   818         for (i = 0; i < nmodes; ++i) {
   819             modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   820             if (!modedata) {
   821                 continue;
   822             }
   823             mode.driverdata = modedata;
   824 
   825             if (SetXVidModeModeInfo(modes[i], &mode)) {
   826                 SDL_AddDisplayMode(sdl_display, &mode);
   827             } else {
   828                 SDL_free(modedata);
   829             }
   830         }
   831         X11_XFree(modes);
   832         return;
   833     }
   834 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   835 
   836     if (!data->use_xrandr && !data->use_vidmode) {
   837         SDL_DisplayModeData *modedata;
   838         /* Add the desktop mode */
   839         mode = sdl_display->desktop_mode;
   840         modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
   841         if (modedata) {
   842             *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata;
   843         }
   844         mode.driverdata = modedata;
   845         SDL_AddDisplayMode(sdl_display, &mode);
   846     }
   847 }
   848 
   849 int
   850 X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode)
   851 {
   852     SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   853     Display *display = viddata->display;
   854     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   855     SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
   856 
   857     viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2);
   858 
   859 #if SDL_VIDEO_DRIVER_X11_XRANDR
   860     if (data->use_xrandr) {
   861         XRRScreenResources *res;
   862         XRROutputInfo *output_info;
   863         XRRCrtcInfo *crtc;
   864         Status status;
   865 
   866         res = X11_XRRGetScreenResources (display, RootWindow(display, data->screen));
   867         if (!res) {
   868             return SDL_SetError("Couldn't get XRandR screen resources");
   869         }
   870 
   871         output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output);
   872         if (!output_info || output_info->connection == RR_Disconnected) {
   873             X11_XRRFreeScreenResources(res);
   874             return SDL_SetError("Couldn't get XRandR output info");
   875         }
   876 
   877         crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc);
   878         if (!crtc) {
   879             X11_XRRFreeOutputInfo(output_info);
   880             X11_XRRFreeScreenResources(res);
   881             return SDL_SetError("Couldn't get XRandR crtc info");
   882         }
   883 
   884         status = X11_XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime,
   885           crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation,
   886           &data->xrandr_output, 1);
   887 
   888         X11_XRRFreeCrtcInfo(crtc);
   889         X11_XRRFreeOutputInfo(output_info);
   890         X11_XRRFreeScreenResources(res);
   891 
   892         if (status != Success) {
   893             return SDL_SetError("X11_XRRSetCrtcConfig failed");
   894         }
   895     }
   896 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
   897 
   898 #if SDL_VIDEO_DRIVER_X11_XVIDMODE
   899     if (data->use_vidmode) {
   900         X11_XF86VidModeSwitchToMode(display, data->vidmode_screen, &modedata->vm_mode);
   901     }
   902 #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
   903 
   904     return 0;
   905 }
   906 
   907 void
   908 X11_QuitModes(_THIS)
   909 {
   910 }
   911 
   912 int
   913 X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect)
   914 {
   915     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   916 
   917     rect->x = data->x;
   918     rect->y = data->y;
   919     rect->w = sdl_display->current_mode.w;
   920     rect->h = sdl_display->current_mode.h;
   921 
   922 #if SDL_VIDEO_DRIVER_X11_XINERAMA
   923     /* Get the real current bounds of the display */
   924     if (data->use_xinerama) {
   925         Display *display = ((SDL_VideoData *) _this->driverdata)->display;
   926         int screencount;
   927         XineramaScreenInfo *xinerama = X11_XineramaQueryScreens(display, &screencount);
   928         if (xinerama) {
   929             rect->x = xinerama[data->xinerama_screen].x_org;
   930             rect->y = xinerama[data->xinerama_screen].y_org;
   931             X11_XFree(xinerama);
   932         }
   933     }
   934 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
   935     return 0;
   936 }
   937 
   938 int
   939 X11_GetDisplayDPI(_THIS, SDL_VideoDisplay * sdl_display, float * ddpi, float * hdpi, float * vdpi)
   940 {
   941     SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata;
   942 
   943     if (ddpi) {
   944         *ddpi = data->ddpi;
   945     }
   946     if (hdpi) {
   947         *hdpi = data->hdpi;
   948     }
   949     if (vdpi) {
   950         *vpid = data->vdpi;
   951     }
   952 
   953     return data->ddpi != 0.0f ? 0 : -1;
   954 }
   955 
   956 #endif /* SDL_VIDEO_DRIVER_X11 */
   957 
   958 /* vi: set ts=4 sw=4 expandtab: */