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