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