1 /// Translated from C to D
2 module x11_monitor;
3 
4 extern(C): @nogc: nothrow: __gshared:
5 //========================================================================
6 // GLFW 3.3 X11 - www.glfw.org
7 //------------------------------------------------------------------------
8 // Copyright (c) 2002-2006 Marcus Geelnard
9 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
10 //
11 // This software is provided 'as-is', without any express or implied
12 // warranty. In no event will the authors be held liable for any damages
13 // arising from the use of this software.
14 //
15 // Permission is granted to anyone to use this software for any purpose,
16 // including commercial applications, and to alter it and redistribute it
17 // freely, subject to the following restrictions:
18 //
19 // 1. The origin of this software must not be misrepresented; you must not
20 //    claim that you wrote the original software. If you use this software
21 //    in a product, an acknowledgment in the product documentation would
22 //    be appreciated but is not required.
23 //
24 // 2. Altered source versions must be plainly marked as such, and must not
25 //    be misrepresented as being the original software.
26 //
27 // 3. This notice may not be removed or altered from any source
28 //    distribution.
29 //
30 //========================================================================
31 // It is fine to use C99 in this file because it will not be built with VS
32 //========================================================================
33 
34 import glfw3.internal;
35 
36 import core.stdc.limits;
37 import core.stdc.stdlib;
38 import core.stdc.string;
39 import core.stdc.math;
40 import core.stdc.config: c_long, c_ulong;
41 
42 // Check whether the display mode should be included in enumeration
43 //
44 static GLFWbool modeIsGood(const(XRRModeInfo)* mi) {
45     return (mi.modeFlags & RR_Interlace) == 0;
46 }
47 
48 // Calculates the refresh rate, in Hz, from the specified RandR mode info
49 //
50 static int calculateRefreshRate(const(XRRModeInfo)* mi) {
51     if (mi.hTotal && mi.vTotal)
52         return cast(int) round(cast(double) mi.dotClock / (cast(double) mi.hTotal * cast(double) mi.vTotal));
53     else
54         return 0;
55 }
56 
57 // Returns the mode info for a RandR mode XID
58 //
59 static const(XRRModeInfo)* getModeInfo(const(XRRScreenResources)* sr, RRMode id) {
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 //
71 static GLFWvidmode vidmodeFromModeInfo(const(XRRModeInfo)* mi, const(XRRCrtcInfo)* ci) {
72     GLFWvidmode mode;
73 
74     if (ci.rotation == RR_Rotate_90 || ci.rotation == RR_Rotate_270)
75     {
76         mode.width  = mi.height;
77         mode.height = mi.width;
78     }
79     else
80     {
81         mode.width  = mi.width;
82         mode.height = mi.height;
83     }
84 
85     mode.refreshRate = calculateRefreshRate(mi);
86 
87     _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
88                   &mode.redBits, &mode.greenBits, &mode.blueBits);
89 
90     return mode;
91 }
92 
93 
94 //////////////////////////////////////////////////////////////////////////
95 //////                       GLFW internal API                      //////
96 //////////////////////////////////////////////////////////////////////////
97 
98 // Poll for changes in the set of connected monitors
99 //
100 void _glfwPollMonitorsX11() {
101     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
102     {
103         int disconnectedCount;int screenCount = 0;
104         _GLFWmonitor** disconnected = null;
105         XineramaScreenInfo* screens = null;
106         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display,
107                                                               _glfw.x11.root);
108         RROutput primary = _glfw.x11.randr.GetOutputPrimary(_glfw.x11.display,
109                                                _glfw.x11.root);
110 
111         if (_glfw.x11.xinerama.available)
112             screens = _glfw.x11.xinerama.QueryScreens(_glfw.x11.display, &screenCount);
113 
114         disconnectedCount = _glfw.monitorCount;
115         if (disconnectedCount)
116         {
117             disconnected = cast(_GLFWmonitor**) calloc(_glfw.monitorCount, (_GLFWmonitor*).sizeof);
118             memcpy(disconnected,
119                    _glfw.monitors,
120                    _glfw.monitorCount * (_GLFWmonitor*).sizeof);
121         }
122 
123         for (int i = 0;  i < sr.noutput;  i++)
124         {
125             int j;int type;int widthMM;int heightMM;
126 
127             XRROutputInfo* oi = _glfw.x11.randr.GetOutputInfo(_glfw.x11.display, sr, sr.outputs[i]);
128             if (oi.connection != RR_Connected || oi.crtc == None)
129             {
130                 _glfw.x11.randr.FreeOutputInfo(oi);
131                 continue;
132             }
133 
134             for (j = 0;  j < disconnectedCount;  j++)
135             {
136                 if (disconnected[j] &&
137                     disconnected[j].x11.output == sr.outputs[i])
138                 {
139                     disconnected[j] = null;
140                     break;
141                 }
142             }
143 
144             if (j < disconnectedCount)
145             {
146                 _glfw.x11.randr.FreeOutputInfo(oi);
147                 continue;
148             }
149 
150             XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, oi.crtc);
151             if (ci.rotation == RR_Rotate_90 || ci.rotation == RR_Rotate_270)
152             {
153                 widthMM  = cast(int) oi.mm_height;
154                 heightMM = cast(int) oi.mm_width;
155             }
156             else
157             {
158                 widthMM  = cast(int) oi.mm_width;
159                 heightMM = cast(int) oi.mm_height;
160             }
161 
162             if (widthMM <= 0 || heightMM <= 0)
163             {
164                 // HACK: If RandR does not provide a physical size, assume the
165                 //       X11 default 96 DPI and calcuate from the CRTC viewport
166                 // NOTE: These members are affected by rotation, unlike the mode
167                 //       info and output info members
168                 widthMM  = cast(int) (ci.width * 25.4f / 96.0f);
169                 heightMM = cast(int) (ci.height * 25.4f / 96.0f);
170             }
171 
172             _GLFWmonitor* monitor = _glfwAllocMonitor(oi.name, widthMM, heightMM);
173             monitor.x11.output = sr.outputs[i];
174             monitor.x11.crtc   = oi.crtc;
175 
176             for (j = 0;  j < screenCount;  j++)
177             {
178                 if (screens[j].x_org == ci.x &&
179                     screens[j].y_org == ci.y &&
180                     screens[j].width == ci.width &&
181                     screens[j].height == ci.height)
182                 {
183                     monitor.x11.index = j;
184                     break;
185                 }
186             }
187 
188             if (monitor.x11.output == primary)
189                 type = _GLFW_INSERT_FIRST;
190             else
191                 type = _GLFW_INSERT_LAST;
192 
193             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
194 
195             _glfw.x11.randr.FreeOutputInfo(oi);
196             _glfw.x11.randr.FreeCrtcInfo(ci);
197         }
198 
199         _glfw.x11.randr.FreeScreenResources(sr);
200 
201         if (screens)
202             XFree(screens);
203 
204         for (int i = 0;  i < disconnectedCount;  i++)
205         {
206             if (disconnected[i])
207                 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
208         }
209 
210         free(disconnected);
211     }
212     else
213     {
214         const(int) widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen);
215         const(int) heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen);
216 
217         _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM),
218                           GLFW_CONNECTED,
219                           _GLFW_INSERT_FIRST);
220     }
221 }
222 
223 // Set the current video mode for the specified monitor
224 //
225 void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const(GLFWvidmode)* desired) {
226     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
227     {
228         GLFWvidmode current;
229         RRMode native = None;
230 
231         const(GLFWvidmode)* best = _glfwChooseVideoMode(monitor, desired);
232         _glfwPlatformGetVideoMode(monitor, &current);
233         if (_glfwCompareVideoModes(&current, best) == 0)
234             return;
235 
236         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
237         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
238         XRROutputInfo* oi = _glfw.x11.randr.GetOutputInfo(_glfw.x11.display, sr, monitor.x11.output);
239 
240         for (int i = 0;  i < oi.nmode;  i++)
241         {
242             const(XRRModeInfo)* mi = getModeInfo(sr, oi.modes[i]);
243             if (!modeIsGood(mi))
244                 continue;
245 
246             const(GLFWvidmode) mode = vidmodeFromModeInfo(mi, ci);
247             if (_glfwCompareVideoModes(best, &mode) == 0)
248             {
249                 native = mi.id;
250                 break;
251             }
252         }
253 
254         if (native)
255         {
256             if (monitor.x11.oldMode == None)
257                 monitor.x11.oldMode = ci.mode;
258 
259             _glfw.x11.randr.SetCrtcConfig(_glfw.x11.display,
260                              sr, monitor.x11.crtc,
261                              CurrentTime,
262                              ci.x, ci.y,
263                              native,
264                              ci.rotation,
265                              ci.outputs,
266                              ci.noutput);
267         }
268 
269         _glfw.x11.randr.FreeOutputInfo(oi);
270         _glfw.x11.randr.FreeCrtcInfo(ci);
271         _glfw.x11.randr.FreeScreenResources(sr);
272     }
273 }
274 
275 // Restore the saved (original) video mode for the specified monitor
276 //
277 void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) {
278     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
279     {
280         if (monitor.x11.oldMode == None)
281             return;
282 
283         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
284         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
285 
286         _glfw.x11.randr.SetCrtcConfig(_glfw.x11.display,
287                          sr, monitor.x11.crtc,
288                          CurrentTime,
289                          ci.x, ci.y,
290                          monitor.x11.oldMode,
291                          ci.rotation,
292                          ci.outputs,
293                          ci.noutput);
294 
295         _glfw.x11.randr.FreeCrtcInfo(ci);
296         _glfw.x11.randr.FreeScreenResources(sr);
297 
298         monitor.x11.oldMode = None;
299     }
300 }
301 
302 
303 //////////////////////////////////////////////////////////////////////////
304 //////                       GLFW platform API                      //////
305 //////////////////////////////////////////////////////////////////////////
306 
307 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) {
308 }
309 
310 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {
311     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
312     {
313         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
314         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
315 
316         if (ci)
317         {
318             if (xpos)
319                 *xpos = ci.x;
320             if (ypos)
321                 *ypos = ci.y;
322 
323             _glfw.x11.randr.FreeCrtcInfo(ci);
324         }
325 
326         _glfw.x11.randr.FreeScreenResources(sr);
327     }
328 }
329 
330 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) {
331     if (xscale)
332         *xscale = _glfw.x11.contentScaleX;
333     if (yscale)
334         *yscale = _glfw.x11.contentScaleY;
335 }
336 
337 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) {
338     int areaX = 0;int areaY = 0;int areaWidth = 0;int areaHeight = 0;
339 
340     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
341     {
342         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
343         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
344 
345         areaX = ci.x;
346         areaY = ci.y;
347 
348         const(XRRModeInfo)* mi = getModeInfo(sr, ci.mode);
349 
350         if (ci.rotation == RR_Rotate_90 || ci.rotation == RR_Rotate_270)
351         {
352             areaWidth  = mi.height;
353             areaHeight = mi.width;
354         }
355         else
356         {
357             areaWidth  = mi.width;
358             areaHeight = mi.height;
359         }
360 
361         _glfw.x11.randr.FreeCrtcInfo(ci);
362         _glfw.x11.randr.FreeScreenResources(sr);
363     }
364     else
365     {
366         areaWidth  = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
367         areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
368     }
369 
370     if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP)
371     {
372         Atom* extents = null;
373         Atom* desktop = null;
374         c_ulong extentCount = _glfwGetWindowPropertyX11(_glfw.x11.root,
375                                       _glfw.x11.NET_WORKAREA,
376                                       XA_CARDINAL,
377                                       cast(ubyte**) &extents);
378 
379         if (_glfwGetWindowPropertyX11(_glfw.x11.root,
380                                       _glfw.x11.NET_CURRENT_DESKTOP,
381                                       XA_CARDINAL,
382                                       cast(ubyte**) &desktop) > 0)
383         {
384             if (extentCount >= 4 && *desktop < extentCount / 4)
385             {
386                 const int globalX      = cast(int) extents[*desktop * 4 + 0];
387                 const int globalY      = cast(int) extents[*desktop * 4 + 1];
388                 const int globalWidth  = cast(int) extents[*desktop * 4 + 2];
389                 const int globalHeight = cast(int) extents[*desktop * 4 + 3];
390 
391                 if (areaX < globalX)
392                 {
393                     areaWidth -= globalX - areaX;
394                     areaX = globalX;
395                 }
396 
397                 if (areaY < globalY)
398                 {
399                     areaHeight -= globalY - areaY;
400                     areaY = globalY;
401                 }
402 
403                 if (areaX + areaWidth > globalX + globalWidth)
404                     areaWidth = globalX - areaX + globalWidth;
405                 if (areaY + areaHeight > globalY + globalHeight)
406                     areaHeight = globalY - areaY + globalHeight;
407             }
408         }
409 
410         if (extents)
411             XFree(extents);
412         if (desktop)
413             XFree(desktop);
414     }
415 
416     if (xpos)
417         *xpos = areaX;
418     if (ypos)
419         *ypos = areaY;
420     if (width)
421         *width = areaWidth;
422     if (height)
423         *height = areaHeight;
424 }
425 
426 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) {
427     GLFWvidmode* result;
428 
429     *count = 0;
430 
431     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
432     {
433         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
434         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
435         XRROutputInfo* oi = _glfw.x11.randr.GetOutputInfo(_glfw.x11.display, sr, monitor.x11.output);
436 
437         result = cast(GLFWvidmode*) calloc(oi.nmode, GLFWvidmode.sizeof);
438 
439         for (int i = 0;  i < oi.nmode;  i++)
440         {
441             const(XRRModeInfo)* mi = getModeInfo(sr, oi.modes[i]);
442             if (!modeIsGood(mi))
443                 continue;
444 
445             const(GLFWvidmode) mode = vidmodeFromModeInfo(mi, ci);
446             int j;
447 
448             for (j = 0;  j < *count;  j++)
449             {
450                 if (_glfwCompareVideoModes(result + j, &mode) == 0)
451                     break;
452             }
453 
454             // Skip duplicate modes
455             if (j < *count)
456                 continue;
457 
458             (*count)++;
459             result[*count - 1] = mode;
460         }
461 
462         _glfw.x11.randr.FreeOutputInfo(oi);
463         _glfw.x11.randr.FreeCrtcInfo(ci);
464         _glfw.x11.randr.FreeScreenResources(sr);
465     }
466     else
467     {
468         *count = 1;
469         result = cast(GLFWvidmode*) calloc(1, GLFWvidmode.sizeof);
470         _glfwPlatformGetVideoMode(monitor, result);
471     }
472 
473     return result;
474 }
475 
476 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {
477     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
478     {
479         XRRScreenResources* sr = _glfw.x11.randr.GetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
480         XRRCrtcInfo* ci = _glfw.x11.randr.GetCrtcInfo(_glfw.x11.display, sr, monitor.x11.crtc);
481 
482         if (ci)
483         {
484             const(XRRModeInfo)* mi = getModeInfo(sr, ci.mode);
485             if (mi)  // mi can be NULL if the monitor has been disconnected
486                 *mode = vidmodeFromModeInfo(mi, ci);
487 
488             _glfw.x11.randr.FreeCrtcInfo(ci);
489         }
490 
491         _glfw.x11.randr.FreeScreenResources(sr);
492     }
493     else
494     {
495         mode.width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
496         mode.height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
497         mode.refreshRate = 0;
498 
499         _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
500                       &mode.redBits, &mode.greenBits, &mode.blueBits);
501     }
502 }
503 
504 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {
505     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
506     {
507         const(size_t) size = _glfw.x11.randr.GetCrtcGammaSize(_glfw.x11.display,
508                                                 monitor.x11.crtc);
509         XRRCrtcGamma* gamma = _glfw.x11.randr.GetCrtcGamma(_glfw.x11.display,
510                                               monitor.x11.crtc);
511 
512         _glfwAllocGammaArrays(ramp, cast(uint) size);
513 
514         memcpy(ramp.red,   gamma.red,   size * ushort.sizeof);
515         memcpy(ramp.green, gamma.green, size * ushort.sizeof);
516         memcpy(ramp.blue,  gamma.blue,  size * ushort.sizeof);
517 
518         _glfw.x11.randr.FreeGamma(gamma);
519         return GLFW_TRUE;
520     }
521     else if (_glfw.x11.vidmode.available)
522     {
523         int size;
524         _glfw.x11.vidmode.GetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
525 
526         _glfwAllocGammaArrays(ramp, size);
527 
528         _glfw.x11.vidmode.GetGammaRamp(_glfw.x11.display,
529                                 _glfw.x11.screen,
530                                 ramp.size, ramp.red, ramp.green, ramp.blue);
531         return GLFW_TRUE;
532     }
533     else
534     {
535         _glfwInputError(GLFW_PLATFORM_ERROR,
536                         "X11: Gamma ramp access not supported by server");
537         return GLFW_FALSE;
538     }
539 }
540 
541 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const(GLFWgammaramp)* ramp) {
542     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
543     {
544         if (_glfw.x11.randr.GetCrtcGammaSize(_glfw.x11.display, monitor.x11.crtc) != ramp.size)
545         {
546             _glfwInputError(GLFW_PLATFORM_ERROR,
547                             "X11: Gamma ramp size must match current ramp size");
548             return;
549         }
550 
551         XRRCrtcGamma* gamma = _glfw.x11.randr.AllocGamma(ramp.size);
552 
553         memcpy(gamma.red,   ramp.red,   ramp.size * ushort.sizeof);
554         memcpy(gamma.green, ramp.green, ramp.size * ushort.sizeof);
555         memcpy(gamma.blue,  ramp.blue,  ramp.size * ushort.sizeof);
556 
557         _glfw.x11.randr.SetCrtcGamma(_glfw.x11.display, monitor.x11.crtc, gamma);
558         _glfw.x11.randr.FreeGamma(gamma);
559     }
560     else if (_glfw.x11.vidmode.available)
561     {
562         _glfw.x11.vidmode.SetGammaRamp(_glfw.x11.display,
563                                 _glfw.x11.screen,
564                                 ramp.size,
565                                 cast(ushort*) ramp.red,
566                                 cast(ushort*) ramp.green,
567                                 cast(ushort*) ramp.blue);
568     }
569     else
570     {
571         _glfwInputError(GLFW_PLATFORM_ERROR,
572                         "X11: Gamma ramp access not supported by server");
573     }
574 }
575 
576 
577 //////////////////////////////////////////////////////////////////////////
578 //////                        GLFW native API                       //////
579 //////////////////////////////////////////////////////////////////////////
580 
581 RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) {
582     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
583     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"None");
584     return monitor.x11.crtc;
585 }
586 
587 RROutput glfwGetX11Monitor(GLFWmonitor* handle) {
588     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
589     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"None");
590     return monitor.x11.output;
591 }