• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current);
237         if (_glfwCompareVideoModes(&current, 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