• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //========================================================================
2 // GLFW 3.5 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 
28 #include "internal.h"
29 
30 #if defined(_GLFW_WIN32)
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <wchar.h>
36 #include <assert.h>
37 
38 
39 // Callback for EnumDisplayMonitors in createMonitor
40 //
monitorCallback(HMONITOR handle,HDC dc,RECT * rect,LPARAM data)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 //
createMonitor(DISPLAY_DEVICEW * adapter,DISPLAY_DEVICEW * display)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     _glfw_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 //
_glfwPollMonitorsWin32(void)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 = _glfw_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                 _glfw_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                 _glfw_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     _glfw_free(disconnected);
245 }
246 
247 // Change the current video mode
248 //
_glfwSetVideoModeWin32(_GLFWmonitor * monitor,const GLFWvidmode * desired)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     _glfwGetVideoModeWin32(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 //
_glfwRestoreVideoModeWin32(_GLFWmonitor * monitor)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 
_glfwGetHMONITORContentScaleWin32(HMONITOR handle,float * xscale,float * yscale)317 void _glfwGetHMONITORContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
318 {
319     UINT xdpi, ydpi;
320 
321     if (xscale)
322         *xscale = 0.f;
323     if (yscale)
324         *yscale = 0.f;
325 
326     if (IsWindows8Point1OrGreater())
327     {
328         if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK)
329         {
330             _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI");
331             return;
332         }
333     }
334     else
335     {
336         const HDC dc = GetDC(NULL);
337         xdpi = GetDeviceCaps(dc, LOGPIXELSX);
338         ydpi = GetDeviceCaps(dc, LOGPIXELSY);
339         ReleaseDC(NULL, dc);
340     }
341 
342     if (xscale)
343         *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
344     if (yscale)
345         *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
346 }
347 
348 
349 //////////////////////////////////////////////////////////////////////////
350 //////                       GLFW platform API                      //////
351 //////////////////////////////////////////////////////////////////////////
352 
_glfwFreeMonitorWin32(_GLFWmonitor * monitor)353 void _glfwFreeMonitorWin32(_GLFWmonitor* monitor)
354 {
355 }
356 
_glfwGetMonitorPosWin32(_GLFWmonitor * monitor,int * xpos,int * ypos)357 void _glfwGetMonitorPosWin32(_GLFWmonitor* monitor, int* xpos, int* ypos)
358 {
359     DEVMODEW dm;
360     ZeroMemory(&dm, sizeof(dm));
361     dm.dmSize = sizeof(dm);
362 
363     EnumDisplaySettingsExW(monitor->win32.adapterName,
364                            ENUM_CURRENT_SETTINGS,
365                            &dm,
366                            EDS_ROTATEDMODE);
367 
368     if (xpos)
369         *xpos = dm.dmPosition.x;
370     if (ypos)
371         *ypos = dm.dmPosition.y;
372 }
373 
_glfwGetMonitorContentScaleWin32(_GLFWmonitor * monitor,float * xscale,float * yscale)374 void _glfwGetMonitorContentScaleWin32(_GLFWmonitor* monitor,
375                                       float* xscale, float* yscale)
376 {
377     _glfwGetHMONITORContentScaleWin32(monitor->win32.handle, xscale, yscale);
378 }
379 
_glfwGetMonitorWorkareaWin32(_GLFWmonitor * monitor,int * xpos,int * ypos,int * width,int * height)380 void _glfwGetMonitorWorkareaWin32(_GLFWmonitor* monitor,
381                                   int* xpos, int* ypos,
382                                   int* width, int* height)
383 {
384     MONITORINFO mi = { sizeof(mi) };
385     GetMonitorInfoW(monitor->win32.handle, &mi);
386 
387     if (xpos)
388         *xpos = mi.rcWork.left;
389     if (ypos)
390         *ypos = mi.rcWork.top;
391     if (width)
392         *width = mi.rcWork.right - mi.rcWork.left;
393     if (height)
394         *height = mi.rcWork.bottom - mi.rcWork.top;
395 }
396 
_glfwGetVideoModesWin32(_GLFWmonitor * monitor,int * count)397 GLFWvidmode* _glfwGetVideoModesWin32(_GLFWmonitor* monitor, int* count)
398 {
399     int modeIndex = 0, size = 0;
400     GLFWvidmode* result = NULL;
401 
402     *count = 0;
403 
404     for (;;)
405     {
406         int i;
407         GLFWvidmode mode;
408         DEVMODEW dm;
409 
410         ZeroMemory(&dm, sizeof(dm));
411         dm.dmSize = sizeof(dm);
412 
413         if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
414             break;
415 
416         modeIndex++;
417 
418         // Skip modes with less than 15 BPP
419         if (dm.dmBitsPerPel < 15)
420             continue;
421 
422         mode.width  = dm.dmPelsWidth;
423         mode.height = dm.dmPelsHeight;
424         mode.refreshRate = dm.dmDisplayFrequency;
425         _glfwSplitBPP(dm.dmBitsPerPel,
426                       &mode.redBits,
427                       &mode.greenBits,
428                       &mode.blueBits);
429 
430         for (i = 0;  i < *count;  i++)
431         {
432             if (_glfwCompareVideoModes(result + i, &mode) == 0)
433                 break;
434         }
435 
436         // Skip duplicate modes
437         if (i < *count)
438             continue;
439 
440         if (monitor->win32.modesPruned)
441         {
442             // Skip modes not supported by the connected displays
443             if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
444                                          &dm,
445                                          NULL,
446                                          CDS_TEST,
447                                          NULL) != DISP_CHANGE_SUCCESSFUL)
448             {
449                 continue;
450             }
451         }
452 
453         if (*count == size)
454         {
455             size += 128;
456             result = (GLFWvidmode*) _glfw_realloc(result, size * sizeof(GLFWvidmode));
457         }
458 
459         (*count)++;
460         result[*count - 1] = mode;
461     }
462 
463     if (!*count)
464     {
465         // HACK: Report the current mode if no valid modes were found
466         result = _glfw_calloc(1, sizeof(GLFWvidmode));
467         _glfwGetVideoModeWin32(monitor, result);
468         *count = 1;
469     }
470 
471     return result;
472 }
473 
_glfwGetVideoModeWin32(_GLFWmonitor * monitor,GLFWvidmode * mode)474 GLFWbool _glfwGetVideoModeWin32(_GLFWmonitor* monitor, GLFWvidmode* mode)
475 {
476     DEVMODEW dm;
477     ZeroMemory(&dm, sizeof(dm));
478     dm.dmSize = sizeof(dm);
479 
480     if (!EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm))
481     {
482         _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query display settings");
483         return GLFW_FALSE;
484     }
485 
486     mode->width  = dm.dmPelsWidth;
487     mode->height = dm.dmPelsHeight;
488     mode->refreshRate = dm.dmDisplayFrequency;
489     _glfwSplitBPP(dm.dmBitsPerPel,
490                   &mode->redBits,
491                   &mode->greenBits,
492                   &mode->blueBits);
493 
494     return GLFW_TRUE;
495 }
496 
_glfwGetGammaRampWin32(_GLFWmonitor * monitor,GLFWgammaramp * ramp)497 GLFWbool _glfwGetGammaRampWin32(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
498 {
499     HDC dc;
500     WORD values[3][256];
501 
502     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
503     GetDeviceGammaRamp(dc, values);
504     DeleteDC(dc);
505 
506     _glfwAllocGammaArrays(ramp, 256);
507 
508     memcpy(ramp->red,   values[0], sizeof(values[0]));
509     memcpy(ramp->green, values[1], sizeof(values[1]));
510     memcpy(ramp->blue,  values[2], sizeof(values[2]));
511 
512     return GLFW_TRUE;
513 }
514 
_glfwSetGammaRampWin32(_GLFWmonitor * monitor,const GLFWgammaramp * ramp)515 void _glfwSetGammaRampWin32(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
516 {
517     HDC dc;
518     WORD values[3][256];
519 
520     if (ramp->size != 256)
521     {
522         _glfwInputError(GLFW_PLATFORM_ERROR,
523                         "Win32: Gamma ramp size must be 256");
524         return;
525     }
526 
527     memcpy(values[0], ramp->red,   sizeof(values[0]));
528     memcpy(values[1], ramp->green, sizeof(values[1]));
529     memcpy(values[2], ramp->blue,  sizeof(values[2]));
530 
531     dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
532     SetDeviceGammaRamp(dc, values);
533     DeleteDC(dc);
534 }
535 
536 
537 //////////////////////////////////////////////////////////////////////////
538 //////                        GLFW native API                       //////
539 //////////////////////////////////////////////////////////////////////////
540 
glfwGetWin32Adapter(GLFWmonitor * handle)541 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
542 {
543     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
544 
545     if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32)
546     {
547         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized");
548         return NULL;
549     }
550 
551     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
552     assert(monitor != NULL);
553 
554     return monitor->win32.publicAdapterName;
555 }
556 
glfwGetWin32Monitor(GLFWmonitor * handle)557 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
558 {
559     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
560 
561     if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32)
562     {
563         _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "Win32: Platform not initialized");
564         return NULL;
565     }
566 
567     _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
568     assert(monitor != NULL);
569 
570     return monitor->win32.publicDisplayName;
571 }
572 
573 #endif // _GLFW_WIN32
574 
575