1 //========================================================================
2 // GLFW 3.2 Win32 - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2002-2006 Marcus Geelnard
5 // Copyright (c) 2006-2016 Camilla Berglund <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 // Create monitor from an adapter and (optionally) a display
37 //
createMonitor(DISPLAY_DEVICEW * adapter,DISPLAY_DEVICEW * display)38 static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter,
39 DISPLAY_DEVICEW* display)
40 {
41 _GLFWmonitor* monitor;
42 char* name;
43 HDC dc;
44
45 if (display)
46 name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString);
47 else
48 name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString);
49 if (!name)
50 {
51 _glfwInputError(GLFW_PLATFORM_ERROR,
52 "Win32: Failed to convert string to UTF-8");
53 return NULL;
54 }
55
56 dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL);
57
58 monitor = _glfwAllocMonitor(name,
59 GetDeviceCaps(dc, HORZSIZE),
60 GetDeviceCaps(dc, VERTSIZE));
61
62 DeleteDC(dc);
63 free(name);
64
65 if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED)
66 monitor->win32.modesPruned = GLFW_TRUE;
67
68 wcscpy(monitor->win32.adapterName, adapter->DeviceName);
69 WideCharToMultiByte(CP_UTF8, 0,
70 adapter->DeviceName, -1,
71 monitor->win32.publicAdapterName,
72 sizeof(monitor->win32.publicAdapterName),
73 NULL, NULL);
74
75 if (display)
76 {
77 wcscpy(monitor->win32.displayName, display->DeviceName);
78 WideCharToMultiByte(CP_UTF8, 0,
79 display->DeviceName, -1,
80 monitor->win32.publicDisplayName,
81 sizeof(monitor->win32.publicDisplayName),
82 NULL, NULL);
83 }
84
85 return monitor;
86 }
87
88
89 //////////////////////////////////////////////////////////////////////////
90 ////// GLFW internal API //////
91 //////////////////////////////////////////////////////////////////////////
92
93 // Change the current video mode
94 //
_glfwSetVideoModeWin32(_GLFWmonitor * monitor,const GLFWvidmode * desired)95 GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired)
96 {
97 GLFWvidmode current;
98 const GLFWvidmode* best;
99 DEVMODEW dm;
100
101 best = _glfwChooseVideoMode(monitor, desired);
102 _glfwPlatformGetVideoMode(monitor, ¤t);
103 if (_glfwCompareVideoModes(¤t, best) == 0)
104 return GLFW_TRUE;
105
106 ZeroMemory(&dm, sizeof(dm));
107 dm.dmSize = sizeof(DEVMODEW);
108 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
109 DM_DISPLAYFREQUENCY;
110 dm.dmPelsWidth = best->width;
111 dm.dmPelsHeight = best->height;
112 dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits;
113 dm.dmDisplayFrequency = best->refreshRate;
114
115 if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
116 dm.dmBitsPerPel = 32;
117
118 if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
119 &dm,
120 NULL,
121 CDS_FULLSCREEN,
122 NULL) != DISP_CHANGE_SUCCESSFUL)
123 {
124 _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode");
125 return GLFW_FALSE;
126 }
127
128 monitor->win32.modeChanged = GLFW_TRUE;
129 return GLFW_TRUE;
130 }
131
132 // Restore the previously saved (original) video mode
133 //
_glfwRestoreVideoModeWin32(_GLFWmonitor * monitor)134 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor)
135 {
136 if (monitor->win32.modeChanged)
137 {
138 ChangeDisplaySettingsExW(monitor->win32.adapterName,
139 NULL, NULL, CDS_FULLSCREEN, NULL);
140 monitor->win32.modeChanged = GLFW_FALSE;
141 }
142 }
143
144
145 //////////////////////////////////////////////////////////////////////////
146 ////// GLFW platform API //////
147 //////////////////////////////////////////////////////////////////////////
148
_glfwPlatformGetMonitors(int * count)149 _GLFWmonitor** _glfwPlatformGetMonitors(int* count)
150 {
151 int found = 0;
152 DWORD adapterIndex, displayIndex, primaryIndex = 0;
153 DISPLAY_DEVICEW adapter, display;
154 GLFWbool hasDisplays = GLFW_FALSE;
155 _GLFWmonitor** monitors = NULL;
156
157 *count = 0;
158
159 // HACK: Check if any active adapters have connected displays
160 // If not, this is a headless system or a VMware guest
161
162 for (adapterIndex = 0; ; adapterIndex++)
163 {
164 ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW));
165 adapter.cb = sizeof(DISPLAY_DEVICEW);
166
167 if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
168 break;
169
170 if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
171 continue;
172
173 ZeroMemory(&display, sizeof(DISPLAY_DEVICEW));
174 display.cb = sizeof(DISPLAY_DEVICEW);
175
176 if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0))
177 {
178 hasDisplays = GLFW_TRUE;
179 break;
180 }
181 }
182
183 for (adapterIndex = 0; ; adapterIndex++)
184 {
185 ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW));
186 adapter.cb = sizeof(DISPLAY_DEVICEW);
187
188 if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0))
189 break;
190
191 if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
192 continue;
193
194 if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
195 primaryIndex = found;
196
197 if (hasDisplays)
198 {
199 for (displayIndex = 0; ; displayIndex++)
200 {
201 ZeroMemory(&display, sizeof(DISPLAY_DEVICEW));
202 display.cb = sizeof(DISPLAY_DEVICEW);
203
204 if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0))
205 break;
206
207 found++;
208 monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found);
209 monitors[found - 1] = createMonitor(&adapter, &display);
210 }
211 }
212 else
213 {
214 found++;
215 monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found);
216 monitors[found - 1] = createMonitor(&adapter, NULL);
217 }
218 }
219
220 _GLFW_SWAP_POINTERS(monitors[0], monitors[primaryIndex]);
221
222 *count = found;
223 return monitors;
224 }
225
_glfwPlatformIsSameMonitor(_GLFWmonitor * first,_GLFWmonitor * second)226 GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second)
227 {
228 if (wcslen(first->win32.displayName))
229 return wcscmp(first->win32.displayName, second->win32.displayName) == 0;
230 else
231 return wcscmp(first->win32.adapterName, second->win32.adapterName) == 0;
232 }
233
_glfwPlatformGetMonitorPos(_GLFWmonitor * monitor,int * xpos,int * ypos)234 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
235 {
236 DEVMODEW settings;
237 ZeroMemory(&settings, sizeof(DEVMODEW));
238 settings.dmSize = sizeof(DEVMODEW);
239
240 EnumDisplaySettingsExW(monitor->win32.adapterName,
241 ENUM_CURRENT_SETTINGS,
242 &settings,
243 EDS_ROTATEDMODE);
244
245 if (xpos)
246 *xpos = settings.dmPosition.x;
247 if (ypos)
248 *ypos = settings.dmPosition.y;
249 }
250
_glfwPlatformGetVideoModes(_GLFWmonitor * monitor,int * count)251 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
252 {
253 int modeIndex = 0, size = 0;
254 GLFWvidmode* result = NULL;
255
256 *count = 0;
257
258 for (;;)
259 {
260 int i;
261 GLFWvidmode mode;
262 DEVMODEW dm;
263
264 ZeroMemory(&dm, sizeof(DEVMODEW));
265 dm.dmSize = sizeof(DEVMODEW);
266
267 if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm))
268 break;
269
270 modeIndex++;
271
272 // Skip modes with less than 15 BPP
273 if (dm.dmBitsPerPel < 15)
274 continue;
275
276 mode.width = dm.dmPelsWidth;
277 mode.height = dm.dmPelsHeight;
278 mode.refreshRate = dm.dmDisplayFrequency;
279 _glfwSplitBPP(dm.dmBitsPerPel,
280 &mode.redBits,
281 &mode.greenBits,
282 &mode.blueBits);
283
284 for (i = 0; i < *count; i++)
285 {
286 if (_glfwCompareVideoModes(result + i, &mode) == 0)
287 break;
288 }
289
290 // Skip duplicate modes
291 if (i < *count)
292 continue;
293
294 if (monitor->win32.modesPruned)
295 {
296 // Skip modes not supported by the connected displays
297 if (ChangeDisplaySettingsExW(monitor->win32.adapterName,
298 &dm,
299 NULL,
300 CDS_TEST,
301 NULL) != DISP_CHANGE_SUCCESSFUL)
302 {
303 continue;
304 }
305 }
306
307 if (*count == size)
308 {
309 size += 128;
310 result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode));
311 }
312
313 (*count)++;
314 result[*count - 1] = mode;
315 }
316
317 if (!*count)
318 {
319 // HACK: Report the current mode if no valid modes were found
320 result = calloc(1, sizeof(GLFWvidmode));
321 _glfwPlatformGetVideoMode(monitor, result);
322 *count = 1;
323 }
324
325 return result;
326 }
327
_glfwPlatformGetVideoMode(_GLFWmonitor * monitor,GLFWvidmode * mode)328 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
329 {
330 DEVMODEW dm;
331
332 ZeroMemory(&dm, sizeof(DEVMODEW));
333 dm.dmSize = sizeof(DEVMODEW);
334
335 EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm);
336
337 mode->width = dm.dmPelsWidth;
338 mode->height = dm.dmPelsHeight;
339 mode->refreshRate = dm.dmDisplayFrequency;
340 _glfwSplitBPP(dm.dmBitsPerPel,
341 &mode->redBits,
342 &mode->greenBits,
343 &mode->blueBits);
344 }
345
_glfwPlatformGetGammaRamp(_GLFWmonitor * monitor,GLFWgammaramp * ramp)346 void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
347 {
348 HDC dc;
349 WORD values[768];
350
351 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
352 GetDeviceGammaRamp(dc, values);
353 DeleteDC(dc);
354
355 _glfwAllocGammaArrays(ramp, 256);
356
357 memcpy(ramp->red, values + 0, 256 * sizeof(unsigned short));
358 memcpy(ramp->green, values + 256, 256 * sizeof(unsigned short));
359 memcpy(ramp->blue, values + 512, 256 * sizeof(unsigned short));
360 }
361
_glfwPlatformSetGammaRamp(_GLFWmonitor * monitor,const GLFWgammaramp * ramp)362 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
363 {
364 HDC dc;
365 WORD values[768];
366
367 if (ramp->size != 256)
368 {
369 _glfwInputError(GLFW_PLATFORM_ERROR,
370 "Win32: Gamma ramp size must be 256");
371 return;
372 }
373
374 memcpy(values + 0, ramp->red, 256 * sizeof(unsigned short));
375 memcpy(values + 256, ramp->green, 256 * sizeof(unsigned short));
376 memcpy(values + 512, ramp->blue, 256 * sizeof(unsigned short));
377
378 dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL);
379 SetDeviceGammaRamp(dc, values);
380 DeleteDC(dc);
381 }
382
383
384 //////////////////////////////////////////////////////////////////////////
385 ////// GLFW native API //////
386 //////////////////////////////////////////////////////////////////////////
387
glfwGetWin32Adapter(GLFWmonitor * handle)388 GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle)
389 {
390 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
391 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
392 return monitor->win32.publicAdapterName;
393 }
394
glfwGetWin32Monitor(GLFWmonitor * handle)395 GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle)
396 {
397 _GLFWmonitor* monitor = (_GLFWmonitor*) handle;
398 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
399 return monitor->win32.publicDisplayName;
400 }
401
402