1 //========================================================================
2 // GLFW 3.5 X11 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
6 //
7 // This software is provided 'as-is', without any express or implied
8 // warranty. In no event will the authors be held liable for any damages
9 // arising from the use of this software.
10 //
11 // Permission is granted to anyone to use this software for any purpose,
12 // including commercial applications, and to alter it and redistribute it
13 // freely, subject to the following restrictions:
14 //
15 // 1. The origin of this software must not be misrepresented; you must not
16 // claim that you wrote the original software. If you use this software
17 // in a product, an acknowledgment in the product documentation would
18 // be appreciated but is not required.
19 //
20 // 2. Altered source versions must be plainly marked as such, and must not
21 // be misrepresented as being the original software.
22 //
23 // 3. This notice may not be removed or altered from any source
24 // distribution.
25 //
26 //========================================================================
27
28 #include "internal.h"
29
30 #if defined(_GLFW_X11)
31
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <math.h>
36 #include <assert.h>
37
38
39 // Check whether the display mode should be included in enumeration
40 //
modeIsGood(const XRRModeInfo * mi)41 static GLFWbool modeIsGood(const XRRModeInfo* mi)
42 {
43 return (mi->modeFlags & RR_Interlace) == 0;
44 }
45
46 // Calculates the refresh rate, in Hz, from the specified RandR mode info
47 //
calculateRefreshRate(const XRRModeInfo * mi)48 static int calculateRefreshRate(const XRRModeInfo* mi)
49 {
50 if (mi->hTotal && mi->vTotal)
51 return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal));
52 else
53 return 0;
54 }
55
56 // Returns the mode info for a RandR mode XID
57 //
getModeInfo(const XRRScreenResources * sr,RRMode id)58 static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id)
59 {
60 for (int i = 0; i < sr->nmode; i++)
61 {
62 if (sr->modes[i].id == id)
63 return sr->modes + i;
64 }
65
66 return NULL;
67 }
68
69 // Convert RandR mode info to GLFW video mode
70 //
vidmodeFromModeInfo(const XRRModeInfo * mi,const XRRCrtcInfo * ci)71 static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi,
72 const XRRCrtcInfo* ci)
73 {
74 GLFWvidmode mode;
75
76 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
77 {
78 mode.width = mi->height;
79 mode.height = mi->width;
80 }
81 else
82 {
83 mode.width = mi->width;
84 mode.height = mi->height;
85 }
86
87 mode.refreshRate = calculateRefreshRate(mi);
88
89 _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
90 &mode.redBits, &mode.greenBits, &mode.blueBits);
91
92 return mode;
93 }
94
95
96 //////////////////////////////////////////////////////////////////////////
97 ////// GLFW internal API //////
98 //////////////////////////////////////////////////////////////////////////
99
100 // Poll for changes in the set of connected monitors
101 //
_glfwPollMonitorsX11(void)102 void _glfwPollMonitorsX11(void)
103 {
104 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
105 {
106 int disconnectedCount, screenCount = 0;
107 _GLFWmonitor** disconnected = NULL;
108 XineramaScreenInfo* screens = NULL;
109 XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display,
110 _glfw.x11.root);
111 RROutput primary = XRRGetOutputPrimary(_glfw.x11.display,
112 _glfw.x11.root);
113
114 if (_glfw.x11.xinerama.available)
115 screens = XineramaQueryScreens(_glfw.x11.display, &screenCount);
116
117 disconnectedCount = _glfw.monitorCount;
118 if (disconnectedCount)
119 {
120 disconnected = _glfw_calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
121 memcpy(disconnected,
122 _glfw.monitors,
123 _glfw.monitorCount * sizeof(_GLFWmonitor*));
124 }
125
126 for (int i = 0; i < sr->noutput; i++)
127 {
128 int j, type, widthMM, heightMM;
129
130 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]);
131 if (oi->connection != RR_Connected || oi->crtc == None)
132 {
133 XRRFreeOutputInfo(oi);
134 continue;
135 }
136
137 for (j = 0; j < disconnectedCount; j++)
138 {
139 if (disconnected[j] &&
140 disconnected[j]->x11.output == sr->outputs[i])
141 {
142 disconnected[j] = NULL;
143 break;
144 }
145 }
146
147 if (j < disconnectedCount)
148 {
149 XRRFreeOutputInfo(oi);
150 continue;
151 }
152
153 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc);
154 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
155 {
156 widthMM = oi->mm_height;
157 heightMM = oi->mm_width;
158 }
159 else
160 {
161 widthMM = oi->mm_width;
162 heightMM = oi->mm_height;
163 }
164
165 if (widthMM <= 0 || heightMM <= 0)
166 {
167 // HACK: If RandR does not provide a physical size, assume the
168 // X11 default 96 DPI and calculate from the CRTC viewport
169 // NOTE: These members are affected by rotation, unlike the mode
170 // info and output info members
171 widthMM = (int) (ci->width * 25.4f / 96.f);
172 heightMM = (int) (ci->height * 25.4f / 96.f);
173 }
174
175 _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);
176 monitor->x11.output = sr->outputs[i];
177 monitor->x11.crtc = oi->crtc;
178
179 for (j = 0; j < screenCount; j++)
180 {
181 if (screens[j].x_org == ci->x &&
182 screens[j].y_org == ci->y &&
183 screens[j].width == ci->width &&
184 screens[j].height == ci->height)
185 {
186 monitor->x11.index = j;
187 break;
188 }
189 }
190
191 if (monitor->x11.output == primary)
192 type = _GLFW_INSERT_FIRST;
193 else
194 type = _GLFW_INSERT_LAST;
195
196 _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
197
198 XRRFreeOutputInfo(oi);
199 XRRFreeCrtcInfo(ci);
200 }
201
202 XRRFreeScreenResources(sr);
203
204 if (screens)
205 XFree(screens);
206
207 for (int i = 0; i < disconnectedCount; i++)
208 {
209 if (disconnected[i])
210 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
211 }
212
213 _glfw_free(disconnected);
214 }
215 else
216 {
217 const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
218 const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
219
220 _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM),
221 GLFW_CONNECTED,
222 _GLFW_INSERT_FIRST);
223 }
224 }
225
226 // Set the current video mode for the specified monitor
227 //
_glfwSetVideoModeX11(_GLFWmonitor * monitor,const GLFWvidmode * desired)228 void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired)
229 {
230 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
231 {
232 GLFWvidmode current;
233 RRMode native = None;
234
235 const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
236 _glfwGetVideoModeX11(monitor, ¤t);
237 if (_glfwCompareVideoModes(¤t, best) == 0)
238 return;
239
240 XRRScreenResources* sr =
241 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
242 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
243 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
244
245 for (int i = 0; i < oi->nmode; i++)
246 {
247 const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
248 if (!modeIsGood(mi))
249 continue;
250
251 const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
252 if (_glfwCompareVideoModes(best, &mode) == 0)
253 {
254 native = mi->id;
255 break;
256 }
257 }
258
259 if (native)
260 {
261 if (monitor->x11.oldMode == None)
262 monitor->x11.oldMode = ci->mode;
263
264 XRRSetCrtcConfig(_glfw.x11.display,
265 sr, monitor->x11.crtc,
266 CurrentTime,
267 ci->x, ci->y,
268 native,
269 ci->rotation,
270 ci->outputs,
271 ci->noutput);
272 }
273
274 XRRFreeOutputInfo(oi);
275 XRRFreeCrtcInfo(ci);
276 XRRFreeScreenResources(sr);
277 }
278 }
279
280 // Restore the saved (original) video mode for the specified monitor
281 //
_glfwRestoreVideoModeX11(_GLFWmonitor * monitor)282 void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor)
283 {
284 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
285 {
286 if (monitor->x11.oldMode == None)
287 return;
288
289 XRRScreenResources* sr =
290 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
291 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
292
293 XRRSetCrtcConfig(_glfw.x11.display,
294 sr, monitor->x11.crtc,
295 CurrentTime,
296 ci->x, ci->y,
297 monitor->x11.oldMode,
298 ci->rotation,
299 ci->outputs,
300 ci->noutput);
301
302 XRRFreeCrtcInfo(ci);
303 XRRFreeScreenResources(sr);
304
305 monitor->x11.oldMode = None;
306 }
307 }
308
309
310 //////////////////////////////////////////////////////////////////////////
311 ////// GLFW platform API //////
312 //////////////////////////////////////////////////////////////////////////
313
_glfwFreeMonitorX11(_GLFWmonitor * monitor)314 void _glfwFreeMonitorX11(_GLFWmonitor* monitor)
315 {
316 }
317
_glfwGetMonitorPosX11(_GLFWmonitor * monitor,int * xpos,int * ypos)318 void _glfwGetMonitorPosX11(_GLFWmonitor* monitor, int* xpos, int* ypos)
319 {
320 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
321 {
322 XRRScreenResources* sr =
323 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
324 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
325
326 if (ci)
327 {
328 if (xpos)
329 *xpos = ci->x;
330 if (ypos)
331 *ypos = ci->y;
332
333 XRRFreeCrtcInfo(ci);
334 }
335
336 XRRFreeScreenResources(sr);
337 }
338 }
339
_glfwGetMonitorContentScaleX11(_GLFWmonitor * monitor,float * xscale,float * yscale)340 void _glfwGetMonitorContentScaleX11(_GLFWmonitor* monitor,
341 float* xscale, float* yscale)
342 {
343 if (xscale)
344 *xscale = _glfw.x11.contentScaleX;
345 if (yscale)
346 *yscale = _glfw.x11.contentScaleY;
347 }
348
_glfwGetMonitorWorkareaX11(_GLFWmonitor * monitor,int * xpos,int * ypos,int * width,int * height)349 void _glfwGetMonitorWorkareaX11(_GLFWmonitor* monitor,
350 int* xpos, int* ypos,
351 int* width, int* height)
352 {
353 int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0;
354
355 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
356 {
357 XRRScreenResources* sr =
358 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
359 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
360
361 areaX = ci->x;
362 areaY = ci->y;
363
364 const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
365
366 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
367 {
368 areaWidth = mi->height;
369 areaHeight = mi->width;
370 }
371 else
372 {
373 areaWidth = mi->width;
374 areaHeight = mi->height;
375 }
376
377 XRRFreeCrtcInfo(ci);
378 XRRFreeScreenResources(sr);
379 }
380 else
381 {
382 areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
383 areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
384 }
385
386 if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP)
387 {
388 Atom* extents = NULL;
389 Atom* desktop = NULL;
390 const unsigned long extentCount =
391 _glfwGetWindowPropertyX11(_glfw.x11.root,
392 _glfw.x11.NET_WORKAREA,
393 XA_CARDINAL,
394 (unsigned char**) &extents);
395
396 if (_glfwGetWindowPropertyX11(_glfw.x11.root,
397 _glfw.x11.NET_CURRENT_DESKTOP,
398 XA_CARDINAL,
399 (unsigned char**) &desktop) > 0)
400 {
401 if (extentCount >= 4 && *desktop < extentCount / 4)
402 {
403 const int globalX = extents[*desktop * 4 + 0];
404 const int globalY = extents[*desktop * 4 + 1];
405 const int globalWidth = extents[*desktop * 4 + 2];
406 const int globalHeight = extents[*desktop * 4 + 3];
407
408 if (areaX < globalX)
409 {
410 areaWidth -= globalX - areaX;
411 areaX = globalX;
412 }
413
414 if (areaY < globalY)
415 {
416 areaHeight -= globalY - areaY;
417 areaY = globalY;
418 }
419
420 if (areaX + areaWidth > globalX + globalWidth)
421 areaWidth = globalX - areaX + globalWidth;
422 if (areaY + areaHeight > globalY + globalHeight)
423 areaHeight = globalY - areaY + globalHeight;
424 }
425 }
426
427 if (extents)
428 XFree(extents);
429 if (desktop)
430 XFree(desktop);
431 }
432
433 if (xpos)
434 *xpos = areaX;
435 if (ypos)
436 *ypos = areaY;
437 if (width)
438 *width = areaWidth;
439 if (height)
440 *height = areaHeight;
441 }
442
_glfwGetVideoModesX11(_GLFWmonitor * monitor,int * count)443 GLFWvidmode* _glfwGetVideoModesX11(_GLFWmonitor* monitor, int* count)
444 {
445 GLFWvidmode* result;
446
447 *count = 0;
448
449 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
450 {
451 XRRScreenResources* sr =
452 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
453 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
454 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
455
456 result = _glfw_calloc(oi->nmode, sizeof(GLFWvidmode));
457
458 for (int i = 0; i < oi->nmode; i++)
459 {
460 const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
461 if (!modeIsGood(mi))
462 continue;
463
464 const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
465 int j;
466
467 for (j = 0; j < *count; j++)
468 {
469 if (_glfwCompareVideoModes(result + j, &mode) == 0)
470 break;
471 }
472
473 // Skip duplicate modes
474 if (j < *count)
475 continue;
476
477 (*count)++;
478 result[*count - 1] = mode;
479 }
480
481 XRRFreeOutputInfo(oi);
482 XRRFreeCrtcInfo(ci);
483 XRRFreeScreenResources(sr);
484 }
485 else
486 {
487 *count = 1;
488 result = _glfw_calloc(1, sizeof(GLFWvidmode));
489 _glfwGetVideoModeX11(monitor, result);
490 }
491
492 return result;
493 }
494
_glfwGetVideoModeX11(_GLFWmonitor * monitor,GLFWvidmode * mode)495 GLFWbool _glfwGetVideoModeX11(_GLFWmonitor* monitor, GLFWvidmode* mode)
496 {
497 if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
498 {
499 XRRScreenResources* sr =
500 XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
501 const XRRModeInfo* mi = NULL;
502
503 XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
504 if (ci)
505 {
506 mi = getModeInfo(sr, ci->mode);
507 if (mi)
508 *mode = vidmodeFromModeInfo(mi, ci);
509
510 XRRFreeCrtcInfo(ci);
511 }
512
513 XRRFreeScreenResources(sr);
514
515 if (!mi)
516 {
517 _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to query video mode");
518 return GLFW_FALSE;
519 }
520 }
521 else
522 {
523 mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
524 mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
525 mode->refreshRate = 0;
526
527 _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
528 &mode->redBits, &mode->greenBits, &mode->blueBits);
529 }
530
531 return GLFW_TRUE;
532 }
533
_glfwGetGammaRampX11(_GLFWmonitor * monitor,GLFWgammaramp * ramp)534 GLFWbool _glfwGetGammaRampX11(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
535 {
536 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
537 {
538 const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,
539 monitor->x11.crtc);
540 XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,
541 monitor->x11.crtc);
542
543 _glfwAllocGammaArrays(ramp, size);
544
545 memcpy(ramp->red, gamma->red, size * sizeof(unsigned short));
546 memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));
547 memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short));
548
549 XRRFreeGamma(gamma);
550 return GLFW_TRUE;
551 }
552 else if (_glfw.x11.vidmode.available)
553 {
554 int size;
555 XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
556
557 _glfwAllocGammaArrays(ramp, size);
558
559 XF86VidModeGetGammaRamp(_glfw.x11.display,
560 _glfw.x11.screen,
561 ramp->size, ramp->red, ramp->green, ramp->blue);
562 return GLFW_TRUE;
563 }
564 else
565 {
566 _glfwInputError(GLFW_PLATFORM_ERROR,
567 "X11: Gamma ramp access not supported by server");
568 return GLFW_FALSE;
569 }
570 }
571
_glfwSetGammaRampX11(_GLFWmonitor * monitor,const GLFWgammaramp * ramp)572 void _glfwSetGammaRampX11(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
573 {
574 if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
575 {
576 if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size)
577 {
578 _glfwInputError(GLFW_PLATFORM_ERROR,
579 "X11: Gamma ramp size must match current ramp size");
580 return;
581 }
582
583 XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
584
585 memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short));
586 memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));
587 memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short));
588
589 XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);
590 XRRFreeGamma(gamma);
591 }
592 else if (_glfw.x11.vidmode.available)
593 {
594 XF86VidModeSetGammaRamp(_glfw.x11.display,
595 _glfw.x11.screen,
596 ramp->size,
597 (unsigned short*) ramp->red,
598 (unsigned short*) ramp->green,
599 (unsigned short*) ramp->blue);
600 }
601 else
602 {
603 _glfwInputError(GLFW_PLATFORM_ERROR,
604 "X11: Gamma ramp access not supported by server");
605 }
606 }
607
608
609 //////////////////////////////////////////////////////////////////////////
610 ////// GLFW native API //////
611 //////////////////////////////////////////////////////////////////////////
612
glfwGetX11Adapter(GLFWmonitor * handle)613 GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)
614 {
615 _GLFW_REQUIRE_INIT_OR_RETURN(None);
616
617 if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
618 {
619 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
620 return None;
621 }
622
623 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
624 assert(monitor != NULL);
625
626 return monitor->x11.crtc;
627 }
628
glfwGetX11Monitor(GLFWmonitor * handle)629 GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)
630 {
631 _GLFW_REQUIRE_INIT_OR_RETURN(None);
632
633 if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
634 {
635 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
636 return None;
637 }
638
639 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
640 assert(monitor != NULL);
641
642 return monitor->x11.output;
643 }
644
645 #endif // _GLFW_X11
646
647