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