1 //========================================================================
2 // GLFW 3.5 - 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 <assert.h>
31 #include <math.h>
32 #include <float.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <limits.h>
36 
37 
38 // Lexically compare video modes, used by qsort
39 //
compareVideoModes(const void* fp, const void* sp)40 static int compareVideoModes(const void* fp, const void* sp)
41 {
42     const GLFWvidmode* fm = fp;
43     const GLFWvidmode* sm = sp;
44     const int fbpp = fm->redBits + fm->greenBits + fm->blueBits;
45     const int sbpp = sm->redBits + sm->greenBits + sm->blueBits;
46     const int farea = fm->width * fm->height;
47     const int sarea = sm->width * sm->height;
48 
49     // First sort on color bits per pixel
50     if (fbpp != sbpp)
51         return fbpp - sbpp;
52 
53     // Then sort on screen area
54     if (farea != sarea)
55         return farea - sarea;
56 
57     // Then sort on width
58     if (fm->width != sm->width)
59         return fm->width - sm->width;
60 
61     // Lastly sort on refresh rate
62     return fm->refreshRate - sm->refreshRate;
63 }
64 
65 // Retrieves the available modes for the specified monitor
66 //
refreshVideoModes(_GLFWmonitor* monitor)67 static GLFWbool refreshVideoModes(_GLFWmonitor* monitor)
68 {
69     int modeCount;
70     GLFWvidmode* modes;
71 
72     if (monitor->modes)
73         return GLFW_TRUE;
74 
75     modes = _glfw.platform.getVideoModes(monitor, &modeCount);
76     if (!modes)
77         return GLFW_FALSE;
78 
79     qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes);
80 
81     _glfw_free(monitor->modes);
82     monitor->modes = modes;
83     monitor->modeCount = modeCount;
84 
85     return GLFW_TRUE;
86 }
87 
88 
89 //////////////////////////////////////////////////////////////////////////
90 //////                         GLFW event API                       //////
91 //////////////////////////////////////////////////////////////////////////
92 
93 // Notifies shared code of a monitor connection or disconnection
94 //
_glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)95 void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
96 {
97     assert(monitor != NULL);
98     assert(action == GLFW_CONNECTED || action == GLFW_DISCONNECTED);
99     assert(placement == _GLFW_INSERT_FIRST || placement == _GLFW_INSERT_LAST);
100 
101     if (action == GLFW_CONNECTED)
102     {
103         _glfw.monitorCount++;
104         _glfw.monitors =
105             _glfw_realloc(_glfw.monitors,
106                           sizeof(_GLFWmonitor*) * _glfw.monitorCount);
107 
108         if (placement == _GLFW_INSERT_FIRST)
109         {
110             memmove(_glfw.monitors + 1,
111                     _glfw.monitors,
112                     ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*));
113             _glfw.monitors[0] = monitor;
114         }
115         else
116             _glfw.monitors[_glfw.monitorCount - 1] = monitor;
117     }
118     else if (action == GLFW_DISCONNECTED)
119     {
120         int i;
121         _GLFWwindow* window;
122 
123         for (window = _glfw.windowListHead;  window;  window = window->next)
124         {
125             if (window->monitor == monitor)
126             {
127                 int width, height, xoff, yoff;
128                 _glfw.platform.getWindowSize(window, &width, &height);
129                 _glfw.platform.setWindowMonitor(window, NULL, 0, 0, width, height, 0);
130                 _glfw.platform.getWindowFrameSize(window, &xoff, &yoff, NULL, NULL);
131                 _glfw.platform.setWindowPos(window, xoff, yoff);
132             }
133         }
134 
135         for (i = 0;  i < _glfw.monitorCount;  i++)
136         {
137             if (_glfw.monitors[i] == monitor)
138             {
139                 _glfw.monitorCount--;
140                 memmove(_glfw.monitors + i,
141                         _glfw.monitors + i + 1,
142                         ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*));
143                 break;
144             }
145         }
146     }
147 
148     if (_glfw.callbacks.monitor)
149         _glfw.callbacks.monitor((GLFWmonitor*) monitor, action);
150 
151     if (action == GLFW_DISCONNECTED)
152         _glfwFreeMonitor(monitor);
153 }
154 
155 // Notifies shared code that a full screen window has acquired or released
156 // a monitor
157 //
_glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)158 void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window)
159 {
160     assert(monitor != NULL);
161     monitor->window = window;
162 }
163 
164 
165 //////////////////////////////////////////////////////////////////////////
166 //////                       GLFW internal API                      //////
167 //////////////////////////////////////////////////////////////////////////
168 
169 // Allocates and returns a monitor object with the specified name and dimensions
170 //
_glfwAllocMonitor(const char* name, int widthMM, int heightMM)171 _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM)
172 {
173     _GLFWmonitor* monitor = _glfw_calloc(1, sizeof(_GLFWmonitor));
174     monitor->widthMM = widthMM;
175     monitor->heightMM = heightMM;
176 
177     strncpy(monitor->name, name, sizeof(monitor->name) - 1);
178 
179     return monitor;
180 }
181 
182 // Frees a monitor object and any data associated with it
183 //
_glfwFreeMonitor(_GLFWmonitor* monitor)184 void _glfwFreeMonitor(_GLFWmonitor* monitor)
185 {
186     if (monitor == NULL)
187         return;
188 
189     _glfw.platform.freeMonitor(monitor);
190 
191     _glfwFreeGammaArrays(&monitor->originalRamp);
192     _glfwFreeGammaArrays(&monitor->currentRamp);
193 
194     _glfw_free(monitor->modes);
195     _glfw_free(monitor);
196 }
197 
198 // Allocates red, green and blue value arrays of the specified size
199 //
_glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)200 void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size)
201 {
202     ramp->red = _glfw_calloc(size, sizeof(unsigned short));
203     ramp->green = _glfw_calloc(size, sizeof(unsigned short));
204     ramp->blue = _glfw_calloc(size, sizeof(unsigned short));
205     ramp->size = size;
206 }
207 
208 // Frees the red, green and blue value arrays and clears the struct
209 //
_glfwFreeGammaArrays(GLFWgammaramp* ramp)210 void _glfwFreeGammaArrays(GLFWgammaramp* ramp)
211 {
212     _glfw_free(ramp->red);
213     _glfw_free(ramp->green);
214     _glfw_free(ramp->blue);
215 
216     memset(ramp, 0, sizeof(GLFWgammaramp));
217 }
218 
219 // Chooses the video mode most closely matching the desired one
220 //
_glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired)221 const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
222                                         const GLFWvidmode* desired)
223 {
224     int i;
225     unsigned int sizeDiff, leastSizeDiff = UINT_MAX;
226     unsigned int rateDiff, leastRateDiff = UINT_MAX;
227     unsigned int colorDiff, leastColorDiff = UINT_MAX;
228     const GLFWvidmode* current;
229     const GLFWvidmode* closest = NULL;
230 
231     if (!refreshVideoModes(monitor))
232         return NULL;
233 
234     for (i = 0;  i < monitor->modeCount;  i++)
235     {
236         current = monitor->modes + i;
237 
238         colorDiff = 0;
239 
240         if (desired->redBits != GLFW_DONT_CARE)
241             colorDiff += abs(current->redBits - desired->redBits);
242         if (desired->greenBits != GLFW_DONT_CARE)
243             colorDiff += abs(current->greenBits - desired->greenBits);
244         if (desired->blueBits != GLFW_DONT_CARE)
245             colorDiff += abs(current->blueBits - desired->blueBits);
246 
247         sizeDiff = abs((current->width - desired->width) *
248                        (current->width - desired->width) +
249                        (current->height - desired->height) *
250                        (current->height - desired->height));
251 
252         if (desired->refreshRate != GLFW_DONT_CARE)
253             rateDiff = abs(current->refreshRate - desired->refreshRate);
254         else
255             rateDiff = UINT_MAX - current->refreshRate;
256 
257         if ((colorDiff < leastColorDiff) ||
258             (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) ||
259             (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff))
260         {
261             closest = current;
262             leastSizeDiff = sizeDiff;
263             leastRateDiff = rateDiff;
264             leastColorDiff = colorDiff;
265         }
266     }
267 
268     return closest;
269 }
270 
271 // Performs lexical comparison between two @ref GLFWvidmode structures
272 //
_glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm)273 int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm)
274 {
275     return compareVideoModes(fm, sm);
276 }
277 
278 // Splits a color depth into red, green and blue bit depths
279 //
_glfwSplitBPP(int bpp, int* red, int* green, int* blue)280 void _glfwSplitBPP(int bpp, int* red, int* green, int* blue)
281 {
282     int delta;
283 
284     // We assume that by 32 the user really meant 24
285     if (bpp == 32)
286         bpp = 24;
287 
288     // Convert "bits per pixel" to red, green & blue sizes
289 
290     *red = *green = *blue = bpp / 3;
291     delta = bpp - (*red * 3);
292     if (delta >= 1)
293         *green = *green + 1;
294 
295     if (delta == 2)
296         *red = *red + 1;
297 }
298 
299 
300 //////////////////////////////////////////////////////////////////////////
301 //////                        GLFW public API                       //////
302 //////////////////////////////////////////////////////////////////////////
303 
glfwGetMonitors(int* count)304 GLFWAPI GLFWmonitor** glfwGetMonitors(int* count)
305 {
306     assert(count != NULL);
307 
308     *count = 0;
309 
310     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
311 
312     *count = _glfw.monitorCount;
313     return (GLFWmonitor**) _glfw.monitors;
314 }
315 
glfwGetPrimaryMonitor(void)316 GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void)
317 {
318     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
319 
320     if (!_glfw.monitorCount)
321         return NULL;
322 
323     return (GLFWmonitor*) _glfw.monitors[0];
324 }
325 
glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos)326 GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos)
327 {
328     if (xpos)
329         *xpos = 0;
330     if (ypos)
331         *ypos = 0;
332 
333     _GLFW_REQUIRE_INIT();
334 
335     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
336     assert(monitor != NULL);
337 
338     _glfw.platform.getMonitorPos(monitor, xpos, ypos);
339 }
340 
glfwGetMonitorWorkarea(GLFWmonitor* handle, int* xpos, int* ypos, int* width, int* height)341 GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle,
342                                     int* xpos, int* ypos,
343                                     int* width, int* height)
344 {
345     if (xpos)
346         *xpos = 0;
347     if (ypos)
348         *ypos = 0;
349     if (width)
350         *width = 0;
351     if (height)
352         *height = 0;
353 
354     _GLFW_REQUIRE_INIT();
355 
356     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
357     assert(monitor != NULL);
358 
359     _glfw.platform.getMonitorWorkarea(monitor, xpos, ypos, width, height);
360 }
361 
glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM)362 GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM)
363 {
364     if (widthMM)
365         *widthMM = 0;
366     if (heightMM)
367         *heightMM = 0;
368 
369     _GLFW_REQUIRE_INIT();
370 
371     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
372     assert(monitor != NULL);
373 
374     if (widthMM)
375         *widthMM = monitor->widthMM;
376     if (heightMM)
377         *heightMM = monitor->heightMM;
378 }
379 
glfwGetMonitorContentScale(GLFWmonitor* handle, float* xscale, float* yscale)380 GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle,
381                                         float* xscale, float* yscale)
382 {
383     if (xscale)
384         *xscale = 0.f;
385     if (yscale)
386         *yscale = 0.f;
387 
388     _GLFW_REQUIRE_INIT();
389 
390     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
391     assert(monitor != NULL);
392 
393     _glfw.platform.getMonitorContentScale(monitor, xscale, yscale);
394 }
395 
glfwGetMonitorName(GLFWmonitor* handle)396 GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
397 {
398     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
399 
400     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
401     assert(monitor != NULL);
402 
403     return monitor->name;
404 }
405 
glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)406 GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)
407 {
408     _GLFW_REQUIRE_INIT();
409 
410     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
411     assert(monitor != NULL);
412 
413     monitor->userPointer = pointer;
414 }
415 
glfwGetMonitorUserPointer(GLFWmonitor* handle)416 GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle)
417 {
418     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
419 
420     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
421     assert(monitor != NULL);
422 
423     return monitor->userPointer;
424 }
425 
glfwSetMonitorCallback(GLFWmonitorfun cbfun)426 GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun)
427 {
428     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
429     _GLFW_SWAP(GLFWmonitorfun, _glfw.callbacks.monitor, cbfun);
430     return cbfun;
431 }
432 
glfwGetVideoModes(GLFWmonitor* handle, int* count)433 GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count)
434 {
435     assert(count != NULL);
436 
437     *count = 0;
438 
439     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
440 
441     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
442     assert(monitor != NULL);
443 
444     if (!refreshVideoModes(monitor))
445         return NULL;
446 
447     *count = monitor->modeCount;
448     return monitor->modes;
449 }
450 
glfwGetVideoMode(GLFWmonitor* handle)451 GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle)
452 {
453     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
454 
455     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
456     assert(monitor != NULL);
457 
458     if (!_glfw.platform.getVideoMode(monitor, &monitor->currentMode))
459         return NULL;
460 
461     return &monitor->currentMode;
462 }
463 
glfwSetGamma(GLFWmonitor* handle, float gamma)464 GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
465 {
466     unsigned int i;
467     unsigned short* values;
468     GLFWgammaramp ramp;
469     const GLFWgammaramp* original;
470     assert(gamma > 0.f);
471     assert(gamma <= FLT_MAX);
472 
473     _GLFW_REQUIRE_INIT();
474 
475     assert(handle != NULL);
476 
477     if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX)
478     {
479         _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma);
480         return;
481     }
482 
483     original = glfwGetGammaRamp(handle);
484     if (!original)
485         return;
486 
487     values = _glfw_calloc(original->size, sizeof(unsigned short));
488 
489     for (i = 0;  i < original->size;  i++)
490     {
491         float value;
492 
493         // Calculate intensity
494         value = i / (float) (original->size - 1);
495         // Apply gamma curve
496         value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
497         // Clamp to value range
498         value = fminf(value, 65535.f);
499 
500         values[i] = (unsigned short) value;
501     }
502 
503     ramp.red = values;
504     ramp.green = values;
505     ramp.blue = values;
506     ramp.size = original->size;
507 
508     glfwSetGammaRamp(handle, &ramp);
509     _glfw_free(values);
510 }
511 
glfwGetGammaRamp(GLFWmonitor* handle)512 GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle)
513 {
514     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
515 
516     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
517     assert(monitor != NULL);
518 
519     _glfwFreeGammaArrays(&monitor->currentRamp);
520     if (!_glfw.platform.getGammaRamp(monitor, &monitor->currentRamp))
521         return NULL;
522 
523     return &monitor->currentRamp;
524 }
525 
glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp)526 GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp)
527 {
528     assert(ramp != NULL);
529     assert(ramp->size > 0);
530     assert(ramp->red != NULL);
531     assert(ramp->green != NULL);
532     assert(ramp->blue != NULL);
533 
534     _GLFW_REQUIRE_INIT();
535 
536     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
537     assert(monitor != NULL);
538 
539     if (ramp->size <= 0)
540     {
541         _glfwInputError(GLFW_INVALID_VALUE,
542                         "Invalid gamma ramp size %i",
543                         ramp->size);
544         return;
545     }
546 
547     if (!monitor->originalRamp.size)
548     {
549         if (!_glfw.platform.getGammaRamp(monitor, &monitor->originalRamp))
550             return;
551     }
552 
553     _glfw.platform.setGammaRamp(monitor, ramp);
554 }
555 
556