1 //========================================================================
2 // GLFW 3.1 X11 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.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 
34 
35 // Check whether the display mode should be included in enumeration
36 //
modeIsGood(const XRRModeInfo * mi)37 static GLboolean modeIsGood(const XRRModeInfo* mi)
38 {
39     return (mi->modeFlags & RR_Interlace) == 0;
40 }
41 
42 // Calculates the refresh rate, in Hz, from the specified RandR mode info
43 //
calculateRefreshRate(const XRRModeInfo * mi)44 static int calculateRefreshRate(const XRRModeInfo* mi)
45 {
46     if (mi->hTotal && mi->vTotal)
47         return (int) ((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal));
48     else
49         return 0;
50 }
51 
52 // Returns the mode info for a RandR mode XID
53 //
getModeInfo(const XRRScreenResources * sr,RRMode id)54 static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id)
55 {
56     int i;
57 
58     for (i = 0;  i < sr->nmode;  i++)
59     {
60         if (sr->modes[i].id == id)
61             return sr->modes + i;
62     }
63 
64     return NULL;
65 }
66 
67 // Convert RandR mode info to GLFW video mode
68 //
vidmodeFromModeInfo(const XRRModeInfo * mi,const XRRCrtcInfo * ci)69 static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi,
70                                        const XRRCrtcInfo* ci)
71 {
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 // Set the current video mode for the specified monitor
99 //
_glfwSetVideoMode(_GLFWmonitor * monitor,const GLFWvidmode * desired)100 GLboolean _glfwSetVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired)
101 {
102     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
103     {
104         XRRScreenResources* sr;
105         XRRCrtcInfo* ci;
106         XRROutputInfo* oi;
107         GLFWvidmode current;
108         const GLFWvidmode* best;
109         RRMode native = None;
110         int i;
111 
112         best = _glfwChooseVideoMode(monitor, desired);
113         _glfwPlatformGetVideoMode(monitor, &current);
114         if (_glfwCompareVideoModes(&current, best) == 0)
115             return GL_TRUE;
116 
117         sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root);
118         ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
119         oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
120 
121         for (i = 0;  i < oi->nmode;  i++)
122         {
123             const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
124             if (!modeIsGood(mi))
125                 continue;
126 
127             const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
128             if (_glfwCompareVideoModes(best, &mode) == 0)
129             {
130                 native = mi->id;
131                 break;
132             }
133         }
134 
135         if (native)
136         {
137             if (monitor->x11.oldMode == None)
138                 monitor->x11.oldMode = ci->mode;
139 
140             XRRSetCrtcConfig(_glfw.x11.display,
141                              sr, monitor->x11.crtc,
142                              CurrentTime,
143                              ci->x, ci->y,
144                              native,
145                              ci->rotation,
146                              ci->outputs,
147                              ci->noutput);
148         }
149 
150         XRRFreeOutputInfo(oi);
151         XRRFreeCrtcInfo(ci);
152         XRRFreeScreenResources(sr);
153 
154         if (!native)
155         {
156             _glfwInputError(GLFW_PLATFORM_ERROR,
157                             "X11: Monitor mode list changed");
158             return GL_FALSE;
159         }
160     }
161 
162     return GL_TRUE;
163 }
164 
165 // Restore the saved (original) video mode for the specified monitor
166 //
_glfwRestoreVideoMode(_GLFWmonitor * monitor)167 void _glfwRestoreVideoMode(_GLFWmonitor* monitor)
168 {
169     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
170     {
171         XRRScreenResources* sr;
172         XRRCrtcInfo* ci;
173 
174         if (monitor->x11.oldMode == None)
175             return;
176 
177         sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root);
178         ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
179 
180         XRRSetCrtcConfig(_glfw.x11.display,
181                          sr, monitor->x11.crtc,
182                          CurrentTime,
183                          ci->x, ci->y,
184                          monitor->x11.oldMode,
185                          ci->rotation,
186                          ci->outputs,
187                          ci->noutput);
188 
189         XRRFreeCrtcInfo(ci);
190         XRRFreeScreenResources(sr);
191 
192         monitor->x11.oldMode = None;
193     }
194 }
195 
196 
197 //////////////////////////////////////////////////////////////////////////
198 //////                       GLFW platform API                      //////
199 //////////////////////////////////////////////////////////////////////////
200 
_glfwPlatformGetMonitors(int * count)201 _GLFWmonitor** _glfwPlatformGetMonitors(int* count)
202 {
203     int i, j, k, found = 0;
204     _GLFWmonitor** monitors = NULL;
205 
206     *count = 0;
207 
208     if (_glfw.x11.randr.available)
209     {
210         int screenCount = 0;
211         XineramaScreenInfo* screens = NULL;
212         XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display,
213                                                        _glfw.x11.root);
214         RROutput primary = XRRGetOutputPrimary(_glfw.x11.display,
215                                                _glfw.x11.root);
216 
217         monitors = calloc(sr->noutput, sizeof(_GLFWmonitor*));
218 
219         if (_glfw.x11.xinerama.available)
220             screens = XineramaQueryScreens(_glfw.x11.display, &screenCount);
221 
222         for (i = 0;  i < sr->ncrtc;  i++)
223         {
224             XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display,
225                                              sr, sr->crtcs[i]);
226 
227             for (j = 0;  j < ci->noutput;  j++)
228             {
229                 int widthMM, heightMM;
230                 _GLFWmonitor* monitor;
231                 XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display,
232                                                      sr, ci->outputs[j]);
233                 if (oi->connection != RR_Connected)
234                 {
235                     XRRFreeOutputInfo(oi);
236                     continue;
237                 }
238 
239                 if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270)
240                 {
241                     widthMM  = oi->mm_height;
242                     heightMM = oi->mm_width;
243                 }
244                 else
245                 {
246                     widthMM  = oi->mm_width;
247                     heightMM = oi->mm_height;
248                 }
249 
250                 monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM);
251                 monitor->x11.output = ci->outputs[j];
252                 monitor->x11.crtc   = oi->crtc;
253 
254                 for (k = 0;  k < screenCount;  k++)
255                 {
256                     if (screens[k].x_org == ci->x &&
257                         screens[k].y_org == ci->y &&
258                         screens[k].width == ci->width &&
259                         screens[k].height == ci->height)
260                     {
261                         monitor->x11.index = k;
262                         break;
263                     }
264                 }
265 
266                 XRRFreeOutputInfo(oi);
267 
268                 found++;
269                 monitors[found - 1] = monitor;
270 
271                 if (ci->outputs[j] == primary)
272                     _GLFW_SWAP_POINTERS(monitors[0], monitors[found - 1]);
273             }
274 
275             XRRFreeCrtcInfo(ci);
276         }
277 
278         XRRFreeScreenResources(sr);
279 
280         if (screens)
281             XFree(screens);
282 
283         if (found == 0)
284         {
285             _glfwInputError(GLFW_PLATFORM_ERROR,
286                             "X11: RandR monitor support seems broken");
287 
288             _glfw.x11.randr.monitorBroken = GL_TRUE;
289             free(monitors);
290             monitors = NULL;
291         }
292     }
293 
294     if (!monitors)
295     {
296         monitors = calloc(1, sizeof(_GLFWmonitor*));
297         monitors[0] = _glfwAllocMonitor("Display",
298                                         DisplayWidthMM(_glfw.x11.display,
299                                                        _glfw.x11.screen),
300                                         DisplayHeightMM(_glfw.x11.display,
301                                                         _glfw.x11.screen));
302         found = 1;
303     }
304 
305     *count = found;
306     return monitors;
307 }
308 
_glfwPlatformIsSameMonitor(_GLFWmonitor * first,_GLFWmonitor * second)309 GLboolean _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second)
310 {
311     return first->x11.crtc == second->x11.crtc;
312 }
313 
_glfwPlatformGetMonitorPos(_GLFWmonitor * monitor,int * xpos,int * ypos)314 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
315 {
316     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
317     {
318         XRRScreenResources* sr;
319         XRRCrtcInfo* ci;
320 
321         sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
322         ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
323 
324         if (xpos)
325             *xpos = ci->x;
326         if (ypos)
327             *ypos = ci->y;
328 
329         XRRFreeCrtcInfo(ci);
330         XRRFreeScreenResources(sr);
331     }
332 }
333 
_glfwPlatformGetVideoModes(_GLFWmonitor * monitor,int * count)334 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
335 {
336     GLFWvidmode* result;
337 
338     *count = 0;
339 
340     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
341     {
342         int i, j;
343         XRRScreenResources* sr;
344         XRRCrtcInfo* ci;
345         XRROutputInfo* oi;
346 
347         sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
348         ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
349         oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output);
350 
351         result = calloc(oi->nmode, sizeof(GLFWvidmode));
352 
353         for (i = 0;  i < oi->nmode;  i++)
354         {
355             const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]);
356             if (!modeIsGood(mi))
357                 continue;
358 
359             const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci);
360 
361             for (j = 0;  j < *count;  j++)
362             {
363                 if (_glfwCompareVideoModes(result + j, &mode) == 0)
364                     break;
365             }
366 
367             // Skip duplicate modes
368             if (j < *count)
369                 continue;
370 
371             (*count)++;
372             result[*count - 1] = mode;
373         }
374 
375         XRRFreeOutputInfo(oi);
376         XRRFreeCrtcInfo(ci);
377         XRRFreeScreenResources(sr);
378     }
379     else
380     {
381         *count = 1;
382         result = calloc(1, sizeof(GLFWvidmode));
383         _glfwPlatformGetVideoMode(monitor, result);
384     }
385 
386     return result;
387 }
388 
_glfwPlatformGetVideoMode(_GLFWmonitor * monitor,GLFWvidmode * mode)389 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
390 {
391     if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
392     {
393         XRRScreenResources* sr;
394         XRRCrtcInfo* ci;
395 
396         sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
397         ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
398 
399         *mode = vidmodeFromModeInfo(getModeInfo(sr, ci->mode), ci);
400 
401         XRRFreeCrtcInfo(ci);
402         XRRFreeScreenResources(sr);
403     }
404     else
405     {
406         mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
407         mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
408         mode->refreshRate = 0;
409 
410         _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
411                       &mode->redBits, &mode->greenBits, &mode->blueBits);
412     }
413 }
414 
_glfwPlatformGetGammaRamp(_GLFWmonitor * monitor,GLFWgammaramp * ramp)415 void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
416 {
417     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
418     {
419         const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display,
420                                                 monitor->x11.crtc);
421         XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display,
422                                               monitor->x11.crtc);
423 
424         _glfwAllocGammaArrays(ramp, size);
425 
426         memcpy(ramp->red, gamma->red, size * sizeof(unsigned short));
427         memcpy(ramp->green, gamma->green, size * sizeof(unsigned short));
428         memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short));
429 
430         XRRFreeGamma(gamma);
431     }
432 #if defined(_GLFW_HAS_XF86VM)
433     else if (_glfw.x11.vidmode.available)
434     {
435         int size;
436         XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size);
437 
438         _glfwAllocGammaArrays(ramp, size);
439 
440         XF86VidModeGetGammaRamp(_glfw.x11.display,
441                                 _glfw.x11.screen,
442                                 ramp->size, ramp->red, ramp->green, ramp->blue);
443     }
444 #endif /*_GLFW_HAS_XF86VM*/
445 }
446 
_glfwPlatformSetGammaRamp(_GLFWmonitor * monitor,const GLFWgammaramp * ramp)447 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
448 {
449     if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken)
450     {
451         XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size);
452 
453         memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short));
454         memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short));
455         memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short));
456 
457         XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma);
458         XRRFreeGamma(gamma);
459     }
460 #if defined(_GLFW_HAS_XF86VM)
461     else if (_glfw.x11.vidmode.available)
462     {
463         XF86VidModeSetGammaRamp(_glfw.x11.display,
464                                 _glfw.x11.screen,
465                                 ramp->size,
466                                 (unsigned short*) ramp->red,
467                                 (unsigned short*) ramp->green,
468                                 (unsigned short*) ramp->blue);
469     }
470 #endif /*_GLFW_HAS_XF86VM*/
471 }
472 
473 
474 //////////////////////////////////////////////////////////////////////////
475 //////                        GLFW native API                       //////
476 //////////////////////////////////////////////////////////////////////////
477 
glfwGetX11Adapter(GLFWmonitor * handle)478 GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle)
479 {
480     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
481     _GLFW_REQUIRE_INIT_OR_RETURN(None);
482     return monitor->x11.crtc;
483 }
484 
glfwGetX11Monitor(GLFWmonitor * handle)485 GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle)
486 {
487     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
488     _GLFW_REQUIRE_INIT_OR_RETURN(None);
489     return monitor->x11.output;
490 }
491 
492