1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
45static int
46get_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
77int
78X11_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
93Uint32
94X11_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
152static SDL_bool
153CheckXinerama(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!). */
194static SDL_bool xinerama_triggered_error = SDL_FALSE;
195static int
196X11_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
208static SDL_bool
209CheckXRandR(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
256static int
257CalculateXRandRRefreshRate(const XRRModeInfo *info)
258{
259 return (info->hTotal && info->vTotal) ?
260 SDL_round(((double)info->dotClock / (double)(info->hTotal * info->vTotal))) : 0;
261}
262
263static SDL_bool
264SetXRandRModeInfo(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
298static void
299SetXRandRDisplayName(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
349static int
350X11_InitModes_XRandR(_THIS)
351{
352 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
353 Display *dpy = data->display;
354 const int screencount = ScreenCount(dpy);
355 const int default_screen = DefaultScreen(dpy);
356 RROutput primary = X11_XRRGetOutputPrimary(dpy, RootWindow(dpy, default_screen));
357 Atom EDID = X11_XInternAtom(dpy, "EDID", False);
358 XRRScreenResources *res = NULL;
359 Uint32 pixelformat;
360 XVisualInfo vinfo;
361 XPixmapFormatValues *pixmapformats;
362 int looking_for_primary;
363 int scanline_pad;
364 int output;
365 int screen, i, n;
366
367 for (looking_for_primary = 1; looking_for_primary >= 0; looking_for_primary--) {
368 for (screen = 0; screen < screencount; screen++) {
369
370 /* we want the primary output first, and then skipped later. */
371 if (looking_for_primary && (screen != default_screen)) {
372 continue;
373 }
374
375 if (get_visualinfo(dpy, screen, &vinfo) < 0) {
376 continue; /* uh, skip this screen? */
377 }
378
379 pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo);
380 if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) {
381 continue; /* Palettized video modes are no longer supported */
382 }
383
384 scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8;
385 pixmapformats = X11_XListPixmapFormats(dpy, &n);
386 if (pixmapformats) {
387 for (i = 0; i < n; ++i) {
388 if (pixmapformats[i].depth == vinfo.depth) {
389 scanline_pad = pixmapformats[i].scanline_pad;
390 break;
391 }
392 }
393 X11_XFree(pixmapformats);
394 }
395
396 res = X11_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen));
397 if (!res || res->noutput == 0) {
398 if (res) {
399 X11_XRRFreeScreenResources(res);
400 }
401
402 res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen));
403 if (!res) {
404 continue;
405 }
406 }
407
408 for (output = 0; output < res->noutput; output++) {
409 XRROutputInfo *output_info;
410 int display_x, display_y;
411 unsigned long display_mm_width, display_mm_height;
412 SDL_DisplayData *displaydata;
413 char display_name[128];
414 SDL_DisplayMode mode;
415 SDL_DisplayModeData *modedata;
416 SDL_VideoDisplay display;
417 RRMode modeID;
418 RRCrtc output_crtc;
419 XRRCrtcInfo *crtc;
420
421 /* The primary output _should_ always be sorted first, but just in case... */
422 if ((looking_for_primary && (res->outputs[output] != primary)) ||
423 (!looking_for_primary && (screen == default_screen) && (res->outputs[output] == primary))) {
424 continue;
425 }
426
427 output_info = X11_XRRGetOutputInfo(dpy, res, res->outputs[output]);
428 if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) {
429 X11_XRRFreeOutputInfo(output_info);
430 continue;
431 }
432
433 SDL_strlcpy(display_name, output_info->name, sizeof(display_name));
434 display_mm_width = output_info->mm_width;
435 display_mm_height = output_info->mm_height;
436 output_crtc = output_info->crtc;
437 X11_XRRFreeOutputInfo(output_info);
438
439 crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc);
440 if (!crtc) {
441 continue;
442 }
443
444 SDL_zero(mode);
445 modeID = crtc->mode;
446 mode.w = crtc->width;
447 mode.h = crtc->height;
448 mode.format = pixelformat;
449
450 display_x = crtc->x;
451 display_y = crtc->y;
452
453 X11_XRRFreeCrtcInfo(crtc);
454
455 displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
456 if (!displaydata) {
457 return SDL_OutOfMemory();
458 }
459
460 modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
461 if (!modedata) {
462 SDL_free(displaydata);
463 return SDL_OutOfMemory();
464 }
465 modedata->xrandr_mode = modeID;
466 mode.driverdata = modedata;
467
468 displaydata->screen = screen;
469 displaydata->visual = vinfo.visual;
470 displaydata->depth = vinfo.depth;
471 displaydata->hdpi = display_mm_width ? (((float) mode.w) * 25.4f / display_mm_width) : 0.0f;
472 displaydata->vdpi = display_mm_height ? (((float) mode.h) * 25.4f / display_mm_height) : 0.0f;
473 displaydata->ddpi = SDL_ComputeDiagonalDPI(mode.w, mode.h, ((float) display_mm_width) / 25.4f,((float) display_mm_height) / 25.4f);
474 displaydata->scanline_pad = scanline_pad;
475 displaydata->x = display_x;
476 displaydata->y = display_y;
477 displaydata->use_xrandr = 1;
478 displaydata->xrandr_output = res->outputs[output];
479
480 SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode);
481 SetXRandRDisplayName(dpy, EDID, display_name, sizeof (display_name), res->outputs[output], display_mm_width, display_mm_height);
482
483 SDL_zero(display);
484 if (*display_name) {
485 display.name = display_name;
486 }
487 display.desktop_mode = mode;
488 display.current_mode = mode;
489 display.driverdata = displaydata;
490 SDL_AddVideoDisplay(&display, SDL_FALSE);
491 }
492
493 X11_XRRFreeScreenResources(res);
494 }
495 }
496
497 if (_this->num_displays == 0) {
498 return SDL_SetError("No available displays");
499 }
500
501 return 0;
502}
503#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
504
505#if SDL_VIDEO_DRIVER_X11_XVIDMODE
506static SDL_bool
507CheckVidMode(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
542static
543Bool 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
568static int
569CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info)
570{
571 return (info->htotal
572 && info->vtotal) ? (1000 * info->dotclock / (info->htotal *
573 info->vtotal)) : 0;
574}
575
576static SDL_bool
577SetXVidModeModeInfo(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
587int
588X11_InitModes(_THIS)
589{
590 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
591 int snum, screen, screencount = 0;
592#if SDL_VIDEO_DRIVER_X11_XINERAMA
593 int xinerama_major, xinerama_minor;
594 int use_xinerama = 0;
595 XineramaScreenInfo *xinerama = NULL;
596#endif
597#if SDL_VIDEO_DRIVER_X11_XRANDR
598 int xrandr_major, xrandr_minor;
599#endif
600#if SDL_VIDEO_DRIVER_X11_XVIDMODE
601 int vm_major, vm_minor;
602 int use_vidmode = 0;
603#endif
604
605/* XRandR is the One True Modern Way to do this on X11. If it's enabled and
606 available, don't even look at other ways of doing things. */
607#if SDL_VIDEO_DRIVER_X11_XRANDR
608 /* require at least XRandR v1.3 */
609 if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) &&
610 (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 3))) {
611 if (X11_InitModes_XRandR(_this) == 0)
612 return 0;
613 }
614#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
615
616/* !!! FIXME: eventually remove support for Xinerama and XVidMode (everything below here). */
617
618 /* This is a workaround for some apps (UnrealEngine4, for example) until
619 we sort out the ramifications of removing XVidMode support outright.
620 This block should be removed with the XVidMode support. */
621 {
622 if (SDL_GetHintBoolean("SDL_VIDEO_X11_REQUIRE_XRANDR", SDL_FALSE)) {
623 #if SDL_VIDEO_DRIVER_X11_XRANDR
624 return SDL_SetError("XRandR support is required but not available");
625 #else
626 return SDL_SetError("XRandR support is required but not built into SDL!");
627 #endif
628 }
629 }
630
631#if SDL_VIDEO_DRIVER_X11_XINERAMA
632 /* Query Xinerama extention
633 * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012)
634 * or newer of the Nvidia binary drivers
635 */
636 if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) {
637 int (*handler) (Display *, XErrorEvent *);
638 X11_XSync(data->display, False);
639 handler = X11_XSetErrorHandler(X11_XineramaFailed);
640 xinerama = X11_XineramaQueryScreens(data->display, &screencount);
641 X11_XSync(data->display, False);
642 X11_XSetErrorHandler(handler);
643 if (xinerama_triggered_error) {
644 xinerama = 0;
645 }
646 if (xinerama) {
647 use_xinerama = xinerama_major * 100 + xinerama_minor;
648 }
649 }
650 if (!xinerama) {
651 screencount = ScreenCount(data->display);
652 }
653#else
654 screencount = ScreenCount(data->display);
655#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
656
657#if SDL_VIDEO_DRIVER_X11_XVIDMODE
658 if (CheckVidMode(data->display, &vm_major, &vm_minor)) {
659 use_vidmode = vm_major * 100 + vm_minor;
660 }
661#endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
662
663 for (snum = 0; snum < screencount; ++snum) {
664 XVisualInfo vinfo;
665 SDL_VideoDisplay display;
666 SDL_DisplayData *displaydata;
667 SDL_DisplayMode mode;
668 SDL_DisplayModeData *modedata;
669 XPixmapFormatValues *pixmapFormats;
670 char display_name[128];
671 int i, n;
672
673 /* Re-order screens to always put default screen first */
674 if (snum == 0) {
675 screen = DefaultScreen(data->display);
676 } else if (snum == DefaultScreen(data->display)) {
677 screen = 0;
678 } else {
679 screen = snum;
680 }
681
682#if SDL_VIDEO_DRIVER_X11_XINERAMA
683 if (xinerama) {
684 if (get_visualinfo(data->display, 0, &vinfo) < 0) {
685 continue;
686 }
687 } else {
688 if (get_visualinfo(data->display, screen, &vinfo) < 0) {
689 continue;
690 }
691 }
692#else
693 if (get_visualinfo(data->display, screen, &vinfo) < 0) {
694 continue;
695 }
696#endif
697
698 displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata));
699 if (!displaydata) {
700 continue;
701 }
702 display_name[0] = '\0';
703
704 mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo);
705 if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) {
706 /* We don't support palettized modes now */
707 SDL_free(displaydata);
708 continue;
709 }
710#if SDL_VIDEO_DRIVER_X11_XINERAMA
711 if (xinerama) {
712 mode.w = xinerama[screen].width;
713 mode.h = xinerama[screen].height;
714 } else {
715 mode.w = DisplayWidth(data->display, screen);
716 mode.h = DisplayHeight(data->display, screen);
717 }
718#else
719 mode.w = DisplayWidth(data->display, screen);
720 mode.h = DisplayHeight(data->display, screen);
721#endif
722 mode.refresh_rate = 0;
723
724 modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData));
725 if (!modedata) {
726 SDL_free(displaydata);
727 continue;
728 }
729 mode.driverdata = modedata;
730
731#if SDL_VIDEO_DRIVER_X11_XINERAMA
732 /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active,
733 * there's only one screen available. So we force the screen number to zero and
734 * let Xinerama specific code handle specific functionality using displaydata->xinerama_info
735 */
736 if (use_xinerama) {
737 displaydata->screen = 0;
738 displaydata->use_xinerama = use_xinerama;
739 displaydata->xinerama_info = xinerama[screen];
740 displaydata->xinerama_screen = screen;
741 }
742 else displaydata->screen = screen;
743#else
744 displaydata->screen = screen;
745#endif
746 displaydata->visual = vinfo.visual;
747 displaydata->depth = vinfo.depth;
748
749 /* We use the displaydata screen index here so that this works
750 for both the Xinerama case, where we get the overall DPI,
751 and the regular X11 screen info case. */
752 displaydata->hdpi = (float)DisplayWidth(data->display, displaydata->screen) * 25.4f /
753 DisplayWidthMM(data->display, displaydata->screen);
754 displaydata->vdpi = (float)DisplayHeight(data->display, displaydata->screen) * 25.4f /
755 DisplayHeightMM(data->display, displaydata->screen);
756 displaydata->ddpi = SDL_ComputeDiagonalDPI(DisplayWidth(data->display, displaydata->screen),
757 DisplayHeight(data->display, displaydata->screen),
758 (float)DisplayWidthMM(data->display, displaydata->screen) / 25.4f,
759 (float)DisplayHeightMM(data->display, displaydata->screen) / 25.4f);
760
761 displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8;
762 pixmapFormats = X11_XListPixmapFormats(data->display, &n);
763 if (pixmapFormats) {
764 for (i = 0; i < n; ++i) {
765 if (pixmapFormats[i].depth == displaydata->depth) {
766 displaydata->scanline_pad = pixmapFormats[i].scanline_pad;
767 break;
768 }
769 }
770 X11_XFree(pixmapFormats);
771 }
772
773#if SDL_VIDEO_DRIVER_X11_XINERAMA
774 if (use_xinerama) {
775 displaydata->x = xinerama[screen].x_org;
776 displaydata->y = xinerama[screen].y_org;
777 }
778 else
779#endif
780 {
781 displaydata->x = 0;
782 displaydata->y = 0;
783 }
784
785#if SDL_VIDEO_DRIVER_X11_XVIDMODE
786 if (!displaydata->use_xrandr &&
787#if SDL_VIDEO_DRIVER_X11_XINERAMA
788 /* XVidMode only works on the screen at the origin */
789 (!displaydata->use_xinerama ||
790 (displaydata->x == 0 && displaydata->y == 0)) &&
791#endif
792 use_vidmode) {
793 displaydata->use_vidmode = use_vidmode;
794 if (displaydata->use_xinerama) {
795 displaydata->vidmode_screen = 0;
796 } else {
797 displaydata->vidmode_screen = screen;
798 }
799 XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode);
800 }
801#endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */
802
803 SDL_zero(display);
804 if (*display_name) {
805 display.name = display_name;
806 }
807 display.desktop_mode = mode;
808 display.current_mode = mode;
809 display.driverdata = displaydata;
810 SDL_AddVideoDisplay(&display, SDL_FALSE);
811 }
812
813#if SDL_VIDEO_DRIVER_X11_XINERAMA
814 if (xinerama) X11_XFree(xinerama);
815#endif
816
817 if (_this->num_displays == 0) {
818 return SDL_SetError("No available displays");
819 }
820 return 0;
821}
822
823void
824X11_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
966int
967X11_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
1024void
1025X11_QuitModes(_THIS)
1026{
1027}
1028
1029int
1030X11_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
1055int
1056X11_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
1073int
1074X11_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