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