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 public 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 // Callback for EnumDisplayMonitors in createMonitor
42 //
43 extern(Windows) static BOOL monitorCallback(HMONITOR handle, HDC dc, RECT* rect, LPARAM data) {
44     MONITORINFOEXW mi;
45     memset(&mi, 0, typeof(mi).sizeof);
46     mi.cbSize = typeof(mi).sizeof;
47 
48     if (GetMonitorInfoW(handle, cast(MONITORINFO*) &mi))
49     {
50         _GLFWmonitor* monitor = cast(_GLFWmonitor*) data;
51         if (wcscmp(mi.szDevice.ptr, monitor.win32.adapterName.ptr) == 0)
52             monitor.win32.handle = handle;
53     }
54 
55     return TRUE;
56 }
57 
58 // Create monitor from an adapter and (optionally) a display
59 //
60 static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) {
61     _GLFWmonitor* monitor;
62     int widthMM;int heightMM;
63     char* name;
64     HDC dc;
65     DEVMODEW dm;
66     RECT rect;
67 
68     if (display)
69         name = _glfwCreateUTF8FromWideStringWin32(display.DeviceString.ptr);
70     else
71         name = _glfwCreateUTF8FromWideStringWin32(adapter.DeviceString.ptr);
72     if (!name)
73         return null;
74 
75     memset(&dm, 0, typeof(dm).sizeof);
76     dm.dmSize = typeof(dm).sizeof;
77     EnumDisplaySettingsW(adapter.DeviceName.ptr, ENUM_CURRENT_SETTINGS, &dm);
78 
79     dc = CreateDCW("DISPLAY"w.ptr, adapter.DeviceName.ptr, null, null);
80 
81     if (IsWindows8Point1OrGreater())
82     {
83         widthMM  = GetDeviceCaps(dc, HORZSIZE);
84         heightMM = GetDeviceCaps(dc, VERTSIZE);
85     }
86     else
87     {
88         widthMM  = cast(int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX));
89         heightMM = cast(int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY));
90     }
91 
92     DeleteDC(dc);
93 
94     monitor = _glfwAllocMonitor(name, widthMM, heightMM);
95     free(name);
96 
97     if (adapter.StateFlags & DISPLAY_DEVICE_MODESPRUNED)
98         monitor.win32.modesPruned = GLFW_TRUE;
99 
100     wcscpy(monitor.win32.adapterName.ptr, adapter.DeviceName.ptr);
101     WideCharToMultiByte(CP_UTF8, 0,
102                         adapter.DeviceName.ptr, -1,
103                         monitor.win32.publicAdapterName.ptr,
104                         typeof(monitor.win32.publicAdapterName).sizeof,
105                         null, null);
106 
107     if (display)
108     {
109         wcscpy(monitor.win32.displayName.ptr, display.DeviceName.ptr);
110         WideCharToMultiByte(CP_UTF8, 0,
111                             display.DeviceName.ptr, -1,
112                             monitor.win32.publicDisplayName.ptr,
113                             typeof(monitor.win32.publicDisplayName).sizeof,
114                             null, null);
115     }
116 
117     rect.left   = dm.dmPosition.x;
118     rect.top    = dm.dmPosition.y;
119     rect.right  = dm.dmPosition.x + dm.dmPelsWidth;
120     rect.bottom = dm.dmPosition.y + dm.dmPelsHeight;
121 
122     EnumDisplayMonitors(null, &rect, &monitorCallback, cast(LPARAM) monitor);
123     return monitor;
124 }
125 
126 
127 //////////////////////////////////////////////////////////////////////////
128 //////                       GLFW internal API                      //////
129 //////////////////////////////////////////////////////////////////////////
130 
131 // Poll for changes in the set of connected monitors
132 //
133 void _glfwPollMonitorsWin32() {
134     int i;int disconnectedCount;
135     _GLFWmonitor** disconnected = null;
136     DWORD adapterIndex;DWORD displayIndex;
137     DISPLAY_DEVICEW adapter;DISPLAY_DEVICEW display;
138     _GLFWmonitor* monitor;
139 
140     disconnectedCount = _glfw.monitorCount;
141     if (disconnectedCount)
142     {
143         disconnected = cast(_GLFWmonitor**) calloc(_glfw.monitorCount, (_GLFWmonitor*).sizeof);
144         memcpy(disconnected,
145                _glfw.monitors,
146                _glfw.monitorCount * (_GLFWmonitor*).sizeof);
147     }
148 
149     for (adapterIndex = 0;  ;  adapterIndex++)
150     {
151         int type = _GLFW_INSERT_LAST;
152 
153         memset(&adapter, 0, typeof(adapter).sizeof);
154         adapter.cb = typeof(adapter).sizeof;
155 
156         if (!EnumDisplayDevicesW(null, adapterIndex, &adapter, 0))
157             break;
158 
159         if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
160             continue;
161 
162         if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
163             type = _GLFW_INSERT_FIRST;
164 
165         for (displayIndex = 0;  ;  displayIndex++)
166         {
167             memset(&display, 0, typeof(display).sizeof);
168             display.cb = typeof(display).sizeof;
169 
170             if (!EnumDisplayDevicesW(adapter.DeviceName.ptr, displayIndex, &display, 0))
171                 break;
172 
173             if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE))
174                 continue;
175 
176             for (i = 0;  i < disconnectedCount;  i++)
177             {
178                 if (disconnected[i] &&
179                     wcscmp(disconnected[i].win32.displayName.ptr,
180                            display.DeviceName.ptr) == 0)
181                 {
182                     disconnected[i] = null;
183                     break;
184                 }
185             }
186 
187             if (i < disconnectedCount)
188                 continue;
189 
190             monitor = createMonitor(&adapter, &display);
191             if (!monitor)
192             {
193                 free(disconnected);
194                 return;
195             }
196 
197             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
198 
199             type = _GLFW_INSERT_LAST;
200         }
201 
202         // HACK: If an active adapter does not have any display devices
203         //       (as sometimes happens), add it directly as a monitor
204         if (displayIndex == 0)
205         {
206             for (i = 0;  i < disconnectedCount;  i++)
207             {
208                 if (disconnected[i] &&
209                     wcscmp(disconnected[i].win32.adapterName.ptr,
210                            adapter.DeviceName.ptr) == 0)
211                 {
212                     disconnected[i] = null;
213                     break;
214                 }
215             }
216 
217             if (i < disconnectedCount)
218                 continue;
219 
220             monitor = createMonitor(&adapter, null);
221             if (!monitor)
222             {
223                 free(disconnected);
224                 return;
225             }
226 
227             _glfwInputMonitor(monitor, GLFW_CONNECTED, type);
228         }
229     }
230 
231     for (i = 0;  i < disconnectedCount;  i++)
232     {
233         if (disconnected[i])
234             _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
235     }
236 
237     free(disconnected);
238 }
239 
240 // Change the current video mode
241 //
242 void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const(GLFWvidmode)* desired) {
243     GLFWvidmode current;
244     const(GLFWvidmode)* best;
245     DEVMODEW dm;
246     LONG result;
247 
248     best = _glfwChooseVideoMode(monitor, desired);
249     _glfwPlatformGetVideoMode(monitor, &current);
250     if (_glfwCompareVideoModes(&current, best) == 0)
251         return;
252 
253     memset(&dm, 0, typeof(dm).sizeof);
254     dm.dmSize = typeof(dm).sizeof;
255     dm.dmFields           = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
256                             DM_DISPLAYFREQUENCY;
257     dm.dmPelsWidth        = best.width;
258     dm.dmPelsHeight       = best.height;
259     dm.dmBitsPerPel       = best.redBits + best.greenBits + best.blueBits;
260     dm.dmDisplayFrequency = best.refreshRate;
261 
262     if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24)
263         dm.dmBitsPerPel = 32;
264 
265     result = ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
266                                       &dm,
267                                       null,
268                                       CDS_FULLSCREEN,
269                                       null);
270     if (result == DISP_CHANGE_SUCCESSFUL)
271         monitor.win32.modeChanged = GLFW_TRUE;
272     else
273     {
274         const(char)* description = "Unknown error";
275 
276         if (result == DISP_CHANGE_BADDUALVIEW)
277             description = "The system uses DualView";
278         else if (result == DISP_CHANGE_BADFLAGS)
279             description = "Invalid flags";
280         else if (result == DISP_CHANGE_BADMODE)
281             description = "Graphics mode not supported";
282         else if (result == DISP_CHANGE_BADPARAM)
283             description = "Invalid parameter";
284         else if (result == DISP_CHANGE_FAILED)
285             description = "Graphics mode failed";
286         else if (result == DISP_CHANGE_NOTUPDATED)
287             description = "Failed to write to registry";
288         else if (result == DISP_CHANGE_RESTART)
289             description = "Computer restart required";
290 
291         _glfwInputError(GLFW_PLATFORM_ERROR,
292                         "Win32: Failed to set video mode: %s",
293                         description);
294     }
295 }
296 
297 // Restore the previously saved (original) video mode
298 //
299 void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) {
300     if (monitor.win32.modeChanged)
301     {
302         ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
303                                  null, null, CDS_FULLSCREEN, null);
304         monitor.win32.modeChanged = GLFW_FALSE;
305     }
306 }
307 
308 void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) {
309     UINT xdpi;UINT ydpi;
310 
311     if (IsWindows8Point1OrGreater())
312         mixin(GetDpiForMonitor)(handle, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, &xdpi, &ydpi);
313     else
314     {
315         HDC dc = GetDC(null);
316         xdpi = GetDeviceCaps(dc, LOGPIXELSX);
317         ydpi = GetDeviceCaps(dc, LOGPIXELSY);
318         ReleaseDC(null, dc);
319     }
320 
321     if (xscale)
322         *xscale = xdpi / cast(float) USER_DEFAULT_SCREEN_DPI;
323     if (yscale)
324         *yscale = ydpi / cast(float) USER_DEFAULT_SCREEN_DPI;
325 }
326 
327 
328 //////////////////////////////////////////////////////////////////////////
329 //////                       GLFW platform API                      //////
330 //////////////////////////////////////////////////////////////////////////
331 
332 void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) {
333 }
334 
335 void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) {
336     DEVMODEW dm;
337     memset(&dm, 0, typeof(dm).sizeof);
338     dm.dmSize = typeof(dm).sizeof;
339 
340     EnumDisplaySettingsExW(monitor.win32.adapterName.ptr,
341                            ENUM_CURRENT_SETTINGS,
342                            &dm,
343                            EDS_ROTATEDMODE);
344 
345     if (xpos)
346         *xpos = dm.dmPosition.x;
347     if (ypos)
348         *ypos = dm.dmPosition.y;
349 }
350 
351 void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) {
352     _glfwGetMonitorContentScaleWin32(monitor.win32.handle, xscale, yscale);
353 }
354 
355 void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) {
356     MONITORINFO mi = MONITORINFO(MONITORINFO.sizeof);
357     GetMonitorInfo(monitor.win32.handle, &mi);
358 
359     if (xpos)
360         *xpos = mi.rcWork.left;
361     if (ypos)
362         *ypos = mi.rcWork.top;
363     if (width)
364         *width = mi.rcWork.right - mi.rcWork.left;
365     if (height)
366         *height = mi.rcWork.bottom - mi.rcWork.top;
367 }
368 
369 GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) {
370     int modeIndex = 0;int size = 0;
371     GLFWvidmode* result = null;
372 
373     *count = 0;
374 
375     for (;;)
376     {
377         int i;
378         GLFWvidmode mode;
379         DEVMODEW dm;
380 
381         memset(&dm, 0, typeof(dm).sizeof);
382         dm.dmSize = typeof(dm).sizeof;
383 
384         if (!EnumDisplaySettingsW(monitor.win32.adapterName.ptr, modeIndex, &dm))
385             break;
386 
387         modeIndex++;
388 
389         // Skip modes with less than 15 BPP
390         if (dm.dmBitsPerPel < 15)
391             continue;
392 
393         mode.width  = dm.dmPelsWidth;
394         mode.height = dm.dmPelsHeight;
395         mode.refreshRate = dm.dmDisplayFrequency;
396         _glfwSplitBPP(dm.dmBitsPerPel,
397                       &mode.redBits,
398                       &mode.greenBits,
399                       &mode.blueBits);
400 
401         for (i = 0;  i < *count;  i++)
402         {
403             if (_glfwCompareVideoModes(result + i, &mode) == 0)
404                 break;
405         }
406 
407         // Skip duplicate modes
408         if (i < *count)
409             continue;
410 
411         if (monitor.win32.modesPruned)
412         {
413             // Skip modes not supported by the connected displays
414             if (ChangeDisplaySettingsExW(monitor.win32.adapterName.ptr,
415                                          &dm,
416                                          null,
417                                          CDS_TEST,
418                                          null) != DISP_CHANGE_SUCCESSFUL)
419             {
420                 continue;
421             }
422         }
423 
424         if (*count == size)
425         {
426             size += 128;
427             result = cast(GLFWvidmode*) realloc(result, size * GLFWvidmode.sizeof);
428         }
429 
430         (*count)++;
431         result[*count - 1] = mode;
432     }
433 
434     if (!*count)
435     {
436         // HACK: Report the current mode if no valid modes were found
437         result = cast(GLFWvidmode*) calloc(1, GLFWvidmode.sizeof);
438         _glfwPlatformGetVideoMode(monitor, result);
439         *count = 1;
440     }
441 
442     return result;
443 }
444 
445 void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {
446     DEVMODEW dm;
447     memset(&dm, 0, typeof(dm).sizeof);
448     dm.dmSize = typeof(dm).sizeof;
449 
450     EnumDisplaySettingsW(monitor.win32.adapterName.ptr, ENUM_CURRENT_SETTINGS, &dm);
451 
452     mode.width  = dm.dmPelsWidth;
453     mode.height = dm.dmPelsHeight;
454     mode.refreshRate = dm.dmDisplayFrequency;
455     _glfwSplitBPP(dm.dmBitsPerPel,
456                   &mode.redBits,
457                   &mode.greenBits,
458                   &mode.blueBits);
459 }
460 
461 GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) {
462     HDC dc;
463     WORD[256][3] values;
464 
465     dc = CreateDCW("DISPLAY"w.ptr, monitor.win32.adapterName.ptr, null, null);
466     GetDeviceGammaRamp(dc, values.ptr);
467     DeleteDC(dc);
468 
469     _glfwAllocGammaArrays(ramp, 256);
470 
471     memcpy(ramp.red,   values[0].ptr, typeof(values[0]).sizeof);
472     memcpy(ramp.green, values[1].ptr, typeof(values[1]).sizeof);
473     memcpy(ramp.blue,  values[2].ptr, typeof(values[2]).sizeof);
474 
475     return GLFW_TRUE;
476 }
477 
478 void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const(GLFWgammaramp)* ramp) {
479     HDC dc;
480     WORD[256][3] values;
481 
482     if (ramp.size != 256)
483     {
484         _glfwInputError(GLFW_PLATFORM_ERROR,
485                         "Win32: Gamma ramp size must be 256");
486         return;
487     }
488 
489     memcpy(values[0].ptr, ramp.red,   typeof(values[0]).sizeof);
490     memcpy(values[1].ptr, ramp.green, typeof(values[1]).sizeof);
491     memcpy(values[2].ptr, ramp.blue,  typeof(values[2]).sizeof);
492 
493     dc = CreateDCW("DISPLAY"w.ptr, monitor.win32.adapterName.ptr, null, null);
494     SetDeviceGammaRamp(dc, values.ptr);
495     DeleteDC(dc);
496 }
497 
498 
499 //////////////////////////////////////////////////////////////////////////
500 //////                        GLFW native API                       //////
501 //////////////////////////////////////////////////////////////////////////
502 
503 const(char)* glfwGetWin32Adapter(GLFWmonitor* handle) {
504     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
505     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
506     return monitor.win32.publicAdapterName.ptr;
507 }
508 
509 const(char)* glfwGetWin32Monitor(GLFWmonitor* handle) {
510     _GLFWmonitor* monitor = cast(_GLFWmonitor*) handle;
511     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
512     return monitor.win32.publicDisplayName.ptr;
513 }