]> git.sesse.net Git - pistorm/blob - raylib/external/glfw/src/win32_monitor.c
Update raylib files and Makefile for Pi 4 testing
[pistorm] / raylib / external / glfw / src / win32_monitor.c
1 //========================================================================
2 // GLFW 3.4 Win32 - 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 // Please use C89 style variable declarations in this file because VS 2010
28 //========================================================================
29
30 #include "internal.h"
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <malloc.h>
36 #include <wchar.h>
37
38
39 // Callback for EnumDisplayMonitors in createMonitor
40 //
41 static BOOL CALLBACK monitorCallback(HMONITOR handle,
42                                      HDC dc,
43                                      RECT* rect,
44                                      LPARAM data)
45 {
46     MONITORINFOEXW mi;
47     ZeroMemory(&mi, sizeof(mi));
48     mi.cbSize = sizeof(mi);
49
50     if (GetMonitorInfoW(handle, (MONITORINFO*) &mi))
51     {
52         _GLFWmonitor* monitor = (_GLFWmonitor*) data;
53         if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0)
54             monitor->win32.handle = handle;
55     }
56
57     return TRUE;
58 }
59
60 // Create monitor from an adapter and (optionally) a display
61 //
62 static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
63                                    DISPLAY_DEVICEW* display)
64 {
65     _GLFWmonitor* monitor;
66     int widthMM, heightMM;
67     char* name;
68     HDC dc;
69     DEVMODEW dm;
70     RECT rect;
71
72     if (display)
73         name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
74     else
75         name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString);
76     if (!name)
77         return NULL;
78
79     ZeroMemory(&dm, sizeof(dm));
80     dm.dmSize = sizeof(dm);
81     EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm);
82
83     dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
84
85     if (IsWindows8Point1OrGreater())
86     {
87         widthMM  = GetDeviceCaps(dc, HORZSIZE);
88         heightMM = GetDeviceCaps(dc, VERTSIZE);
89     }
90     else
91     {
92         widthMM  = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
93         heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
94     }
95
96     DeleteDC(dc);
97
98     monitor = _glfwAllocMonitor(name, widthMM, heightMM);
99     free(name);
100
101     if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
102         monitor->win32.modesPruned = GLFW_TRUE;
103
104     wcscpy(monitor->win32.adapterName, adapter->DeviceName);
105     WideCharToMultiByte(CP_UTF8, 0,
106                         adapter->DeviceName, -1,
107                         monitor->win32.publicAdapterName,
108                         sizeof(monitor->win32.publicAdapterName),
109                         NULL, NULL);
110
111     if (display)
112     {
113         wcscpy(monitor->win32.displayName, display->DeviceName);
114         WideCharToMultiByte(CP_UTF8, 0,
115                             display->DeviceName, -1,
116                             monitor->win32.publicDisplayName,
117                             sizeof(monitor->win32.publicDisplayName),
118                             NULL, NULL);
119     }
120
121     rect.left   = dm.dmPosition.x;
122     rect.top    = dm.dmPosition.y;
123     rect.right  = dm.dmPosition.x + dm.dmPelsWidth;
124     rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
125
126     EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor);
127     return monitor;
128 }
129
130
131 //////////////////////////////////////////////////////////////////////////
132 //////                       GLFW internal API                      //////
133 //////////////////////////////////////////////////////////////////////////
134
135 // Poll for changes in the set of connected monitors
136 //
137 void _glfwPollMonitorsWin32(void)
138 {
139     int i, disconnectedCount;
140     _GLFWmonitor** disconnected = NULL;
141     DWORD adapterIndex, displayIndex;
142     DISPLAY_DEVICEW adapter, display;
143     _GLFWmonitor* monitor;
144
145     disconnectedCount = _glfw.monitorCount;
146     if (disconnectedCount)
147     {
148         disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
149         memcpy(disconnected,
150                _glfw.monitors,
151                _glfw.monitorCount * sizeof(_GLFWmonitor*));
152     }
153
154     for (adapterIndex = 0;  ;  adapterIndex++)
155     {
156         int type = _GLFW_INSERT_LAST;
157
158         ZeroMemory(&adapter, sizeof(adapter));
159         adapter.cb = sizeof(adapter);
160
161         if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
162             break;
163
164         if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
165             continue;
166
167         if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
168             type = _GLFW_INSERT_FIRST;
169
170         for (displayIndex = 0;  ;  displayIndex++)
171         {
172             ZeroMemory(&display, sizeof(display));
173             display.cb = sizeof(display);
174
175             if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
176                 break;
177
178             if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
179                 continue;
180
181             for (i = 0;  i < disconnectedCount;  i++)
182             {
183                 if (disconnected[i] &&
184                     wcscmp(disconnected[i]->win32.displayName,
185                            display.DeviceName) == 0)
186                 {
187                     disconnected[i] = NULL;
188                     // handle may have changed, update
189                     EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]);
190                     break;
191                 }
192             }
193
194             if (i < disconnectedCount)
195                 continue;
196
197             monitor = createMonitor(&adapter, &display);
198             if (!monitor)
199             {
200                 free(disconnected);
201                 return;
202             }
203
204             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
205
206             type = _GLFW_INSERT_LAST;
207         }
208
209         // HACK: If an active adapter does not have any display devices
210         //       (as sometimes happens), add it directly as a monitor
211         if (displayIndex == 0)
212         {
213             for (i = 0;  i < disconnectedCount;  i++)
214             {
215                 if (disconnected[i] &&
216                     wcscmp(disconnected[i]->win32.adapterName,
217                            adapter.DeviceName) == 0)
218                 {
219                     disconnected[i] = NULL;
220                     break;
221                 }
222             }
223
224             if (i < disconnectedCount)
225                 continue;
226
227             monitor = createMonitor(&adapter, NULL);
228             if (!monitor)
229             {
230                 free(disconnected);
231                 return;
232             }
233
234             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
235         }
236     }
237
238     for (i = 0;  i < disconnectedCount;  i++)
239     {
240         if (disconnected[i])
241             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
242     }
243
244     free(disconnected);
245 }
246
247 // Change the current video mode
248 //
249 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
250 {
251     GLFWvidmode current;
252     const GLFWvidmode* best;
253     DEVMODEW dm;
254     LONG result;
255
256     best = _glfwChooseVideoMode(monitor, desired);
257     _glfwPlatformGetVideoMode(monitor, &current);
258     if (_glfwCompareVideoModes(&current, best) == 0)
259         return;
260
261     ZeroMemory(&dm, sizeof(dm));
262     dm.dmSize = sizeof(dm);
263     dm.dmFields           = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
264                             DM_DISPLAYFREQUENCY;
265     dm.dmPelsWidth        = best->width;
266     dm.dmPelsHeight       = best->height;
267     dm.dmBitsPerPel       = best->redBits + best->greenBits + best->blueBits;
268     dm.dmDisplayFrequency = best->refreshRate;
269
270     if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
271         dm.dmBitsPerPel = 32;
272
273     result = ChangeDisplaySettingsExW(monitor->win32.adapterName,
274                                       &dm,
275                                       NULL,
276                                       CDS_FULLSCREEN,
277                                       NULL);
278     if (result == DISP_CHANGE_SUCCESSFUL)
279         monitor->win32.modeChanged = GLFW_TRUE;
280     else
281     {
282         const char* description = "Unknown error";
283
284         if (result == DISP_CHANGE_BADDUALVIEW)
285             description = "The system uses DualView";
286         else if (result == DISP_CHANGE_BADFLAGS)
287             description = "Invalid flags";
288         else if (result == DISP_CHANGE_BADMODE)
289             description = "Graphics mode not supported";
290         else if (result == DISP_CHANGE_BADPARAM)
291             description = "Invalid parameter";
292         else if (result == DISP_CHANGE_FAILED)
293             description = "Graphics mode failed";
294         else if (result == DISP_CHANGE_NOTUPDATED)
295             description = "Failed to write to registry";
296         else if (result == DISP_CHANGE_RESTART)
297             description = "Computer restart required";
298
299         _glfwInputError(GLFW_PLATFORM_ERROR,
300                         "Win32: Failed to set video mode: %s",
301                         description);
302     }
303 }
304
305 // Restore the previously saved (original) video mode
306 //
307 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
308 {
309     if (monitor->win32.modeChanged)
310     {
311         ChangeDisplaySettingsExW(monitor->win32.adapterName,
312                                  NULL, NULL, CDS_FULLSCREEN, NULL);
313         monitor->win32.modeChanged = GLFW_FALSE;
314     }
315 }
316
317 void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
318 {
319     UINT xdpi, ydpi;
320
321     if (IsWindows8Point1OrGreater())
322         GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
323     else
324     {
325         const HDC dc = GetDC(NULL);
326         xdpi = GetDeviceCaps(dc, LOGPIXELSX);
327         ydpi = GetDeviceCaps(dc, LOGPIXELSY);
328         ReleaseDC(NULL, dc);
329     }
330
331     if (xscale)
332         *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
333     if (yscale)
334         *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
335 }
336
337
338 //////////////////////////////////////////////////////////////////////////
339 //////                       GLFW platform API                      //////
340 //////////////////////////////////////////////////////////////////////////
341
342 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
343 {
344 }
345
346 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
347 {
348     DEVMODEW dm;
349     ZeroMemory(&dm, sizeof(dm));
350     dm.dmSize = sizeof(dm);
351
352     EnumDisplaySettingsExW(monitor->win32.adapterName,
353                            ENUM_CURRENT_SETTINGS,
354                            &dm,
355                            EDS_ROTATEDMODE);
356
357     if (xpos)
358         *xpos = dm.dmPosition.x;
359     if (ypos)
360         *ypos = dm.dmPosition.y;
361 }
362
363 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
364                                          float* xscale, float* yscale)
365 {
366     _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
367 }
368
369 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
370                                      int* xpos, int* ypos,
371                                      int* width, int* height)
372 {
373     MONITORINFO mi = { sizeof(mi) };
374     GetMonitorInfo(monitor->win32.handle, &mi);
375
376     if (xpos)
377         *xpos = mi.rcWork.left;
378     if (ypos)
379         *ypos = mi.rcWork.top;
380     if (width)
381         *width = mi.rcWork.right - mi.rcWork.left;
382     if (height)
383         *height = mi.rcWork.bottom - mi.rcWork.top;
384 }
385
386 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
387 {
388     int modeIndex = 0, size = 0;
389     GLFWvidmode* result = NULL;
390
391     *count = 0;
392
393     for (;;)
394     {
395         int i;
396         GLFWvidmode mode;
397         DEVMODEW dm;
398
399         ZeroMemory(&dm, sizeof(dm));
400         dm.dmSize = sizeof(dm);
401
402         if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
403             break;
404
405         modeIndex++;
406
407         // Skip modes with less than 15 BPP
408         if (dm.dmBitsPerPel < 15)
409             continue;
410
411         mode.width  = dm.dmPelsWidth;
412         mode.height = dm.dmPelsHeight;
413         mode.refreshRate = dm.dmDisplayFrequency;
414         _glfwSplitBPP(dm.dmBitsPerPel,
415                       &mode.redBits,
416                       &mode.greenBits,
417                       &mode.blueBits);
418
419         for (i = 0;  i < *count;  i++)
420         {
421             if (_glfwCompareVideoModes(result + i, &mode) == 0)
422                 break;
423         }
424
425         // Skip duplicate modes
426         if (i < *count)
427             continue;
428
429         if (monitor->win32.modesPruned)
430         {
431             // Skip modes not supported by the connected displays
432             if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
433                                          &dm,
434                                          NULL,
435                                          CDS_TEST,
436                                          NULL) != DISP_CHANGE_SUCCESSFUL)
437             {
438                 continue;
439             }
440         }
441
442         if (*count == size)
443         {
444             size += 128;
445             result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode));
446         }
447
448         (*count)++;
449         result[*count - 1] = mode;
450     }
451
452     if (!*count)
453     {
454         // HACK: Report the current mode if no valid modes were found
455         result = calloc(1, sizeof(GLFWvidmode));
456         _glfwPlatformGetVideoMode(monitor, result);
457         *count = 1;
458     }
459
460     return result;
461 }
462
463 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
464 {
465     DEVMODEW dm;
466     ZeroMemory(&dm, sizeof(dm));
467     dm.dmSize = sizeof(dm);
468
469     EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
470
471     mode->width  = dm.dmPelsWidth;
472     mode->height = dm.dmPelsHeight;
473     mode->refreshRate = dm.dmDisplayFrequency;
474     _glfwSplitBPP(dm.dmBitsPerPel,
475                   &mode->redBits,
476                   &mode->greenBits,
477                   &mode->blueBits);
478 }
479
480 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
481 {
482     HDC dc;
483     WORD values[3][256];
484
485     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
486     GetDeviceGammaRamp(dc, values);
487     DeleteDC(dc);
488
489     _glfwAllocGammaArrays(ramp, 256);
490
491     memcpy(ramp->red,   values[0], sizeof(values[0]));
492     memcpy(ramp->green, values[1], sizeof(values[1]));
493     memcpy(ramp->blue,  values[2], sizeof(values[2]));
494
495     return GLFW_TRUE;
496 }
497
498 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
499 {
500     HDC dc;
501     WORD values[3][256];
502
503     if (ramp->size != 256)
504     {
505         _glfwInputError(GLFW_PLATFORM_ERROR,
506                         "Win32: Gamma ramp size must be 256");
507         return;
508     }
509
510     memcpy(values[0], ramp->red,   sizeof(values[0]));
511     memcpy(values[1], ramp->green, sizeof(values[1]));
512     memcpy(values[2], ramp->blue,  sizeof(values[2]));
513
514     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
515     SetDeviceGammaRamp(dc, values);
516     DeleteDC(dc);
517 }
518
519
520 //////////////////////////////////////////////////////////////////////////
521 //////                        GLFW native API                       //////
522 //////////////////////////////////////////////////////////////////////////
523
524 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
525 {
526     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
527     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
528     return monitor->win32.publicAdapterName;
529 }
530
531 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
532 {
533     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
534     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
535     return monitor->win32.publicDisplayName;
536 }
537