1 | //======================================================================== |
2 | // GLFW 3.4 - www.glfw.org |
3 | //------------------------------------------------------------------------ |
4 | // Copyright (c) 2002-2006 Marcus Geelnard |
5 | // Copyright (c) 2006-2019 Camilla Löwy <[email protected]> |
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 | // Please use C89 style variable declarations in this file because VS 2010 |
28 | //======================================================================== |
29 | |
30 | #include "internal.h" |
31 | |
32 | #include <assert.h> |
33 | #include <math.h> |
34 | #include <float.h> |
35 | #include <string.h> |
36 | #include <stdlib.h> |
37 | #include <limits.h> |
38 | |
39 | #if defined(__linux__) && (defined(__x86_64__) || defined(__amd64__)) |
40 | __asm__(".symver powf,powf@GLIBC_2.2.5" ); |
41 | #endif |
42 | |
43 | // Lexically compare video modes, used by qsort |
44 | // |
45 | static int compareVideoModes(const void* fp, const void* sp) |
46 | { |
47 | const GLFWvidmode* fm = fp; |
48 | const GLFWvidmode* sm = sp; |
49 | const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; |
50 | const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; |
51 | const int farea = fm->width * fm->height; |
52 | const int sarea = sm->width * sm->height; |
53 | |
54 | // First sort on color bits per pixel |
55 | if (fbpp != sbpp) |
56 | return fbpp - sbpp; |
57 | |
58 | // Then sort on screen area |
59 | if (farea != sarea) |
60 | return farea - sarea; |
61 | |
62 | // Then sort on width |
63 | if (fm->width != sm->width) |
64 | return fm->width - sm->width; |
65 | |
66 | // Lastly sort on refresh rate |
67 | return fm->refreshRate - sm->refreshRate; |
68 | } |
69 | |
70 | // Retrieves the available modes for the specified monitor |
71 | // |
72 | static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) |
73 | { |
74 | int modeCount; |
75 | GLFWvidmode* modes; |
76 | |
77 | if (monitor->modes) |
78 | return GLFW_TRUE; |
79 | |
80 | modes = _glfw.platform.getVideoModes(monitor, &modeCount); |
81 | if (!modes) |
82 | return GLFW_FALSE; |
83 | |
84 | qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); |
85 | |
86 | _glfw_free(monitor->modes); |
87 | monitor->modes = modes; |
88 | monitor->modeCount = modeCount; |
89 | |
90 | return GLFW_TRUE; |
91 | } |
92 | |
93 | |
94 | ////////////////////////////////////////////////////////////////////////// |
95 | ////// GLFW event API ////// |
96 | ////////////////////////////////////////////////////////////////////////// |
97 | |
98 | // Notifies shared code of a monitor connection or disconnection |
99 | // |
100 | void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) |
101 | { |
102 | if (action == GLFW_CONNECTED) |
103 | { |
104 | _glfw.monitorCount++; |
105 | _glfw.monitors = |
106 | _glfw_realloc(_glfw.monitors, |
107 | sizeof(_GLFWmonitor*) * _glfw.monitorCount); |
108 | |
109 | if (placement == _GLFW_INSERT_FIRST) |
110 | { |
111 | memmove(_glfw.monitors + 1, |
112 | _glfw.monitors, |
113 | ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); |
114 | _glfw.monitors[0] = monitor; |
115 | } |
116 | else |
117 | _glfw.monitors[_glfw.monitorCount - 1] = monitor; |
118 | } |
119 | else if (action == GLFW_DISCONNECTED) |
120 | { |
121 | int i; |
122 | _GLFWwindow* window; |
123 | |
124 | for (window = _glfw.windowListHead; window; window = window->next) |
125 | { |
126 | if (window->monitor == monitor) |
127 | { |
128 | int width, height, xoff, yoff; |
129 | _glfw.platform.getWindowSize(window, &width, &height); |
130 | _glfw.platform.setWindowMonitor(window, NULL, 0, 0, width, height, 0); |
131 | _glfw.platform.getWindowFrameSize(window, &xoff, &yoff, NULL, NULL); |
132 | _glfw.platform.setWindowPos(window, xoff, yoff); |
133 | } |
134 | } |
135 | |
136 | for (i = 0; i < _glfw.monitorCount; i++) |
137 | { |
138 | if (_glfw.monitors[i] == monitor) |
139 | { |
140 | _glfw.monitorCount--; |
141 | memmove(_glfw.monitors + i, |
142 | _glfw.monitors + i + 1, |
143 | ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); |
144 | break; |
145 | } |
146 | } |
147 | } |
148 | |
149 | if (_glfw.callbacks.monitor) |
150 | _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); |
151 | |
152 | if (action == GLFW_DISCONNECTED) |
153 | _glfwFreeMonitor(monitor); |
154 | } |
155 | |
156 | // Notifies shared code that a full screen window has acquired or released |
157 | // a monitor |
158 | // |
159 | void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) |
160 | { |
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 | // |
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 | // |
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 | // |
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 | // |
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 | // |
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 | // |
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 | // |
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 | |
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 | |
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 | |
326 | GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) |
327 | { |
328 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
329 | assert(monitor != NULL); |
330 | |
331 | if (xpos) |
332 | *xpos = 0; |
333 | if (ypos) |
334 | *ypos = 0; |
335 | |
336 | _GLFW_REQUIRE_INIT(); |
337 | |
338 | _glfw.platform.getMonitorPos(monitor, xpos, ypos); |
339 | } |
340 | |
341 | GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, |
342 | int* xpos, int* ypos, |
343 | int* width, int* height) |
344 | { |
345 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
346 | assert(monitor != NULL); |
347 | |
348 | if (xpos) |
349 | *xpos = 0; |
350 | if (ypos) |
351 | *ypos = 0; |
352 | if (width) |
353 | *width = 0; |
354 | if (height) |
355 | *height = 0; |
356 | |
357 | _GLFW_REQUIRE_INIT(); |
358 | |
359 | _glfw.platform.getMonitorWorkarea(monitor, xpos, ypos, width, height); |
360 | } |
361 | |
362 | GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) |
363 | { |
364 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
365 | assert(monitor != NULL); |
366 | |
367 | if (widthMM) |
368 | *widthMM = 0; |
369 | if (heightMM) |
370 | *heightMM = 0; |
371 | |
372 | _GLFW_REQUIRE_INIT(); |
373 | |
374 | if (widthMM) |
375 | *widthMM = monitor->widthMM; |
376 | if (heightMM) |
377 | *heightMM = monitor->heightMM; |
378 | } |
379 | |
380 | GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, |
381 | float* xscale, float* yscale) |
382 | { |
383 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
384 | assert(monitor != NULL); |
385 | |
386 | if (xscale) |
387 | *xscale = 0.f; |
388 | if (yscale) |
389 | *yscale = 0.f; |
390 | |
391 | _GLFW_REQUIRE_INIT(); |
392 | _glfw.platform.getMonitorContentScale(monitor, xscale, yscale); |
393 | } |
394 | |
395 | GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) |
396 | { |
397 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
398 | assert(monitor != NULL); |
399 | |
400 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
401 | return monitor->name; |
402 | } |
403 | |
404 | GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) |
405 | { |
406 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
407 | assert(monitor != NULL); |
408 | |
409 | _GLFW_REQUIRE_INIT(); |
410 | monitor->userPointer = pointer; |
411 | } |
412 | |
413 | GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) |
414 | { |
415 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
416 | assert(monitor != NULL); |
417 | |
418 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
419 | return monitor->userPointer; |
420 | } |
421 | |
422 | GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) |
423 | { |
424 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
425 | _GLFW_SWAP(GLFWmonitorfun, _glfw.callbacks.monitor, cbfun); |
426 | return cbfun; |
427 | } |
428 | |
429 | GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) |
430 | { |
431 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
432 | assert(monitor != NULL); |
433 | assert(count != NULL); |
434 | |
435 | *count = 0; |
436 | |
437 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
438 | |
439 | if (!refreshVideoModes(monitor)) |
440 | return NULL; |
441 | |
442 | *count = monitor->modeCount; |
443 | return monitor->modes; |
444 | } |
445 | |
446 | GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) |
447 | { |
448 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
449 | assert(monitor != NULL); |
450 | |
451 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
452 | |
453 | _glfw.platform.getVideoMode(monitor, &monitor->currentMode); |
454 | return &monitor->currentMode; |
455 | } |
456 | |
457 | GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) |
458 | { |
459 | unsigned int i; |
460 | unsigned short* values; |
461 | GLFWgammaramp ramp; |
462 | const GLFWgammaramp* original; |
463 | assert(handle != NULL); |
464 | assert(gamma > 0.f); |
465 | assert(gamma <= FLT_MAX); |
466 | |
467 | _GLFW_REQUIRE_INIT(); |
468 | |
469 | if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) |
470 | { |
471 | _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f" , gamma); |
472 | return; |
473 | } |
474 | |
475 | original = glfwGetGammaRamp(handle); |
476 | if (!original) |
477 | return; |
478 | |
479 | values = _glfw_calloc(original->size, sizeof(unsigned short)); |
480 | |
481 | for (i = 0; i < original->size; i++) |
482 | { |
483 | float value; |
484 | |
485 | // Calculate intensity |
486 | value = i / (float) (original->size - 1); |
487 | // Apply gamma curve |
488 | value = powf(value, 1.f / gamma) * 65535.f + 0.5f; |
489 | // Clamp to value range |
490 | value = _glfw_fminf(value, 65535.f); |
491 | |
492 | values[i] = (unsigned short) value; |
493 | } |
494 | |
495 | ramp.red = values; |
496 | ramp.green = values; |
497 | ramp.blue = values; |
498 | ramp.size = original->size; |
499 | |
500 | glfwSetGammaRamp(handle, &ramp); |
501 | _glfw_free(values); |
502 | } |
503 | |
504 | GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) |
505 | { |
506 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
507 | assert(monitor != NULL); |
508 | |
509 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
510 | |
511 | _glfwFreeGammaArrays(&monitor->currentRamp); |
512 | if (!_glfw.platform.getGammaRamp(monitor, &monitor->currentRamp)) |
513 | return NULL; |
514 | |
515 | return &monitor->currentRamp; |
516 | } |
517 | |
518 | GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) |
519 | { |
520 | _GLFWmonitor* monitor = (_GLFWmonitor*) handle; |
521 | assert(monitor != NULL); |
522 | assert(ramp != NULL); |
523 | assert(ramp->size > 0); |
524 | assert(ramp->red != NULL); |
525 | assert(ramp->green != NULL); |
526 | assert(ramp->blue != NULL); |
527 | |
528 | if (ramp->size <= 0) |
529 | { |
530 | _glfwInputError(GLFW_INVALID_VALUE, |
531 | "Invalid gamma ramp size %i" , |
532 | ramp->size); |
533 | return; |
534 | } |
535 | |
536 | _GLFW_REQUIRE_INIT(); |
537 | |
538 | if (!monitor->originalRamp.size) |
539 | { |
540 | if (!_glfw.platform.getGammaRamp(monitor, &monitor->originalRamp)) |
541 | return; |
542 | } |
543 | |
544 | _glfw.platform.setGammaRamp(monitor, ramp); |
545 | } |
546 | |
547 | |