1 /// Translated from C to D
2 module glfw3.win32_monitor;
3 
4 extern(C): @nogc: nothrow: __gshared:
5 //========================================================================
6 // GLFW 3.3 Win32 - www.glfw.org
7 //------------------------------------------------------------------------
8 // Copyright (c) 2002-2006 Marcus Geelnard
9 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
10 //
11 // This software is provided 'as-is', without any express or implied
12 // warranty. In no event will the authors be held liable for any damages
13 // arising from the use of this software.
14 //
15 // Permission is granted to anyone to use this software for any purpose,
16 // including commercial applications, and to alter it and redistribute it
17 // freely, subject to the following restrictions:
18 //
19 // 1. The origin of this software must not be misrepresented; you must not
20 //    claim that you wrote the original software. If you use this software
21 //    in a product, an acknowledgment in the product documentation would
22 //    be appreciated but is not required.
23 //
24 // 2. Altered source versions must be plainly marked as such, and must not
25 //    be misrepresented as being the original software.
26 //
27 // 3. This notice may not be removed or altered from any source
28 //    distribution.
29 //
30 //========================================================================
31 // Please use C89 style variable declarations in this file because VS 2010
32 //========================================================================
33 
34 import glfw3.internal;
35 
36 import core.stdc.stdlib;
37 import core.stdc.string;
38 import core.stdc.limits;
39 import core.stdc.wchar_;
40 
41 package:
42 
43 // Callback for EnumDisplayMonitors in createMonitor
44 //
45 extern(Windows) private BOOL monitorCallback(HMONITOR handle, HDC dc, RECT* rect, LPARAM data) {
46     MONITORINFOEXW mi;
47     memset(&mi, 0, typeof(mi).sizeof);
48     mi.cbSize = typeof(mi).sizeof;
49 
50     if (GetMonitorInfoW(handle, cast(MONITORINFO*) &mi))
51     {
52         _GLFWmonitor* monitor = cast(_GLFWmonitor*) data;
53         if (wcscmp(mi.szDevice.ptr, monitor.win32.adapterName.ptr) == 0)
54             monitor.win32.handle = handle;
55     }
56 
57     return TRUE;
58 }
59 
60 // Create monitor from an adapter and (optionally) a display
61 //
62 private extern(D) _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) {
63     _GLFWmonitor* monitor;
64     int widthMM;int heightMM;
65     char* name;
66     HDC dc;
67     DEVMODEW dm;
68     RECT rect;
69 
70     if (display)
71         name = _glfwCreateUTF8FromWideStringWin32(display.DeviceString.ptr);
72     else
73         name = _glfwCreateUTF8FromWideStringWin32(adapter.DeviceString.ptr);
74     if (!name)
75         return null;
76 
77     memset(&dm, 0, typeof(dm).sizeof);
78     dm.dmSize = typeof(dm).sizeof;
79     EnumDisplaySettingsW(adapter.DeviceName.ptr, ENUM_CURRENT_SETTINGS, &dm);
80 
81     dc = CreateDCW("DISPLAY"w.ptr, adapter.DeviceName.ptr, null, null);
82 
83     if (IsWindows8Point1OrGreater())
84     {
85         widthMM  = GetDeviceCaps(dc, HORZSIZE);
86         heightMM = GetDeviceCaps(dc, VERTSIZE);
87     }
88     else
89     {
90         widthMM  = cast(int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
91         heightMM = cast(int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
92     }
93 
94     DeleteDC(dc);
95 
96     monitor = _glfwAllocMonitor(name, widthMM, heightMM);
97     free(name);
98 
99     if (adapter.StateFlags & DISPLAY_DEVICE_MODESPRUNED)
100         monitor.win32.modesPruned = GLFW_TRUE;
101 
102     wcscpy(monitor.win32.adapterName.ptr, adapter.DeviceName.ptr);
103     WideCharToMultiByte(CP_UTF8, 0,
104                         adapter.DeviceName.ptr, -1,
105                         monitor.win32.publicAdapterName.ptr,
106                         typeof(monitor.win32.publicAdapterName).sizeof,
107                         null, null);
108 
109     if (display)
110     {
111         wcscpy(monitor.win32.displayName.ptr, display.DeviceName.ptr);
112         WideCharToMultiByte(CP_UTF8, 0,
113                             display.DeviceName.ptr, -1,
114                             monitor.win32.publicDisplayName.ptr,
115                             typeof(monitor.win32.publicDisplayName).sizeof,
116                             null, null);
117     }
118 
119     rect.left   = dm.dmPosition.x;
120     rect.top    = dm.dmPosition.y;
121     rect.right  = dm.dmPosition.x + dm.dmPelsWidth;
122     rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
123 
124     EnumDisplayMonitors(null, &rect, &monitorCallback, cast(LPARAM) monitor);
125     return monitor;
126 }
127 
128 
129 //////////////////////////////////////////////////////////////////////////
130 //////                       GLFW internal API                      //////
131 //////////////////////////////////////////////////////////////////////////
132 
133 // Poll for changes in the set of connected monitors
134 //
135 void _glfwPollMonitorsWin32() {
136     int i;int disconnectedCount;
137     _GLFWmonitor** disconnected = null;
138     DWORD adapterIndex;DWORD displayIndex;
139     DISPLAY_DEVICEW adapter;DISPLAY_DEVICEW display;
140     _GLFWmonitor* monitor;
141 
142     disconnectedCount = _glfw.monitorCount;
143     if (disconnectedCount)
144     {
145         disconnected = cast(_GLFWmonitor**) calloc(_glfw.monitorCount, (_GLFWmonitor*).sizeof);
146         memcpy(disconnected,
147                _glfw.monitors,
148                _glfw.monitorCount * (_GLFWmonitor*).sizeof);
149     }
150 
151     for (adapterIndex = 0;  ;  adapterIndex++)
152     {
153         int type = _GLFW_INSERT_LAST;
154 
155         memset(&adapter, 0, typeof(adapter).sizeof);
156         adapter.cb = typeof(adapter).sizeof;
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             memset(&display, 0, typeof(display).sizeof);
170             display.cb = typeof(display).sizeof;
171 
172             if (!EnumDisplayDevicesW(adapter.DeviceName.ptr, 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.ptr,
182                            display.DeviceName.ptr) == 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.ptr,
212                            adapter.DeviceName.ptr) == 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 //
244 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const(GLFWvidmode)* desired) {
245     GLFWvidmode current;
246     const(GLFWvidmode)* best;
247     DEVMODEW dm;
248     LONG result;
249 
250     best = _glfwChooseVideoMode(monitor, desired);
251     _glfwPlatformGetVideoMode(monitor, &current);
252     if (_glfwCompareVideoModes(&current, best) == 0)
253         return;
254 
255     memset(&dm, 0, typeof(dm).sizeof);
256     dm.dmSize = typeof(dm).sizeof;
257     dm.dmFields           = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
258                             DM_DISPLAYFREQUENCY;
259     dm.dmPelsWidth        = best.width;
260     dm.dmPelsHeight       = best.height;
261     dm.dmBitsPerPel       = best.redBits + best.greenBits + best.blueBits;
262     dm.dmDisplayFrequency = best.refreshRate;
263 
264     if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
265         dm.dmBitsPerPel = 32;
266 
267     result = ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
268                                       &dm,
269                                       null,
270                                       CDS_FULLSCREEN,
271                                       null);
272     if (result == DISP_CHANGE_SUCCESSFUL)
273         monitor.win32.modeChanged = GLFW_TRUE;
274     else
275     {
276         const(char)* description = "Unknown error";
277 
278         if (result == DISP_CHANGE_BADDUALVIEW)
279             description = "The system uses DualView";
280         else if (result == DISP_CHANGE_BADFLAGS)
281             description = "Invalid flags";
282         else if (result == DISP_CHANGE_BADMODE)
283             description = "Graphics mode not supported";
284         else if (result == DISP_CHANGE_BADPARAM)
285             description = "Invalid parameter";
286         else if (result == DISP_CHANGE_FAILED)
287             description = "Graphics mode failed";
288         else if (result == DISP_CHANGE_NOTUPDATED)
289             description = "Failed to write to registry";
290         else if (result == DISP_CHANGE_RESTART)
291             description = "Computer restart required";
292 
293         _glfwInputError(GLFW_PLATFORM_ERROR,
294                         "Win32: Failed to set video mode: %s",
295                         description);
296     }
297 }
298 
299 // Restore the previously saved (original) video mode
300 //
301 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) {
302     if (monitor.win32.modeChanged)
303     {
304         ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
305                                  null, null, CDS_FULLSCREEN, null);
306         monitor.win32.modeChanged = GLFW_FALSE;
307     }
308 }
309 
310 void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) {
311     UINT xdpi;UINT ydpi;
312 
313     if (IsWindows8Point1OrGreater())
314         mixin(GetDpiForMonitor)(handle, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
315     else
316     {
317         HDC dc = GetDC(null);
318         xdpi = GetDeviceCaps(dc, LOGPIXELSX);
319         ydpi = GetDeviceCaps(dc, LOGPIXELSY);
320         ReleaseDC(null, dc);
321     }
322 
323     if (xscale)
324         *xscale = xdpi / cast(float) USER_DEFAULT_SCREEN_DPI;
325     if (yscale)
326         *yscale = ydpi / cast(float) USER_DEFAULT_SCREEN_DPI;
327 }
328 
329 
330 //////////////////////////////////////////////////////////////////////////
331 //////                       GLFW platform API                      //////
332 //////////////////////////////////////////////////////////////////////////
333 
334 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) {
335 }
336 
337 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {
338     DEVMODEW dm;
339     memset(&dm, 0, typeof(dm).sizeof);
340     dm.dmSize = typeof(dm).sizeof;
341 
342     EnumDisplaySettingsExW(monitor.win32.adapterName.ptr,
343                            ENUM_CURRENT_SETTINGS,
344                            &dm,
345                            EDS_ROTATEDMODE);
346 
347     if (xpos)
348         *xpos = dm.dmPosition.x;
349     if (ypos)
350         *ypos = dm.dmPosition.y;
351 }
352 
353 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) {
354     _glfwGetMonitorContentScaleWin32(monitor.win32.handle, xscale, yscale);
355 }
356 
357 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) {
358     MONITORINFO mi = MONITORINFO(MONITORINFO.sizeof);
359     GetMonitorInfo(monitor.win32.handle, &mi);
360 
361     if (xpos)
362         *xpos = mi.rcWork.left;
363     if (ypos)
364         *ypos = mi.rcWork.top;
365     if (width)
366         *width = mi.rcWork.right - mi.rcWork.left;
367     if (height)
368         *height = mi.rcWork.bottom - mi.rcWork.top;
369 }
370 
371 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) {
372     int modeIndex = 0;int size = 0;
373     GLFWvidmode* result = null;
374 
375     *count = 0;
376 
377     for (;;)
378     {
379         int i;
380         GLFWvidmode mode;
381         DEVMODEW dm;
382 
383         memset(&dm, 0, typeof(dm).sizeof);
384         dm.dmSize = typeof(dm).sizeof;
385 
386         if (!EnumDisplaySettingsW(monitor.win32.adapterName.ptr, modeIndex, &dm))
387             break;
388 
389         modeIndex++;
390 
391         // Skip modes with less than 15 BPP
392         if (dm.dmBitsPerPel < 15)
393             continue;
394 
395         mode.width  = dm.dmPelsWidth;
396         mode.height = dm.dmPelsHeight;
397         mode.refreshRate = dm.dmDisplayFrequency;
398         _glfwSplitBPP(dm.dmBitsPerPel,
399                       &mode.redBits,
400                       &mode.greenBits,
401                       &mode.blueBits);
402 
403         for (i = 0;  i < *count;  i++)
404         {
405             if (_glfwCompareVideoModes(result + i, &mode) == 0)
406                 break;
407         }
408 
409         // Skip duplicate modes
410         if (i < *count)
411             continue;
412 
413         if (monitor.win32.modesPruned)
414         {
415             // Skip modes not supported by the connected displays
416             if (ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
417                                          &dm,
418                                          null,
419                                          CDS_TEST,
420                                          null) != DISP_CHANGE_SUCCESSFUL)
421             {
422                 continue;
423             }
424         }
425 
426         if (*count == size)
427         {
428             size += 128;
429             result = cast(GLFWvidmode*) realloc(result, size * GLFWvidmode.sizeof);
430         }
431 
432         (*count)++;
433         result[*count - 1] = mode;
434     }
435 
436     if (!*count)
437     {
438         // HACK: Report the current mode if no valid modes were found
439         result = cast(GLFWvidmode*) calloc(1, GLFWvidmode.sizeof);
440         _glfwPlatformGetVideoMode(monitor, result);
441         *count = 1;
442     }
443 
444     return result;
445 }
446 
447 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {
448     DEVMODEW dm;
449     memset(&dm, 0, typeof(dm).sizeof);
450     dm.dmSize = typeof(dm).sizeof;
451 
452     EnumDisplaySettingsW(monitor.win32.adapterName.ptr, ENUM_CURRENT_SETTINGS, &dm);
453 
454     mode.width  = dm.dmPelsWidth;
455     mode.height = dm.dmPelsHeight;
456     mode.refreshRate = dm.dmDisplayFrequency;
457     _glfwSplitBPP(dm.dmBitsPerPel,
458                   &mode.redBits,
459                   &mode.greenBits,
460                   &mode.blueBits);
461 }
462 
463 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {
464     HDC dc;
465     WORD[256][3] values;
466 
467     dc = CreateDCW("DISPLAY"w.ptr, monitor.win32.adapterName.ptr, null, null);
468     GetDeviceGammaRamp(dc, values.ptr);
469     DeleteDC(dc);
470 
471     _glfwAllocGammaArrays(ramp, 256);
472 
473     memcpy(ramp.red,   values[0].ptr, typeof(values[0]).sizeof);
474     memcpy(ramp.green, values[1].ptr, typeof(values[1]).sizeof);
475     memcpy(ramp.blue,  values[2].ptr, typeof(values[2]).sizeof);
476 
477     return GLFW_TRUE;
478 }
479 
480 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const(GLFWgammaramp)* ramp) {
481     HDC dc;
482     WORD[256][3] values;
483 
484     if (ramp.size != 256)
485     {
486         _glfwInputError(GLFW_PLATFORM_ERROR,
487                         "Win32: Gamma ramp size must be 256");
488         return;
489     }
490 
491     memcpy(values[0].ptr, ramp.red,   typeof(values[0]).sizeof);
492     memcpy(values[1].ptr, ramp.green, typeof(values[1]).sizeof);
493     memcpy(values[2].ptr, ramp.blue,  typeof(values[2]).sizeof);
494 
495     dc = CreateDCW("DISPLAY"w.ptr, monitor.win32.adapterName.ptr, null, null);
496     SetDeviceGammaRamp(dc, values.ptr);
497     DeleteDC(dc);
498 }
499 
500 
501 //////////////////////////////////////////////////////////////////////////
502 //////                        GLFW native API                       //////
503 //////////////////////////////////////////////////////////////////////////
504 
505 const(char)* glfwGetWin32Adapter(GLFWmonitor* handle) {
506     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
507     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
508     return monitor.win32.publicAdapterName.ptr;
509 }
510 
511 const(char)* glfwGetWin32Monitor(GLFWmonitor* handle) {
512     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
513     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
514     return monitor.win32.publicDisplayName.ptr;
515 }