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, ¤t);
258 if (_glfwCompareVideoModes(¤t, 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