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