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, &current);
234         if (_glfwCompareVideoModes(&current, 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