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, ¤t);
253 if (_glfwCompareVideoModes(¤t, 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 / (float) USER_DEFAULT_SCREEN_DPI;
328 if (yscale)
329 *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
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