master
ブランチ master
cpputils / deps / glfw / src / win32_monitor.c
16.3 KB、535行 生 Blame 履歴
1//========================================================================
2// GLFW 3.3 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//
41static 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//
62static _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//
137void _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 break;
189 }
190 }
191
192 if (i < disconnectedCount)
193 continue;
194
195 monitor = createMonitor(&adapter, &display);
196 if (!monitor)
197 {
198 free(disconnected);
199 return;
200 }
201
202 _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
203
204 type = _GLFW_INSERT_LAST;
205 }
206
207 // HACK: If an active adapter does not have any display devices
208 // (as sometimes happens), add it directly as a monitor
209 if (displayIndex == 0)
210 {
211 for (i = 0; i < disconnectedCount; i++)
212 {
213 if (disconnected[i] &&
214 wcscmp(disconnected[i]->win32.adapterName,
215 adapter.DeviceName) == 0)
216 {
217 disconnected[i] = NULL;
218 break;
219 }
220 }
221
222 if (i < disconnectedCount)
223 continue;
224
225 monitor = createMonitor(&adapter, NULL);
226 if (!monitor)
227 {
228 free(disconnected);
229 return;
230 }
231
232 _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
233 }
234 }
235
236 for (i = 0; i < disconnectedCount; i++)
237 {
238 if (disconnected[i])
239 _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
240 }
241
242 free(disconnected);
243}
244
245// Change the current video mode
246//
247void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
248{
249 GLFWvidmode current;
250 const GLFWvidmode* best;
251 DEVMODEW dm;
252 LONG result;
253
254 best = _glfwChooseVideoMode(monitor, desired);
255 _glfwPlatformGetVideoMode(monitor, &current);
256 if (_glfwCompareVideoModes(&current, best) == 0)
257 return;
258
259 ZeroMemory(&dm, sizeof(dm));
260 dm.dmSize = sizeof(dm);
261 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
262 DM_DISPLAYFREQUENCY;
263 dm.dmPelsWidth = best->width;
264 dm.dmPelsHeight = best->height;
265 dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits;
266 dm.dmDisplayFrequency = best->refreshRate;
267
268 if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
269 dm.dmBitsPerPel = 32;
270
271 result = ChangeDisplaySettingsExW(monitor->win32.adapterName,
272 &dm,
273 NULL,
274 CDS_FULLSCREEN,
275 NULL);
276 if (result == DISP_CHANGE_SUCCESSFUL)
277 monitor->win32.modeChanged = GLFW_TRUE;
278 else
279 {
280 const char* description = "Unknown error";
281
282 if (result == DISP_CHANGE_BADDUALVIEW)
283 description = "The system uses DualView";
284 else if (result == DISP_CHANGE_BADFLAGS)
285 description = "Invalid flags";
286 else if (result == DISP_CHANGE_BADMODE)
287 description = "Graphics mode not supported";
288 else if (result == DISP_CHANGE_BADPARAM)
289 description = "Invalid parameter";
290 else if (result == DISP_CHANGE_FAILED)
291 description = "Graphics mode failed";
292 else if (result == DISP_CHANGE_NOTUPDATED)
293 description = "Failed to write to registry";
294 else if (result == DISP_CHANGE_RESTART)
295 description = "Computer restart required";
296
297 _glfwInputError(GLFW_PLATFORM_ERROR,
298 "Win32: Failed to set video mode: %s",
299 description);
300 }
301}
302
303// Restore the previously saved (original) video mode
304//
305void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
306{
307 if (monitor->win32.modeChanged)
308 {
309 ChangeDisplaySettingsExW(monitor->win32.adapterName,
310 NULL, NULL, CDS_FULLSCREEN, NULL);
311 monitor->win32.modeChanged = GLFW_FALSE;
312 }
313}
314
315void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale)
316{
317 UINT xdpi, ydpi;
318
319 if (IsWindows8Point1OrGreater())
320 GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
321 else
322 {
323 const HDC dc = GetDC(NULL);
324 xdpi = GetDeviceCaps(dc, LOGPIXELSX);
325 ydpi = GetDeviceCaps(dc, LOGPIXELSY);
326 ReleaseDC(NULL, dc);
327 }
328
329 if (xscale)
330 *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI;
331 if (yscale)
332 *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI;
333}
334
335
336//////////////////////////////////////////////////////////////////////////
337////// GLFW platform API //////
338//////////////////////////////////////////////////////////////////////////
339
340void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
341{
342}
343
344void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
345{
346 DEVMODEW dm;
347 ZeroMemory(&dm, sizeof(dm));
348 dm.dmSize = sizeof(dm);
349
350 EnumDisplaySettingsExW(monitor->win32.adapterName,
351 ENUM_CURRENT_SETTINGS,
352 &dm,
353 EDS_ROTATEDMODE);
354
355 if (xpos)
356 *xpos = dm.dmPosition.x;
357 if (ypos)
358 *ypos = dm.dmPosition.y;
359}
360
361void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
362 float* xscale, float* yscale)
363{
364 _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale);
365}
366
367void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
368 int* xpos, int* ypos,
369 int* width, int* height)
370{
371 MONITORINFO mi = { sizeof(mi) };
372 GetMonitorInfo(monitor->win32.handle, &mi);
373
374 if (xpos)
375 *xpos = mi.rcWork.left;
376 if (ypos)
377 *ypos = mi.rcWork.top;
378 if (width)
379 *width = mi.rcWork.right - mi.rcWork.left;
380 if (height)
381 *height = mi.rcWork.bottom - mi.rcWork.top;
382}
383
384GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
385{
386 int modeIndex = 0, size = 0;
387 GLFWvidmode* result = NULL;
388
389 *count = 0;
390
391 for (;;)
392 {
393 int i;
394 GLFWvidmode mode;
395 DEVMODEW dm;
396
397 ZeroMemory(&dm, sizeof(dm));
398 dm.dmSize = sizeof(dm);
399
400 if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
401 break;
402
403 modeIndex++;
404
405 // Skip modes with less than 15 BPP
406 if (dm.dmBitsPerPel < 15)
407 continue;
408
409 mode.width = dm.dmPelsWidth;
410 mode.height = dm.dmPelsHeight;
411 mode.refreshRate = dm.dmDisplayFrequency;
412 _glfwSplitBPP(dm.dmBitsPerPel,
413 &mode.redBits,
414 &mode.greenBits,
415 &mode.blueBits);
416
417 for (i = 0; i < *count; i++)
418 {
419 if (_glfwCompareVideoModes(result + i, &mode) == 0)
420 break;
421 }
422
423 // Skip duplicate modes
424 if (i < *count)
425 continue;
426
427 if (monitor->win32.modesPruned)
428 {
429 // Skip modes not supported by the connected displays
430 if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
431 &dm,
432 NULL,
433 CDS_TEST,
434 NULL) != DISP_CHANGE_SUCCESSFUL)
435 {
436 continue;
437 }
438 }
439
440 if (*count == size)
441 {
442 size += 128;
443 result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode));
444 }
445
446 (*count)++;
447 result[*count - 1] = mode;
448 }
449
450 if (!*count)
451 {
452 // HACK: Report the current mode if no valid modes were found
453 result = calloc(1, sizeof(GLFWvidmode));
454 _glfwPlatformGetVideoMode(monitor, result);
455 *count = 1;
456 }
457
458 return result;
459}
460
461void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
462{
463 DEVMODEW dm;
464 ZeroMemory(&dm, sizeof(dm));
465 dm.dmSize = sizeof(dm);
466
467 EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
468
469 mode->width = dm.dmPelsWidth;
470 mode->height = dm.dmPelsHeight;
471 mode->refreshRate = dm.dmDisplayFrequency;
472 _glfwSplitBPP(dm.dmBitsPerPel,
473 &mode->redBits,
474 &mode->greenBits,
475 &mode->blueBits);
476}
477
478GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
479{
480 HDC dc;
481 WORD values[3][256];
482
483 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
484 GetDeviceGammaRamp(dc, values);
485 DeleteDC(dc);
486
487 _glfwAllocGammaArrays(ramp, 256);
488
489 memcpy(ramp->red, values[0], sizeof(values[0]));
490 memcpy(ramp->green, values[1], sizeof(values[1]));
491 memcpy(ramp->blue, values[2], sizeof(values[2]));
492
493 return GLFW_TRUE;
494}
495
496void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
497{
498 HDC dc;
499 WORD values[3][256];
500
501 if (ramp->size != 256)
502 {
503 _glfwInputError(GLFW_PLATFORM_ERROR,
504 "Win32: Gamma ramp size must be 256");
505 return;
506 }
507
508 memcpy(values[0], ramp->red, sizeof(values[0]));
509 memcpy(values[1], ramp->green, sizeof(values[1]));
510 memcpy(values[2], ramp->blue, sizeof(values[2]));
511
512 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
513 SetDeviceGammaRamp(dc, values);
514 DeleteDC(dc);
515}
516
517
518//////////////////////////////////////////////////////////////////////////
519////// GLFW native API //////
520//////////////////////////////////////////////////////////////////////////
521
522GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
523{
524 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
525 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
526 return monitor->win32.publicAdapterName;
527}
528
529GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
530{
531 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
532 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
533 return monitor->win32.publicDisplayName;
534}
535