]> git.sesse.net Git - pistorm/blob - raylib_pi4_test/external/glfw/src/x11_monitor.c
8de865960066716ed0ba7d9c7305215e88004fde
[pistorm] / raylib_pi4_test / external / glfw / src / x11_monitor.c
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 <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 // 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 //
40 static 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 //
47 static 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 //
57 static 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 //
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 //
101 void _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 = 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         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 //
227 void _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         _glfwPlatformGetVideoMode(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 //
281 void _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
313 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
314 {
315 }
316
317 void _glfwPlatformGetMonitorPos(_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
339 void _glfwPlatformGetMonitorContentScale(_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
348 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height)
349 {
350     int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0;
351
352     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
353     {
354         XRRScreenResources* sr =
355             XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
356         XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
357
358         areaX = ci->x;
359         areaY = ci->y;
360
361         const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
362
363         if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
364         {
365             areaWidth  = mi->height;
366             areaHeight = mi->width;
367         }
368         else
369         {
370             areaWidth  = mi->width;
371             areaHeight = mi->height;
372         }
373
374         XRRFreeCrtcInfo(ci);
375         XRRFreeScreenResources(sr);
376     }
377     else
378     {
379         areaWidth  = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
380         areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
381     }
382
383     if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP)
384     {
385         Atom* extents = NULL;
386         Atom* desktop = NULL;
387         const unsigned long extentCount =
388             _glfwGetWindowPropertyX11(_glfw.x11.root,
389                                       _glfw.x11.NET_WORKAREA,
390                                       XA_CARDINAL,
391                                       (unsigned char**) &extents);
392
393         if (_glfwGetWindowPropertyX11(_glfw.x11.root,
394                                       _glfw.x11.NET_CURRENT_DESKTOP,
395                                       XA_CARDINAL,
396                                       (unsigned char**) &desktop) > 0)
397         {
398             if (extentCount >= 4 && *desktop < extentCount / 4)
399             {
400                 const int globalX = extents[*desktop * 4 + 0];
401                 const int globalY = extents[*desktop * 4 + 1];
402                 const int globalWidth  = extents[*desktop * 4 + 2];
403                 const int globalHeight = extents[*desktop * 4 + 3];
404
405                 if (areaX < globalX)
406                 {
407                     areaWidth -= globalX - areaX;
408                     areaX = globalX;
409                 }
410
411                 if (areaY < globalY)
412                 {
413                     areaHeight -= globalY - areaY;
414                     areaY = globalY;
415                 }
416
417                 if (areaX + areaWidth > globalX + globalWidth)
418                     areaWidth = globalX - areaX + globalWidth;
419                 if (areaY + areaHeight > globalY + globalHeight)
420                     areaHeight = globalY - areaY + globalHeight;
421             }
422         }
423
424         if (extents)
425             XFree(extents);
426         if (desktop)
427             XFree(desktop);
428     }
429
430     if (xpos)
431         *xpos = areaX;
432     if (ypos)
433         *ypos = areaY;
434     if (width)
435         *width = areaWidth;
436     if (height)
437         *height = areaHeight;
438 }
439
440 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
441 {
442     GLFWvidmode* result;
443
444     *count = 0;
445
446     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
447     {
448         XRRScreenResources* sr =
449             XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
450         XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
451         XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
452
453         result = calloc(oi->nmode, sizeof(GLFWvidmode));
454
455         for (int i = 0;  i < oi->nmode;  i++)
456         {
457             const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
458             if (!modeIsGood(mi))
459                 continue;
460
461             const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
462             int j;
463
464             for (j = 0;  j < *count;  j++)
465             {
466                 if (_glfwCompareVideoModes(result + j, &mode) == 0)
467                     break;
468             }
469
470             // Skip duplicate modes
471             if (j < *count)
472                 continue;
473
474             (*count)++;
475             result[*count - 1] = mode;
476         }
477
478         XRRFreeOutputInfo(oi);
479         XRRFreeCrtcInfo(ci);
480         XRRFreeScreenResources(sr);
481     }
482     else
483     {
484         *count = 1;
485         result = calloc(1, sizeof(GLFWvidmode));
486         _glfwPlatformGetVideoMode(monitor, result);
487     }
488
489     return result;
490 }
491
492 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
493 {
494     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
495     {
496         XRRScreenResources* sr =
497             XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
498         XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
499
500         if (ci)
501         {
502             const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
503             if (mi)  // mi can be NULL if the monitor has been disconnected
504                 *mode = vidmodeFromModeInfo(mi, ci);
505
506             XRRFreeCrtcInfo(ci);
507         }
508
509         XRRFreeScreenResources(sr);
510     }
511     else
512     {
513         mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
514         mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
515         mode->refreshRate = 0;
516
517         _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
518                       &mode->redBits, &mode->greenBits, &mode->blueBits);
519     }
520 }
521
522 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
523 {
524     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
525     {
526         const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,
527                                                 monitor->x11.crtc);
528         XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,
529                                               monitor->x11.crtc);
530
531         _glfwAllocGammaArrays(ramp, size);
532
533         memcpy(ramp->red,   gamma->red,   size * sizeof(unsigned short));
534         memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));
535         memcpy(ramp->blue,  gamma->blue,  size * sizeof(unsigned short));
536
537         XRRFreeGamma(gamma);
538         return GLFW_TRUE;
539     }
540     else if (_glfw.x11.vidmode.available)
541     {
542         int size;
543         XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
544
545         _glfwAllocGammaArrays(ramp, size);
546
547         XF86VidModeGetGammaRamp(_glfw.x11.display,
548                                 _glfw.x11.screen,
549                                 ramp->size, ramp->red, ramp->green, ramp->blue);
550         return GLFW_TRUE;
551     }
552     else
553     {
554         _glfwInputError(GLFW_PLATFORM_ERROR,
555                         "X11: Gamma ramp access not supported by server");
556         return GLFW_FALSE;
557     }
558 }
559
560 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
561 {
562     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
563     {
564         if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size)
565         {
566             _glfwInputError(GLFW_PLATFORM_ERROR,
567                             "X11: Gamma ramp size must match current ramp size");
568             return;
569         }
570
571         XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
572
573         memcpy(gamma->red,   ramp->red,   ramp->size * sizeof(unsigned short));
574         memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));
575         memcpy(gamma->blue,  ramp->blue,  ramp->size * sizeof(unsigned short));
576
577         XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);
578         XRRFreeGamma(gamma);
579     }
580     else if (_glfw.x11.vidmode.available)
581     {
582         XF86VidModeSetGammaRamp(_glfw.x11.display,
583                                 _glfw.x11.screen,
584                                 ramp->size,
585                                 (unsigned short*) ramp->red,
586                                 (unsigned short*) ramp->green,
587                                 (unsigned short*) ramp->blue);
588     }
589     else
590     {
591         _glfwInputError(GLFW_PLATFORM_ERROR,
592                         "X11: Gamma ramp access not supported by server");
593     }
594 }
595
596
597 //////////////////////////////////////////////////////////////////////////
598 //////                        GLFW native API                       //////
599 //////////////////////////////////////////////////////////////////////////
600
601 GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)
602 {
603     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
604     _GLFW_REQUIRE_INIT_OR_RETURN(None);
605     return monitor->x11.crtc;
606 }
607
608 GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)
609 {
610     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
611     _GLFW_REQUIRE_INIT_OR_RETURN(None);
612     return monitor->x11.output;
613 }
614