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