1 /// Translated from C to D
2 module glfw3.win32_window;
3 
4 extern(C): __gshared:
5 // @nogc: nothrow:
6 
7 //========================================================================
8 // GLFW 3.3 Win32 - www.glfw.org
9 //------------------------------------------------------------------------
10 // Copyright (c) 2002-2006 Marcus Geelnard
11 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
12 //
13 // This software is provided 'as-is', without any express or implied
14 // warranty. In no event will the authors be held liable for any damages
15 // arising from the use of this software.
16 //
17 // Permission is granted to anyone to use this software for any purpose,
18 // including commercial applications, and to alter it and redistribute it
19 // freely, subject to the following restrictions:
20 //
21 // 1. The origin of this software must not be misrepresented; you must not
22 //    claim that you wrote the original software. If you use this software
23 //    in a product, an acknowledgment in the product documentation would
24 //    be appreciated but is not required.
25 //
26 // 2. Altered source versions must be plainly marked as such, and must not
27 //    be misrepresented as being the original software.
28 //
29 // 3. This notice may not be removed or altered from any source
30 //    distribution.
31 //
32 //========================================================================
33 // Please use C89 style variable declarations in this file because VS 2010
34 //========================================================================
35 
36 public import glfw3.internal;
37 
38 public import glfw3.win32_platform: IsWindowsVistaOrGreater, IsWindowsXPOrGreater, IsWindows7OrGreater;
39 
40 import core.stdc.limits;
41 import core.stdc.stdlib;
42 import core.stdc.string;
43 import core.sys.windows.windows;
44 //public import windowsx;
45 //public import shellapi;
46 
47 // Returns the window style for the specified window
48 //
49 static DWORD getWindowStyle(const(_GLFWwindow)* window) {
50     DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
51 
52     if (window.monitor)
53         style |= WS_POPUP;
54     else
55     {
56         style |= WS_SYSMENU | WS_MINIMIZEBOX;
57 
58         if (window.decorated)
59         {
60             style |= WS_CAPTION;
61 
62             if (window.resizable)
63                 style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
64         }
65         else
66             style |= WS_POPUP;
67     }
68 
69     return style;
70 }
71 
72 // Returns the extended window style for the specified window
73 //
74 static DWORD getWindowExStyle(const(_GLFWwindow)* window) {
75     DWORD style = WS_EX_APPWINDOW;
76 
77     if (window.monitor || window.floating)
78         style |= WS_EX_TOPMOST;
79 
80     return style;
81 }
82 
83 // Returns the image whose area most closely matches the desired one
84 //
85 static const(GLFWimage)* chooseImage(int count, const(GLFWimage)* images, int width, int height) {
86     int i;int leastDiff = INT_MAX;
87     const(GLFWimage)* closest = null;
88 
89     for (i = 0;  i < count;  i++)
90     {
91         const(int) currDiff = abs(images[i].width * images[i].height -
92                                  width * height);
93         if (currDiff < leastDiff)
94         {
95             closest = images + i;
96             leastDiff = currDiff;
97         }
98     }
99 
100     return closest;
101 }
102 
103 // Creates an RGBA icon or cursor
104 //
105 static HICON createIcon(const(GLFWimage)* image, int xhot, int yhot, GLFWbool icon) {
106     int i;
107     HDC dc;
108     HICON handle;
109     HBITMAP color;HBITMAP mask;
110     BITMAPV5HEADER bi;
111     ICONINFO ii;
112     ubyte* target = null;
113     const(ubyte)* source = image.pixels;
114 
115     memset(&bi, 0, typeof(bi).sizeof);
116     bi.bV5Size        = typeof(bi).sizeof;
117     bi.bV5Width       = image.width;
118     bi.bV5Height      = -image.height;
119     bi.bV5Planes      = 1;
120     bi.bV5BitCount    = 32;
121     bi.bV5Compression = BI_BITFIELDS;
122     bi.bV5RedMask     = 0x00ff0000;
123     bi.bV5GreenMask   = 0x0000ff00;
124     bi.bV5BlueMask    = 0x000000ff;
125     bi.bV5AlphaMask   = 0xff000000;
126 
127     dc = GetDC(null);
128     color = CreateDIBSection(dc,
129                              cast(BITMAPINFO*) &bi,
130                              DIB_RGB_COLORS,
131                              cast(void**) &target,
132                              null,
133                              cast(DWORD) 0);
134     ReleaseDC(null, dc);
135 
136     if (!color)
137     {
138         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
139                              "Win32: Failed to create RGBA bitmap");
140         return null;
141     }
142 
143     mask = CreateBitmap(image.width, image.height, 1, 1, null);
144     if (!mask)
145     {
146         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
147                              "Win32: Failed to create mask bitmap");
148         DeleteObject(color);
149         return null;
150     }
151 
152     for (i = 0;  i < image.width * image.height;  i++)
153     {
154         target[0] = source[2];
155         target[1] = source[1];
156         target[2] = source[0];
157         target[3] = source[3];
158         target += 4;
159         source += 4;
160     }
161 
162     memset(&ii, 0, typeof(ii).sizeof);
163     ii.fIcon    = icon;
164     ii.xHotspot = xhot;
165     ii.yHotspot = yhot;
166     ii.hbmMask  = mask;
167     ii.hbmColor = color;
168 
169     handle = CreateIconIndirect(&ii);
170 
171     DeleteObject(color);
172     DeleteObject(mask);
173 
174     if (!handle)
175     {
176         if (icon)
177         {
178             _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
179                                  "Win32: Failed to create icon");
180         }
181         else
182         {
183             _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
184                                  "Win32: Failed to create cursor");
185         }
186     }
187 
188     return handle;
189 }
190 
191 // Translate content area size to full window size according to styles and DPI
192 //
193 static void getFullWindowSize(DWORD style, DWORD exStyle, int contentWidth, int contentHeight, int* fullWidth, int* fullHeight, UINT dpi) {
194     RECT rect = RECT(0, 0, contentWidth, contentHeight);
195 
196     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
197         mixin(AdjustWindowRectExForDpi)(&rect, style, FALSE, exStyle, dpi);
198     else
199         /*DynCall*/AdjustWindowRectEx(&rect, style, FALSE, exStyle);
200 
201     *fullWidth = rect.right - rect.left;
202     *fullHeight = rect.bottom - rect.top;
203 }
204 // Enforce the content area aspect ratio based on which edge is being dragged
205 //
206 static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) {
207     int xoff;int yoff;
208     UINT dpi = USER_DEFAULT_SCREEN_DPI;
209     const(float) ratio = cast(float) window.numer / cast(float) window.denom;
210 
211     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
212         dpi = mixin(GetDpiForWindow)(window.win32.handle);
213 
214     getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
215                       0, 0, &xoff, &yoff, dpi);
216 
217     if (edge == WMSZ_LEFT  || edge == WMSZ_BOTTOMLEFT ||
218         edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
219     {
220         area.bottom = area.top + yoff +
221             cast(int) ((area.right - area.left - xoff) / ratio);
222     }
223     else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
224     {
225         area.top = area.bottom - yoff -
226             cast(int) ((area.right - area.left - xoff) / ratio);
227     }
228     else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
229     {
230         area.right = area.left + xoff +
231             cast(int) ((area.bottom - area.top - yoff) * ratio);
232     }
233 }
234 
235 // Updates the cursor image according to its cursor mode
236 //
237 static void updateCursorImage(_GLFWwindow* window) {
238     if (window.cursorMode == GLFW_CURSOR_NORMAL)
239     {
240         if (window.cursor)
241             SetCursor(window.cursor.win32.handle);
242         else
243             SetCursor(LoadCursorW(null, IDC_ARROW));
244     }
245     else
246         SetCursor(null);
247 }
248 
249 // Updates the cursor clip rect
250 //
251 static void updateClipRect(_GLFWwindow* window) {
252     if (window)
253     {
254         RECT clipRect;
255         GetClientRect(cast(void*) window.win32.handle, &clipRect);
256         ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &clipRect.left);
257         ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &clipRect.right);
258         ClipCursor(&clipRect);
259     }
260     else
261         ClipCursor(null);
262 }
263 
264 // Enables WM_INPUT messages for the mouse for the specified window
265 //
266 static void enableRawMouseMotion(_GLFWwindow* window) {
267     const(RAWINPUTDEVICE) rid = RAWINPUTDEVICE( 0x01, 0x02, 0, cast(void*) window.win32.handle );
268 
269     if (!RegisterRawInputDevices(&rid, 1, typeof(rid).sizeof))
270     {
271         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
272                              "Win32: Failed to register raw input device");
273     }
274 }
275 
276 // Disables WM_INPUT messages for the mouse
277 //
278 static void disableRawMouseMotion(_GLFWwindow* window) {
279     const RAWINPUTDEVICE rid = RAWINPUTDEVICE(0x01, 0x02, RIDEV_REMOVE, null);
280 
281     if (!RegisterRawInputDevices(&rid, 1, typeof(rid).sizeof))
282     {
283         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
284                              "Win32: Failed to remove raw input device");
285     }
286 }
287 
288 // Apply disabled cursor mode to a focused window
289 //
290 static void disableCursor(_GLFWwindow* window) {
291     _glfw.win32.disabledCursorWindow = window;
292     _glfwPlatformGetCursorPos(window,
293                               &_glfw.win32.restoreCursorPosX,
294                               &_glfw.win32.restoreCursorPosY);
295     updateCursorImage(window);
296     _glfwCenterCursorInContentArea(window);
297     updateClipRect(window);
298 
299     if (window.rawMouseMotion)
300         enableRawMouseMotion(window);
301 }
302 
303 // Exit disabled cursor mode for the specified window
304 //
305 static void enableCursor(_GLFWwindow* window) {
306     if (window.rawMouseMotion)
307         disableRawMouseMotion(window);
308 
309     _glfw.win32.disabledCursorWindow = null;
310     updateClipRect(null);
311     _glfwPlatformSetCursorPos(window,
312                               _glfw.win32.restoreCursorPosX,
313                               _glfw.win32.restoreCursorPosY);
314     updateCursorImage(window);
315 }
316 
317 // Returns whether the cursor is in the content area of the specified window
318 //
319 static GLFWbool cursorInContentArea(_GLFWwindow* window) {
320     RECT area;
321     POINT pos;
322 
323     if (!GetCursorPos(&pos))
324         return GLFW_FALSE;
325 
326     if (WindowFromPoint(pos) != window.win32.handle)
327         return GLFW_FALSE;
328 
329     GetClientRect(cast(void*) window.win32.handle, &area);
330     ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &area.left);
331     ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &area.right);
332 
333     return PtInRect(&area, pos);
334 }
335 
336 // Update native window styles to match attributes
337 //
338 static void updateWindowStyles(const(_GLFWwindow)* window) {
339     RECT rect;
340     DWORD style = GetWindowLongW(cast(void*) window.win32.handle, GWL_STYLE);
341     style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP);
342     style |= getWindowStyle(window);
343 
344     GetClientRect(cast(void*) window.win32.handle, &rect);
345 
346     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
347     {
348         mixin(AdjustWindowRectExForDpi)(&rect, style, FALSE,
349                                  getWindowExStyle(window),
350                                  mixin(GetDpiForWindow)(cast(void*) window.win32.handle));
351     }
352     else
353         AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
354 
355     ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &rect.left);
356     ClientToScreen(cast(void*) window.win32.handle, cast(POINT*) &rect.right);
357     SetWindowLongW(cast(void*) window.win32.handle, GWL_STYLE, style);
358     SetWindowPos(cast(void*) window.win32.handle, HWND_TOP,
359                  rect.left, rect.top,
360                  rect.right - rect.left, rect.bottom - rect.top,
361                  SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
362 }
363 
364 // Update window framebuffer transparency
365 //
366 static void updateFramebufferTransparency(const(_GLFWwindow)* window) {
367     BOOL enabled;
368 
369     if (!IsWindowsVistaOrGreater())
370         return;
371 
372     if (SUCCEEDED(_glfw.win32.dwmapi.IsCompositionEnabled(&enabled)) && enabled)
373     {
374         HRGN region = CreateRectRgn(0, 0, -1, -1);
375         DWM_BLURBEHIND bb = DWM_BLURBEHIND(0);
376         bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
377         bb.hRgnBlur = region;
378         bb.fEnable = TRUE;
379 
380         if (SUCCEEDED(_glfw.win32.dwmapi.EnableBlurBehindWindow(cast(void*) window.win32.handle, &bb)))
381         {
382             // Decorated windows don't repaint the transparent background
383             // leaving a trail behind animations
384             // HACK: Making the window layered with a transparency color key
385             //       seems to fix this.  Normally, when specifying
386             //       a transparency color key to be used when composing the
387             //       layered window, all pixels painted by the window in this
388             //       color will be transparent.  That doesn't seem to be the
389             //       case anymore, at least when used with blur behind window
390             //       plus negative region.
391             LONG exStyle = GetWindowLongW(cast(void*) window.win32.handle, GWL_EXSTYLE);
392             exStyle |= WS_EX_LAYERED;
393             SetWindowLongW(cast(void*) window.win32.handle, GWL_EXSTYLE, exStyle);
394 
395             // Using a color key not equal to black to fix the trailing
396             // issue.  When set to black, something is making the hit test
397             // not resize with the window frame.
398             SetLayeredWindowAttributes(cast(void*) window.win32.handle,
399                                        RGB(255, 0, 255), 255, LWA_COLORKEY);
400         }
401 
402         DeleteObject(region);
403     }
404     else
405     {
406         LONG exStyle = GetWindowLongW(cast(void*) window.win32.handle, GWL_EXSTYLE);
407         exStyle &= ~WS_EX_LAYERED;
408         SetWindowLongW(cast(void*) window.win32.handle, GWL_EXSTYLE, exStyle);
409         RedrawWindow(cast(void*) window.win32.handle, null, null,
410                      RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
411     }
412 }
413 
414 // Retrieves and translates modifier keys
415 //
416 static int getKeyMods() {
417     int mods = 0;
418 
419     if (GetKeyState(VK_SHIFT) & 0x8000)
420         mods |= GLFW_MOD_SHIFT;
421     if (GetKeyState(VK_CONTROL) & 0x8000)
422         mods |= GLFW_MOD_CONTROL;
423     if (GetKeyState(VK_MENU) & 0x8000)
424         mods |= GLFW_MOD_ALT;
425     if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
426         mods |= GLFW_MOD_SUPER;
427     if (GetKeyState(VK_CAPITAL) & 1)
428         mods |= GLFW_MOD_CAPS_LOCK;
429     if (GetKeyState(VK_NUMLOCK) & 1)
430         mods |= GLFW_MOD_NUM_LOCK;
431 
432     return mods;
433 }
434 
435 static void fitToMonitor(_GLFWwindow* window) {
436     MONITORINFO mi = MONITORINFO(MONITORINFO.sizeof);
437     GetMonitorInfo(window.monitor.win32.handle, &mi);
438     SetWindowPos(cast(void*) window.win32.handle, HWND_TOPMOST,
439                  mi.rcMonitor.left,
440                  mi.rcMonitor.top,
441                  mi.rcMonitor.right - mi.rcMonitor.left,
442                  mi.rcMonitor.bottom - mi.rcMonitor.top,
443                  SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
444 }
445 
446 // Make the specified window and its video mode active on its monitor
447 //
448 static void acquireMonitor(_GLFWwindow* window) {
449     if (!_glfw.win32.acquiredMonitorCount)
450     {
451         SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
452 
453         // HACK: When mouse trails are enabled the cursor becomes invisible when
454         //       the OpenGL ICD switches to page flipping
455         if (IsWindowsXPOrGreater())
456         {
457             SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
458             SystemParametersInfo(SPI_SETMOUSETRAILS, 0, null, 0);
459         }
460     }
461 
462     if (!window.monitor.window)
463         _glfw.win32.acquiredMonitorCount++;
464 
465     _glfwSetVideoModeWin32(window.monitor, &window.videoMode);
466     _glfwInputMonitorWindow(window.monitor, window);
467 }
468 
469 // Remove the window and restore the original video mode
470 //
471 static void releaseMonitor(_GLFWwindow* window) {
472     if (window.monitor.window != window)
473         return;
474 
475     _glfw.win32.acquiredMonitorCount--;
476     if (!_glfw.win32.acquiredMonitorCount)
477     {
478         SetThreadExecutionState(ES_CONTINUOUS);
479 
480         // HACK: Restore mouse trail length saved in acquireMonitor
481         if (IsWindowsXPOrGreater())
482             SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, null, 0);
483     }
484 
485     _glfwInputMonitorWindow(window.monitor, null);
486     _glfwRestoreVideoModeWin32(window.monitor);
487 }
488 
489 // Window callback function (handles window messages)
490 //
491 static LRESULT windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
492     auto window = cast(_GLFWwindow*) GetPropW(hWnd, "GLFW"w.ptr);
493     if (!window)
494     {
495         // This is the message handling for the hidden helper window
496         // and for a regular window during its initial creation
497 
498         switch (uMsg)
499         {
500             case WM_NCCREATE:
501             {
502                 if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
503                     mixin(EnableNonClientDpiScaling)(hWnd);
504 
505                 break;
506             }
507 
508             case WM_DISPLAYCHANGE:
509                 _glfwPollMonitorsWin32();
510                 break;
511 
512             case WM_DEVICECHANGE:
513             {
514                 if (wParam == DBT_DEVICEARRIVAL)
515                 {
516                     DEV_BROADCAST_HDR* dbh = cast(DEV_BROADCAST_HDR*) lParam;
517                     if (dbh && dbh.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
518                         _glfwDetectJoystickConnectionWin32();
519                 }
520                 else if (wParam == DBT_DEVICEREMOVECOMPLETE)
521                 {
522                     DEV_BROADCAST_HDR* dbh = cast(DEV_BROADCAST_HDR*) lParam;
523                     if (dbh && dbh.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
524                         _glfwDetectJoystickDisconnectionWin32();
525                 }
526 
527                 break;
528             }
529             default: break;
530         }
531 
532         return DefWindowProcW(hWnd, uMsg, wParam, lParam);
533     }
534 
535     switch (uMsg)
536     {
537         case WM_MOUSEACTIVATE:
538         {
539             // HACK: Postpone cursor disabling when the window was activated by
540             //       clicking a caption button
541             if (HIWORD(lParam) == WM_LBUTTONDOWN)
542             {
543                 if (LOWORD(lParam) != HTCLIENT)
544                     window.win32.frameAction = GLFW_TRUE;
545             }
546 
547             break;
548         }
549 
550         case WM_CAPTURECHANGED:
551         {
552             // HACK: Disable the cursor once the caption button action has been
553             //       completed or cancelled
554             if (lParam == 0 && window.win32.frameAction)
555             {
556                 if (window.cursorMode == GLFW_CURSOR_DISABLED)
557                     disableCursor(window);
558 
559                 window.win32.frameAction = GLFW_FALSE;
560             }
561 
562             break;
563         }
564 
565         case WM_SETFOCUS:
566         {
567             _glfwInputWindowFocus(window, GLFW_TRUE);
568 
569             // HACK: Do not disable cursor while the user is interacting with
570             //       a caption button
571             if (window.win32.frameAction)
572                 break;
573 
574             if (window.cursorMode == GLFW_CURSOR_DISABLED)
575                 disableCursor(window);
576 
577             return 0;
578         }
579 
580         case WM_KILLFOCUS:
581         {
582             if (window.cursorMode == GLFW_CURSOR_DISABLED)
583                 enableCursor(window);
584 
585             if (window.monitor && window.autoIconify)
586                 _glfwPlatformIconifyWindow(window);
587 
588             _glfwInputWindowFocus(window, GLFW_FALSE);
589             return 0;
590         }
591 
592         case WM_SYSCOMMAND:
593         {
594             switch (wParam & 0xfff0)
595             {
596                 case SC_SCREENSAVE:
597                 case SC_MONITORPOWER:
598                 {
599                     if (window.monitor)
600                     {
601                         // We are running in full screen mode, so disallow
602                         // screen saver and screen blanking
603                         return 0;
604                     }
605                     else
606                         break;
607                 }
608 
609                 // User trying to access application menu using ALT?
610                 case SC_KEYMENU:
611                     return 0;
612                 default: break;
613             }
614             break;
615         }
616 
617         case WM_CLOSE:
618         {
619             _glfwInputWindowCloseRequest(window);
620             return 0;
621         }
622 
623         case WM_INPUTLANGCHANGE:
624         {
625             _glfwUpdateKeyNamesWin32();
626             break;
627         }
628 
629         case WM_CHAR:
630         case WM_SYSCHAR:
631         case WM_UNICHAR:
632         {
633             const(GLFWbool) plain = (uMsg != WM_SYSCHAR);
634 
635             if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR)
636             {
637                 // WM_UNICHAR is not sent by Windows, but is sent by some
638                 // third-party input method engine
639                 // Returning TRUE here announces support for this message
640                 return TRUE;
641             }
642 
643             _glfwInputChar(window, cast(uint) wParam, getKeyMods(), plain);
644             return 0;
645         }
646 
647         case WM_KEYDOWN:
648         case WM_SYSKEYDOWN:
649         case WM_KEYUP:
650         case WM_SYSKEYUP:
651         {
652             int key;int scancode;
653             const(int) action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
654             const(int) mods = getKeyMods();
655 
656             scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
657             if (!scancode)
658             {
659                 // NOTE: Some synthetic key messages have a scancode of zero
660                 // HACK: Map the virtual key back to a usable scancode
661                 scancode = MapVirtualKeyW(cast(UINT) wParam, MAPVK_VK_TO_VSC);
662             }
663 
664             key = _glfw.win32.keycodes[scancode];
665 
666             // The Ctrl keys require special handling
667             if (wParam == VK_CONTROL)
668             {
669                 if (HIWORD(lParam) & KF_EXTENDED)
670                 {
671                     // Right side keys have the extended key bit set
672                     key = GLFW_KEY_RIGHT_CONTROL;
673                 }
674                 else
675                 {
676                     // NOTE: Alt Gr sends Left Ctrl followed by Right Alt
677                     // HACK: We only want one event for Alt Gr, so if we detect
678                     //       this sequence we discard this Left Ctrl message now
679                     //       and later report Right Alt normally
680                     MSG next;
681                     const(DWORD) time = GetMessageTime();
682 
683                     if (PeekMessageW(&next, null, 0, 0, PM_NOREMOVE))
684                     {
685                         if (next.message == WM_KEYDOWN ||
686                             next.message == WM_SYSKEYDOWN ||
687                             next.message == WM_KEYUP ||
688                             next.message == WM_SYSKEYUP)
689                         {
690                             if (next.wParam == VK_MENU &&
691                                 (HIWORD(next.lParam) & KF_EXTENDED) &&
692                                 next.time == time)
693                             {
694                                 // Next message is Right Alt down so discard this
695                                 break;
696                             }
697                         }
698                     }
699 
700                     // This is a regular Left Ctrl message
701                     key = GLFW_KEY_LEFT_CONTROL;
702                 }
703             }
704             else if (wParam == VK_PROCESSKEY)
705             {
706                 // IME notifies that keys have been filtered by setting the
707                 // virtual key-code to VK_PROCESSKEY
708                 break;
709             }
710 
711             if (action == GLFW_RELEASE && wParam == VK_SHIFT)
712             {
713                 // HACK: Release both Shift keys on Shift up event, as when both
714                 //       are pressed the first release does not emit any event
715                 // NOTE: The other half of this is in _glfwPlatformPollEvents
716                 _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
717                 _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
718             }
719             else if (wParam == VK_SNAPSHOT)
720             {
721                 // HACK: Key down is not reported for the Print Screen key
722                 _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
723                 _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
724             }
725             else
726                 _glfwInputKey(window, key, scancode, action, mods);
727 
728             break;
729         }
730 
731         case WM_LBUTTONDOWN:
732         case WM_RBUTTONDOWN:
733         case WM_MBUTTONDOWN:
734         case WM_XBUTTONDOWN:
735         case WM_LBUTTONUP:
736         case WM_RBUTTONUP:
737         case WM_MBUTTONUP:
738         case WM_XBUTTONUP:
739         {
740             int i;int button;int action;
741 
742             if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
743                 button = GLFW_MOUSE_BUTTON_LEFT;
744             else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
745                 button = GLFW_MOUSE_BUTTON_RIGHT;
746             else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
747                 button = GLFW_MOUSE_BUTTON_MIDDLE;
748             else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
749                 button = GLFW_MOUSE_BUTTON_4;
750             else
751                 button = GLFW_MOUSE_BUTTON_5;
752 
753             if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
754                 uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
755             {
756                 action = GLFW_PRESS;
757             }
758             else
759                 action = GLFW_RELEASE;
760 
761             for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
762             {
763                 if (window.mouseButtons[i] == GLFW_PRESS)
764                     break;
765             }
766 
767             if (i > GLFW_MOUSE_BUTTON_LAST)
768                 SetCapture(hWnd);
769 
770             _glfwInputMouseClick(window, button, action, getKeyMods());
771 
772             for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
773             {
774                 if (window.mouseButtons[i] == GLFW_PRESS)
775                     break;
776             }
777 
778             if (i > GLFW_MOUSE_BUTTON_LAST)
779                 ReleaseCapture();
780 
781             if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
782                 return TRUE;
783 
784             return 0;
785         }
786 
787         case WM_MOUSEMOVE:
788         {
789             const(int) x = GET_X_LPARAM(lParam);
790             const(int) y = GET_Y_LPARAM(lParam);
791 
792             if (!window.win32.cursorTracked)
793             {
794                 TRACKMOUSEEVENT tme;
795                 memset(&tme, 0, typeof(tme).sizeof);
796                 tme.cbSize = typeof(tme).sizeof;
797                 tme.dwFlags = TME_LEAVE;
798                 tme.hwndTrack = window.win32.handle;
799                 TrackMouseEvent(&tme);
800 
801                 window.win32.cursorTracked = GLFW_TRUE;
802                 _glfwInputCursorEnter(window, GLFW_TRUE);
803             }
804 
805             if (window.cursorMode == GLFW_CURSOR_DISABLED)
806             {
807                 const(int) dx = x - window.win32.lastCursorPosX;
808                 const(int) dy = y - window.win32.lastCursorPosY;
809 
810                 if (_glfw.win32.disabledCursorWindow != window)
811                     break;
812                 if (window.rawMouseMotion)
813                     break;
814 
815                 _glfwInputCursorPos(window,
816                                     window.virtualCursorPosX + dx,
817                                     window.virtualCursorPosY + dy);
818             }
819             else
820                 _glfwInputCursorPos(window, x, y);
821 
822             window.win32.lastCursorPosX = x;
823             window.win32.lastCursorPosY = y;
824 
825             return 0;
826         }
827 
828         case WM_INPUT:
829         {
830             UINT size = 0;
831             HRAWINPUT ri = cast(HRAWINPUT) lParam;
832             RAWINPUT* data = null;
833             int dx;int dy;
834 
835             if (_glfw.win32.disabledCursorWindow != window)
836                 break;
837             if (!window.rawMouseMotion)
838                 break;
839 
840             GetRawInputData(ri, RID_INPUT, null, &size, RAWINPUTHEADER.sizeof);
841             if (size > cast(UINT) _glfw.win32.rawInputSize)
842             {
843                 free(_glfw.win32.rawInput);
844                 _glfw.win32.rawInput = cast(RAWINPUT*) calloc(size, 1);
845                 _glfw.win32.rawInputSize = size;
846             }
847 
848             size = _glfw.win32.rawInputSize;
849             if (GetRawInputData(ri, RID_INPUT,
850                                 _glfw.win32.rawInput, &size,
851                                 RAWINPUTHEADER.sizeof) == cast(UINT) -1)
852             {
853                 _glfwInputError(GLFW_PLATFORM_ERROR,
854                                 "Win32: Failed to retrieve raw input data");
855                 break;
856             }
857 
858             data = _glfw.win32.rawInput;
859             if (data.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
860             {
861                 dx = data.data.mouse.lLastX - window.win32.lastCursorPosX;
862                 dy = data.data.mouse.lLastY - window.win32.lastCursorPosY;
863             }
864             else
865             {
866                 dx = data.data.mouse.lLastX;
867                 dy = data.data.mouse.lLastY;
868             }
869 
870             _glfwInputCursorPos(window,
871                                 window.virtualCursorPosX + dx,
872                                 window.virtualCursorPosY + dy);
873 
874             window.win32.lastCursorPosX += dx;
875             window.win32.lastCursorPosY += dy;
876             break;
877         }
878 
879         case WM_MOUSELEAVE:
880         {
881             window.win32.cursorTracked = GLFW_FALSE;
882             _glfwInputCursorEnter(window, GLFW_FALSE);
883             return 0;
884         }
885 
886         case WM_MOUSEWHEEL:
887         {
888             _glfwInputScroll(window, 0.0, cast(SHORT) HIWORD(wParam) / cast(double) WHEEL_DELTA);
889             return 0;
890         }
891 
892         case WM_MOUSEHWHEEL:
893         {
894             // This message is only sent on Windows Vista and later
895             // NOTE: The X-axis is inverted for consistency with macOS and X11
896             _glfwInputScroll(window, -(cast(SHORT) HIWORD(wParam) / cast(double) WHEEL_DELTA), 0.0);
897             return 0;
898         }
899 
900         case WM_ENTERSIZEMOVE:
901         case WM_ENTERMENULOOP:
902         {
903             if (window.win32.frameAction)
904                 break;
905 
906             // HACK: Enable the cursor while the user is moving or
907             //       resizing the window or using the window menu
908             if (window.cursorMode == GLFW_CURSOR_DISABLED)
909                 enableCursor(window);
910 
911             break;
912         }
913 
914         case WM_EXITSIZEMOVE:
915         case WM_EXITMENULOOP:
916         {
917             if (window.win32.frameAction)
918                 break;
919 
920             // HACK: Disable the cursor once the user is done moving or
921             //       resizing the window or using the menu
922             if (window.cursorMode == GLFW_CURSOR_DISABLED)
923                 disableCursor(window);
924 
925             break;
926         }
927 
928         case WM_SIZE:
929         {
930             const(GLFWbool) iconified = wParam == SIZE_MINIMIZED;
931             const(GLFWbool) maximized = wParam == SIZE_MAXIMIZED ||
932                                        (window.win32.maximized &&
933                                         wParam != SIZE_RESTORED);
934 
935             if (_glfw.win32.disabledCursorWindow == window)
936                 updateClipRect(window);
937 
938             if (window.win32.iconified != iconified)
939                 _glfwInputWindowIconify(window, iconified);
940 
941             if (window.win32.maximized != maximized)
942                 _glfwInputWindowMaximize(window, maximized);
943 
944             _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam));
945             _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam));
946 
947             if (window.monitor && window.win32.iconified != iconified)
948             {
949                 if (iconified)
950                     releaseMonitor(window);
951                 else
952                 {
953                     acquireMonitor(window);
954                     fitToMonitor(window);
955                 }
956             }
957 
958             window.win32.iconified = iconified;
959             window.win32.maximized = maximized;
960             return 0;
961         }
962 
963         case WM_MOVE:
964         {
965             if (_glfw.win32.disabledCursorWindow == window)
966                 updateClipRect(window);
967 
968             // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
969             // those macros do not handle negative window positions correctly
970             _glfwInputWindowPos(window,
971                                 GET_X_LPARAM(lParam),
972                                 GET_Y_LPARAM(lParam));
973             return 0;
974         }
975 
976         case WM_SIZING:
977         {
978             if (window.numer == GLFW_DONT_CARE ||
979                 window.denom == GLFW_DONT_CARE)
980             {
981                 break;
982             }
983 
984             applyAspectRatio(window, cast(int) wParam, cast(RECT*) lParam);
985             return TRUE;
986         }
987 
988         case WM_GETMINMAXINFO:
989         {
990             int xoff;int yoff;
991             UINT dpi = USER_DEFAULT_SCREEN_DPI;
992             MINMAXINFO* mmi = cast(MINMAXINFO*) lParam;
993 
994             if (window.monitor)
995                 break;
996 
997             if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
998                 dpi = mixin(GetDpiForWindow)(cast(void*) window.win32.handle);
999 
1000             getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
1001                               0, 0, &xoff, &yoff, dpi);
1002 
1003             if (window.minwidth != GLFW_DONT_CARE &&
1004                 window.minheight != GLFW_DONT_CARE)
1005             {
1006                 mmi.ptMinTrackSize.x = window.minwidth + xoff;
1007                 mmi.ptMinTrackSize.y = window.minheight + yoff;
1008             }
1009 
1010             if (window.maxwidth != GLFW_DONT_CARE &&
1011                 window.maxheight != GLFW_DONT_CARE)
1012             {
1013                 mmi.ptMaxTrackSize.x = window.maxwidth + xoff;
1014                 mmi.ptMaxTrackSize.y = window.maxheight + yoff;
1015             }
1016 
1017             if (!window.decorated)
1018             {
1019                 MONITORINFO mi;
1020                 HMONITOR mh = MonitorFromWindow(cast(void*) window.win32.handle,
1021                                                       MONITOR_DEFAULTTONEAREST);
1022 
1023                 memset(&mi, 0, typeof(mi).sizeof);
1024                 mi.cbSize = typeof(mi).sizeof;
1025                 GetMonitorInfo(mh, &mi);
1026 
1027                 mmi.ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
1028                 mmi.ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
1029                 mmi.ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
1030                 mmi.ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
1031             }
1032 
1033             return 0;
1034         }
1035 
1036         case WM_PAINT:
1037         {
1038             _glfwInputWindowDamage(window);
1039             break;
1040         }
1041 
1042         case WM_ERASEBKGND:
1043         {
1044             return TRUE;
1045         }
1046 
1047         case WM_NCACTIVATE:
1048         case WM_NCPAINT:
1049         {
1050             // Prevent title bar from being drawn after restoring a minimized
1051             // undecorated window
1052             if (!window.decorated)
1053                 return TRUE;
1054 
1055             break;
1056         }
1057 
1058         case WM_DWMCOMPOSITIONCHANGED:
1059         {
1060             if (window.win32.transparent)
1061                 updateFramebufferTransparency(window);
1062             return 0;
1063         }
1064 
1065         case WM_GETDPISCALEDSIZE:
1066         {
1067             if (window.win32.scaleToMonitor)
1068                 break;
1069 
1070             // Adjust the window size to keep the content area size constant
1071             if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
1072             {
1073                 RECT source = RECT(0);RECT target = RECT(0);
1074                 SIZE* size = cast(SIZE*) lParam;
1075 
1076                 mixin(AdjustWindowRectExForDpi)(&source, getWindowStyle(window),
1077                                          FALSE, getWindowExStyle(window),
1078                                          mixin(GetDpiForWindow)(cast(void*) window.win32.handle));
1079                 mixin(AdjustWindowRectExForDpi)(&target, getWindowStyle(window),
1080                                          FALSE, getWindowExStyle(window),
1081                                          LOWORD(wParam));
1082 
1083                 size.cx += (target.right - target.left) -
1084                             (source.right - source.left);
1085                 size.cy += (target.bottom - target.top) -
1086                             (source.bottom - source.top);
1087                 return TRUE;
1088             }
1089 
1090             break;
1091         }
1092 
1093         case WM_DPICHANGED:
1094         {
1095             const(float) xscale = HIWORD(wParam) / cast(float) USER_DEFAULT_SCREEN_DPI;
1096             const(float) yscale = LOWORD(wParam) / cast(float) USER_DEFAULT_SCREEN_DPI;
1097 
1098             // Only apply the suggested size if the OS is new enough to have
1099             // sent a WM_GETDPISCALEDSIZE before this
1100             if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
1101             {
1102                 RECT* suggested = cast(RECT*) lParam;
1103                 SetWindowPos(cast(void*) window.win32.handle, HWND_TOP,
1104                              suggested.left,
1105                              suggested.top,
1106                              suggested.right - suggested.left,
1107                              suggested.bottom - suggested.top,
1108                              SWP_NOACTIVATE | SWP_NOZORDER);
1109             }
1110 
1111             _glfwInputWindowContentScale(window, xscale, yscale);
1112             break;
1113         }
1114 
1115         case WM_SETCURSOR:
1116         {
1117             if (LOWORD(lParam) == HTCLIENT)
1118             {
1119                 updateCursorImage(window);
1120                 return TRUE;
1121             }
1122 
1123             break;
1124         }
1125 
1126         case WM_DROPFILES:
1127         {
1128             HDROP drop = cast(HDROP) wParam;
1129             POINT pt;
1130             int i;
1131 
1132             const(int) count = DragQueryFileW(drop, 0xffffffff, null, 0);
1133             auto paths = cast(char**) calloc(count, (char*).sizeof);
1134 
1135             // Move the mouse to the position of the drop
1136             DragQueryPoint(drop, &pt);
1137             _glfwInputCursorPos(window, pt.x, pt.y);
1138 
1139             for (i = 0;  i < count;  i++)
1140             {
1141                 const(UINT) length = DragQueryFileW(drop, i, null, 0);
1142                 WCHAR* buffer = cast(WCHAR*) calloc(cast(size_t) length + 1, WCHAR.sizeof);
1143 
1144                 DragQueryFileW(drop, i, buffer, length + 1);
1145                 paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
1146 
1147                 free(buffer);
1148             }
1149 
1150             _glfwInputDrop(window, count, cast(const(char)**) paths);
1151 
1152             for (i = 0;  i < count;  i++)
1153                 free(paths[i]);
1154             free(paths);
1155 
1156             DragFinish(drop);
1157             return 0;
1158         }
1159         default: break;
1160     }
1161 
1162     return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1163 }
1164 
1165 // Creates the GLFW window
1166 //
1167 static int createNativeWindow(_GLFWwindow* window, const(_GLFWwndconfig)* wndconfig, const(_GLFWfbconfig)* fbconfig) {
1168     int xpos;int ypos;int fullWidth;int fullHeight;
1169     WCHAR* wideTitle;
1170     DWORD style = getWindowStyle(window);
1171     DWORD exStyle = getWindowExStyle(window);
1172 
1173     if (window.monitor)
1174     {
1175         GLFWvidmode mode;
1176 
1177         // NOTE: This window placement is temporary and approximate, as the
1178         //       correct position and size cannot be known until the monitor
1179         //       video mode has been picked in _glfwSetVideoModeWin32
1180         _glfwPlatformGetMonitorPos(window.monitor, &xpos, &ypos);
1181         _glfwPlatformGetVideoMode(window.monitor, &mode);
1182         fullWidth  = mode.width;
1183         fullHeight = mode.height;
1184     }
1185     else
1186     {
1187         xpos = CW_USEDEFAULT;
1188         ypos = CW_USEDEFAULT;
1189 
1190         window.win32.maximized = wndconfig.maximized;
1191         if (wndconfig.maximized)
1192             style |= WS_MAXIMIZE;
1193 
1194         getFullWindowSize(style, exStyle,
1195                           wndconfig.width, wndconfig.height,
1196                           &fullWidth, &fullHeight,
1197                           USER_DEFAULT_SCREEN_DPI);
1198     }
1199 
1200     wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig.title);
1201     if (!wideTitle)
1202         return GLFW_FALSE;
1203 
1204     window.win32.handle = CreateWindowExW(exStyle,
1205                                            _GLFW_WNDCLASSNAME.ptr,
1206                                            wideTitle,
1207                                            style,
1208                                            xpos, ypos,
1209                                            fullWidth, fullHeight,
1210                                            null, // No parent window
1211                                            null, // No window menu
1212                                            GetModuleHandleW(null),
1213                                            null);
1214 
1215     free(wideTitle);
1216 
1217     if (!window.win32.handle)
1218     {
1219         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
1220                              "Win32: Failed to create window");
1221         return GLFW_FALSE;
1222     }
1223 
1224     SetPropW(window.win32.handle, "GLFW"w.ptr, window);
1225 
1226     if (IsWindows7OrGreater())
1227     {
1228         mixin(ChangeWindowMessageFilterEx)(window.win32.handle,
1229                                     WM_DROPFILES, MSGFLT_ALLOW, null);
1230         mixin(ChangeWindowMessageFilterEx)(window.win32.handle,
1231                                     WM_COPYDATA, MSGFLT_ALLOW, null);
1232         mixin(ChangeWindowMessageFilterEx)(window.win32.handle,
1233                                     WM_COPYGLOBALDATA, MSGFLT_ALLOW, null);
1234     }
1235 
1236     window.win32.scaleToMonitor = wndconfig.scaleToMonitor;
1237 
1238     // Adjust window rect to account for DPI scaling of the window frame and
1239     // (if enabled) DPI scaling of the content area
1240     // This cannot be done until we know what monitor the window was placed on
1241     if (!window.monitor)
1242     {
1243         RECT rect = RECT( 0, 0, wndconfig.width, wndconfig.height );
1244         WINDOWPLACEMENT wp = WINDOWPLACEMENT( WINDOWPLACEMENT.sizeof );
1245 
1246         if (wndconfig.scaleToMonitor)
1247         {
1248             float xscale;float yscale;
1249             _glfwPlatformGetWindowContentScale(window, &xscale, &yscale);
1250             rect.right = cast(int) (rect.right * xscale);
1251             rect.bottom = cast(int) (rect.bottom * yscale);
1252         }
1253 
1254         ClientToScreen(window.win32.handle, cast(POINT*) &rect.left);
1255         ClientToScreen(window.win32.handle, cast(POINT*) &rect.right);
1256 
1257         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1258         {
1259             mixin(AdjustWindowRectExForDpi)(&rect, style, FALSE, exStyle,
1260                                      mixin(GetDpiForWindow)(window.win32.handle));
1261         }
1262         else
1263             AdjustWindowRectEx(&rect, style, FALSE, exStyle);
1264 
1265         // Only update the restored window rect as the window may be maximized
1266         GetWindowPlacement(window.win32.handle, &wp);
1267         wp.rcNormalPosition = rect;
1268         wp.showCmd = SW_HIDE;
1269         SetWindowPlacement(window.win32.handle, &wp);
1270     }
1271 
1272     DragAcceptFiles(window.win32.handle, TRUE);
1273 
1274     if (fbconfig.transparent)
1275     {
1276         updateFramebufferTransparency(window);
1277         window.win32.transparent = GLFW_TRUE;
1278     }
1279 
1280     return GLFW_TRUE;
1281 }
1282 
1283 
1284 //////////////////////////////////////////////////////////////////////////
1285 //////                       GLFW internal API                      //////
1286 //////////////////////////////////////////////////////////////////////////
1287 
1288 // Registers the GLFW window class
1289 //
1290 GLFWbool _glfwRegisterWindowClassWin32() {
1291     WNDCLASSEXW wc;
1292 
1293     memset(&wc, 0, typeof(wc).sizeof);
1294     wc.cbSize        = typeof(wc).sizeof;
1295     wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1296     wc.lpfnWndProc   = cast(WNDPROC) &windowProc;
1297     wc.hInstance     = GetModuleHandleW(null);
1298     wc.hCursor       = LoadCursorW(null, IDC_ARROW);
1299     wc.lpszClassName = _GLFW_WNDCLASSNAME.ptr;
1300 
1301     // Load user-provided icon if available
1302     wc.hIcon = LoadImageW(GetModuleHandleW(null),
1303                           "GLFW_ICON"w.ptr, IMAGE_ICON,
1304                           0, 0, LR_DEFAULTSIZE | LR_SHARED);
1305     if (!wc.hIcon)
1306     {
1307         // No user-provided icon found, load default icon
1308         wc.hIcon = LoadImageW(null,
1309                               IDI_APPLICATION, IMAGE_ICON,
1310                               0, 0, LR_DEFAULTSIZE | LR_SHARED);
1311     }
1312 
1313     if (!RegisterClassExW(&wc))
1314     {
1315         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
1316                              "Win32: Failed to register window class");
1317         return GLFW_FALSE;
1318     }
1319 
1320     return GLFW_TRUE;
1321 }
1322 
1323 // Unregisters the GLFW window class
1324 //
1325 void _glfwUnregisterWindowClassWin32() {
1326     UnregisterClassW(_GLFW_WNDCLASSNAME.ptr, GetModuleHandleW(null));
1327 }
1328 
1329 
1330 //////////////////////////////////////////////////////////////////////////
1331 //////                       GLFW platform API                      //////
1332 //////////////////////////////////////////////////////////////////////////
1333 
1334 int _glfwPlatformCreateWindow(_GLFWwindow* window, const(_GLFWwndconfig)* wndconfig, const(_GLFWctxconfig)* ctxconfig, const(_GLFWfbconfig)* fbconfig) {
1335     if (!createNativeWindow(window, wndconfig, fbconfig))
1336         return GLFW_FALSE;
1337 
1338     if (ctxconfig.client != GLFW_NO_API)
1339     {
1340         if (ctxconfig.source == GLFW_NATIVE_CONTEXT_API)
1341         {
1342             if (!_glfwInitWGL())
1343                 return GLFW_FALSE;
1344             if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
1345                 return GLFW_FALSE;
1346         }
1347         else if (ctxconfig.source == GLFW_EGL_CONTEXT_API)
1348         {
1349             if (!_glfwInitEGL())
1350                 return GLFW_FALSE;
1351             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
1352                 return GLFW_FALSE;
1353         }
1354         else if (ctxconfig.source == GLFW_OSMESA_CONTEXT_API)
1355         {
1356             if (!_glfwInitOSMesa())
1357                 return GLFW_FALSE;
1358             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
1359                 return GLFW_FALSE;
1360         }
1361     }
1362 
1363     if (window.monitor)
1364     {
1365         _glfwPlatformShowWindow(window);
1366         _glfwPlatformFocusWindow(window);
1367         acquireMonitor(window);
1368         fitToMonitor(window);
1369     }
1370 
1371     return GLFW_TRUE;
1372 }
1373 
1374 void _glfwPlatformDestroyWindow(_GLFWwindow* window) {
1375     if (window.monitor)
1376         releaseMonitor(window);
1377 
1378     if (window.context.destroy)
1379         window.context.destroy(window);
1380 
1381     if (_glfw.win32.disabledCursorWindow == window)
1382         _glfw.win32.disabledCursorWindow = null;
1383 
1384     if (window.win32.handle)
1385     {
1386         RemovePropW(window.win32.handle, "GLFW"w.ptr);
1387         DestroyWindow(window.win32.handle);
1388         window.win32.handle = null;
1389     }
1390 
1391     if (window.win32.bigIcon)
1392         DestroyIcon(window.win32.bigIcon);
1393 
1394     if (window.win32.smallIcon)
1395         DestroyIcon(window.win32.smallIcon);
1396 }
1397 
1398 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const(char)* title) {
1399     WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
1400     if (!wideTitle)
1401         return;
1402 
1403     SetWindowTextW(window.win32.handle, wideTitle);
1404     free(wideTitle);
1405 }
1406 
1407 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const(GLFWimage)* images) {
1408     HICON bigIcon = null;HICON smallIcon = null;
1409 
1410     if (count)
1411     {
1412         const(GLFWimage)* bigImage = chooseImage(count, images,
1413                                                 GetSystemMetrics(SM_CXICON),
1414                                                 GetSystemMetrics(SM_CYICON));
1415         const(GLFWimage)* smallImage = chooseImage(count, images,
1416                                                   GetSystemMetrics(SM_CXSMICON),
1417                                                   GetSystemMetrics(SM_CYSMICON));
1418 
1419         bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
1420         smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
1421     }
1422     else
1423     {
1424         bigIcon = cast(HICON) GetClassLongPtrW(window.win32.handle, GCLP_HICON);
1425         smallIcon = cast(HICON) GetClassLongPtrW(window.win32.handle, GCLP_HICONSM);
1426     }
1427 
1428     SendMessage(window.win32.handle, WM_SETICON, ICON_BIG, cast(LPARAM) bigIcon);
1429     SendMessage(window.win32.handle, WM_SETICON, ICON_SMALL, cast(LPARAM) smallIcon);
1430 
1431     if (window.win32.bigIcon)
1432         DestroyIcon(window.win32.bigIcon);
1433 
1434     if (window.win32.smallIcon)
1435         DestroyIcon(window.win32.smallIcon);
1436 
1437     if (count)
1438     {
1439         window.win32.bigIcon = bigIcon;
1440         window.win32.smallIcon = smallIcon;
1441     }
1442 }
1443 
1444 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) {
1445     POINT pos = POINT(0, 0);
1446     ClientToScreen(window.win32.handle, &pos);
1447 
1448     if (xpos)
1449         *xpos = pos.x;
1450     if (ypos)
1451         *ypos = pos.y;
1452 }
1453 
1454 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) {
1455     RECT rect = RECT( xpos, ypos, xpos, ypos );
1456 
1457     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1458     {
1459         mixin(AdjustWindowRectExForDpi)(&rect, getWindowStyle(window),
1460                                  FALSE, getWindowExStyle(window),
1461                                  mixin(GetDpiForWindow)(window.win32.handle));
1462     }
1463     else
1464     {
1465         /*DynCall*/AdjustWindowRectEx(&rect, getWindowStyle(window),
1466                            FALSE, getWindowExStyle(window));
1467     }
1468 
1469     SetWindowPos(window.win32.handle, null, rect.left, rect.top, 0, 0,
1470                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
1471 }
1472 
1473 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) {
1474     RECT area;
1475     GetClientRect(window.win32.handle, &area);
1476 
1477     if (width)
1478         *width = area.right;
1479     if (height)
1480         *height = area.bottom;
1481 }
1482 
1483 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) {
1484     if (window.monitor)
1485     {
1486         if (window.monitor.window == window)
1487         {
1488             acquireMonitor(window);
1489             fitToMonitor(window);
1490         }
1491     }
1492     else
1493     {
1494         RECT rect = RECT( 0, 0, width, height );
1495 
1496         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1497         {
1498             mixin(AdjustWindowRectExForDpi)(&rect, getWindowStyle(window),
1499                                      FALSE, getWindowExStyle(window),
1500                                      mixin(GetDpiForWindow)(window.win32.handle));
1501         }
1502         else
1503         {
1504             /*DynCall*/AdjustWindowRectEx(&rect, getWindowStyle(window),
1505                                FALSE, getWindowExStyle(window));
1506         }
1507 
1508         SetWindowPos(window.win32.handle, HWND_TOP,
1509                      0, 0, rect.right - rect.left, rect.bottom - rect.top,
1510                      SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
1511     }
1512 }
1513 
1514 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) {
1515     RECT area;
1516 
1517     if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
1518         (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
1519     {
1520         return;
1521     }
1522 
1523     GetWindowRect(window.win32.handle, &area);
1524     MoveWindow(window.win32.handle,
1525                area.left, area.top,
1526                area.right - area.left,
1527                area.bottom - area.top, TRUE);
1528 }
1529 
1530 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) {
1531     RECT area;
1532 
1533     if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
1534         return;
1535 
1536     GetWindowRect(window.win32.handle, &area);
1537     applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
1538     MoveWindow(window.win32.handle,
1539                area.left, area.top,
1540                area.right - area.left,
1541                area.bottom - area.top, TRUE);
1542 }
1543 
1544 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) {
1545     _glfwPlatformGetWindowSize(window, width, height);
1546 }
1547 
1548 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) {
1549     RECT rect;
1550     int width;int height;
1551 
1552     _glfwPlatformGetWindowSize(window, &width, &height);
1553     SetRect(&rect, 0, 0, width, height);
1554 
1555     if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1556     {
1557         mixin(AdjustWindowRectExForDpi)(&rect, getWindowStyle(window),
1558                                  FALSE, getWindowExStyle(window),
1559                                  mixin(GetDpiForWindow)(window.win32.handle));
1560     }
1561     else
1562     {
1563         /*DynCall*/AdjustWindowRectEx(&rect, getWindowStyle(window),
1564                            FALSE, getWindowExStyle(window));
1565     }
1566 
1567     if (left)
1568         *left = -rect.left;
1569     if (top)
1570         *top = -rect.top;
1571     if (right)
1572         *right = rect.right - width;
1573     if (bottom)
1574         *bottom = rect.bottom - height;
1575 }
1576 
1577 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) {
1578     HANDLE handle = MonitorFromWindow(window.win32.handle,
1579                                             MONITOR_DEFAULTTONEAREST);
1580     _glfwGetMonitorContentScaleWin32(handle, xscale, yscale);
1581 }
1582 
1583 void _glfwPlatformIconifyWindow(_GLFWwindow* window) {
1584     ShowWindow(window.win32.handle, SW_MINIMIZE);
1585 }
1586 
1587 void _glfwPlatformRestoreWindow(_GLFWwindow* window) {
1588     ShowWindow(window.win32.handle, SW_RESTORE);
1589 }
1590 
1591 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) {
1592     ShowWindow(window.win32.handle, SW_MAXIMIZE);
1593 }
1594 
1595 void _glfwPlatformShowWindow(_GLFWwindow* window) {
1596     ShowWindow(window.win32.handle, SW_SHOWNA);
1597 }
1598 
1599 void _glfwPlatformHideWindow(_GLFWwindow* window) {
1600     ShowWindow(window.win32.handle, SW_HIDE);
1601 }
1602 
1603 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) {
1604     FlashWindow(window.win32.handle, TRUE);
1605 }
1606 
1607 void _glfwPlatformFocusWindow(_GLFWwindow* window) {
1608     BringWindowToTop(window.win32.handle);
1609     SetForegroundWindow(window.win32.handle);
1610     SetFocus(window.win32.handle);
1611 }
1612 
1613 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) {
1614     if (window.monitor == monitor)
1615     {
1616         if (monitor)
1617         {
1618             if (monitor.window == window)
1619             {
1620                 acquireMonitor(window);
1621                 fitToMonitor(window);
1622             }
1623         }
1624         else
1625         {
1626             RECT rect = RECT( xpos, ypos, xpos + width, ypos + height );
1627 
1628             if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1629             {
1630                 mixin(AdjustWindowRectExForDpi)(&rect, getWindowStyle(window),
1631                                          FALSE, getWindowExStyle(window),
1632                                          mixin(GetDpiForWindow)(window.win32.handle));
1633             }
1634             else
1635             {
1636                 /*DynCall*/AdjustWindowRectEx(&rect, getWindowStyle(window),
1637                                    FALSE, getWindowExStyle(window));
1638             }
1639 
1640             SetWindowPos(window.win32.handle, HWND_TOP,
1641                          rect.left, rect.top,
1642                          rect.right - rect.left, rect.bottom - rect.top,
1643                          SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
1644         }
1645 
1646         return;
1647     }
1648 
1649     if (window.monitor)
1650         releaseMonitor(window);
1651 
1652     _glfwInputWindowMonitor(window, monitor);
1653 
1654     if (window.monitor)
1655     {
1656         MONITORINFO mi = MONITORINFO(MONITORINFO.sizeof);
1657         UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
1658 
1659         if (window.decorated)
1660         {
1661             DWORD style = GetWindowLongW(window.win32.handle, GWL_STYLE);
1662             style &= ~WS_OVERLAPPEDWINDOW;
1663             style |= getWindowStyle(window);
1664             SetWindowLongW(window.win32.handle, GWL_STYLE, style);
1665             flags |= SWP_FRAMECHANGED;
1666         }
1667 
1668         acquireMonitor(window);
1669 
1670         GetMonitorInfo(window.monitor.win32.handle, &mi);
1671         SetWindowPos(window.win32.handle, HWND_TOPMOST,
1672                      mi.rcMonitor.left,
1673                      mi.rcMonitor.top,
1674                      mi.rcMonitor.right - mi.rcMonitor.left,
1675                      mi.rcMonitor.bottom - mi.rcMonitor.top,
1676                      flags);
1677     }
1678     else
1679     {
1680         HWND after;
1681         RECT rect = RECT( xpos, ypos, xpos + width, ypos + height );
1682         DWORD style = GetWindowLongW(window.win32.handle, GWL_STYLE);
1683         UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
1684 
1685         if (window.decorated)
1686         {
1687             style &= ~WS_POPUP;
1688             style |= getWindowStyle(window);
1689             SetWindowLongW(window.win32.handle, GWL_STYLE, style);
1690 
1691             flags |= SWP_FRAMECHANGED;
1692         }
1693 
1694         if (window.floating)
1695             after = HWND_TOPMOST;
1696         else
1697             after = HWND_NOTOPMOST;
1698 
1699         if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
1700         {
1701             mixin(AdjustWindowRectExForDpi)(&rect, getWindowStyle(window),
1702                                      FALSE, getWindowExStyle(window),
1703                                      mixin(GetDpiForWindow)(window.win32.handle));
1704         }
1705         else
1706         {
1707             /*DynCall*/AdjustWindowRectEx(&rect, getWindowStyle(window),
1708                                FALSE, getWindowExStyle(window));
1709         }
1710 
1711         SetWindowPos(window.win32.handle, after,
1712                      rect.left, rect.top,
1713                      rect.right - rect.left, rect.bottom - rect.top,
1714                      flags);
1715     }
1716 }
1717 
1718 int _glfwPlatformWindowFocused(_GLFWwindow* window) {
1719     return window.win32.handle == GetActiveWindow();
1720 }
1721 
1722 int _glfwPlatformWindowIconified(_GLFWwindow* window) {
1723     return IsIconic(window.win32.handle);
1724 }
1725 
1726 int _glfwPlatformWindowVisible(_GLFWwindow* window) {
1727     return IsWindowVisible(window.win32.handle);
1728 }
1729 
1730 int _glfwPlatformWindowMaximized(_GLFWwindow* window) {
1731     return IsZoomed(window.win32.handle);
1732 }
1733 
1734 int _glfwPlatformWindowHovered(_GLFWwindow* window) {
1735     return cursorInContentArea(window);
1736 }
1737 
1738 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) {
1739     BOOL enabled;
1740 
1741     if (!window.win32.transparent)
1742         return GLFW_FALSE;
1743 
1744     if (!IsWindowsVistaOrGreater())
1745         return GLFW_FALSE;
1746 
1747     return SUCCEEDED(_glfw.win32.dwmapi.IsCompositionEnabled(&enabled)) && enabled;
1748 }
1749 
1750 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) {
1751     updateWindowStyles(window);
1752 }
1753 
1754 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) {
1755     updateWindowStyles(window);
1756 }
1757 
1758 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) {
1759     HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST;
1760     SetWindowPos(window.win32.handle, after, 0, 0, 0, 0,
1761                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1762 }
1763 
1764 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) {
1765     BYTE alpha;
1766     DWORD flags;
1767 
1768     if ((GetWindowLongW(window.win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
1769         GetLayeredWindowAttributes(window.win32.handle, null, &alpha, &flags))
1770     {
1771         if (flags & LWA_ALPHA)
1772             return alpha / 255.0f;
1773     }
1774 
1775     return 1.0f;
1776 }
1777 
1778 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) {
1779     if (opacity < 1.0f)
1780     {
1781         const(BYTE) alpha = cast(BYTE) (255 * opacity);
1782         DWORD style = GetWindowLongW(window.win32.handle, GWL_EXSTYLE);
1783         style |= WS_EX_LAYERED;
1784         SetWindowLongW(window.win32.handle, GWL_EXSTYLE, style);
1785         SetLayeredWindowAttributes(window.win32.handle, 0, alpha, LWA_ALPHA);
1786     }
1787     else
1788     {
1789         DWORD style = GetWindowLongW(window.win32.handle, GWL_EXSTYLE);
1790         style &= ~WS_EX_LAYERED;
1791         SetWindowLongW(window.win32.handle, GWL_EXSTYLE, style);
1792     }
1793 }
1794 
1795 void _glfwPlatformSetRawMouseMotion(_GLFWwindow* window, GLFWbool enabled) {
1796     if (_glfw.win32.disabledCursorWindow != window)
1797         return;
1798 
1799     if (enabled)
1800         enableRawMouseMotion(window);
1801     else
1802         disableRawMouseMotion(window);
1803 }
1804 
1805 GLFWbool _glfwPlatformRawMouseMotionSupported() {
1806     return GLFW_TRUE;
1807 }
1808 
1809 void _glfwPlatformPollEvents() {
1810     MSG msg;
1811     HWND handle;
1812     _GLFWwindow* window;
1813 
1814     while (PeekMessageW(&msg, null, 0, 0, PM_REMOVE))
1815     {
1816         if (msg.message == WM_QUIT)
1817         {
1818             // NOTE: While GLFW does not itself post WM_QUIT, other processes
1819             //       may post it to this one, for example Task Manager
1820             // HACK: Treat WM_QUIT as a close on all windows
1821 
1822             window = _glfw.windowListHead;
1823             while (window)
1824             {
1825                 _glfwInputWindowCloseRequest(window);
1826                 window = window.next;
1827             }
1828         }
1829         else
1830         {
1831             TranslateMessage(&msg);
1832             DispatchMessageW(&msg);
1833         }
1834     }
1835 
1836     // HACK: Release modifier keys that the system did not emit KEYUP for
1837     // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
1838     //       no key up message is generated by the first key release
1839     // NOTE: Windows key is not reported as released by the Win+V hotkey
1840     //       Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
1841     //       because they change the input focus
1842     // NOTE: The other half of this is in the WM_*KEY* handler in windowProc
1843     handle = GetActiveWindow();
1844     if (handle)
1845     {
1846         window = cast(_GLFWwindow*) GetPropW(handle, "GLFW"w.ptr);
1847         if (window)
1848         {
1849             int i;
1850             const(int)[2][4] keys = [
1851                 [ VK_LSHIFT, GLFW_KEY_LEFT_SHIFT ],
1852                 [ VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT ],
1853                 [ VK_LWIN, GLFW_KEY_LEFT_SUPER ],
1854                 [ VK_RWIN, GLFW_KEY_RIGHT_SUPER ]
1855             ];
1856 
1857             for (i = 0;  i < 4;  i++)
1858             {
1859                 const(int) vk = keys[i][0];
1860                 const(int) key = keys[i][1];
1861                 const(int) scancode = _glfw.win32.scancodes[key];
1862 
1863                 if ((GetKeyState(vk) & 0x8000))
1864                     continue;
1865                 if (window.keys[key] != GLFW_PRESS)
1866                     continue;
1867 
1868                 _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
1869             }
1870         }
1871     }
1872 
1873     window = _glfw.win32.disabledCursorWindow;
1874     if (window)
1875     {
1876         int width;int height;
1877         _glfwPlatformGetWindowSize(window, &width, &height);
1878 
1879         // NOTE: Re-center the cursor only if it has moved since the last call,
1880         //       to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
1881         if (window.win32.lastCursorPosX != width / 2 ||
1882             window.win32.lastCursorPosY != height / 2)
1883         {
1884             _glfwPlatformSetCursorPos(window, width / 2, height / 2);
1885         }
1886     }
1887 }
1888 
1889 void _glfwPlatformWaitEvents() {
1890     WaitMessage();
1891 
1892     _glfwPlatformPollEvents();
1893 }
1894 
1895 void _glfwPlatformWaitEventsTimeout(double timeout) {
1896     MsgWaitForMultipleObjects(0, null, FALSE, cast(DWORD) (timeout * 1e3), QS_ALLEVENTS);
1897 
1898     _glfwPlatformPollEvents();
1899 }
1900 
1901 void _glfwPlatformPostEmptyEvent() {
1902     PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0);
1903 }
1904 
1905 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) {
1906     POINT pos;
1907 
1908     if (GetCursorPos(&pos))
1909     {
1910         ScreenToClient(window.win32.handle, &pos);
1911 
1912         if (xpos)
1913             *xpos = pos.x;
1914         if (ypos)
1915             *ypos = pos.y;
1916     }
1917 }
1918 
1919 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) {
1920     POINT pos = POINT( cast(int) xpos, cast(int) ypos );
1921 
1922     // Store the new position so it can be recognized later
1923     window.win32.lastCursorPosX = pos.x;
1924     window.win32.lastCursorPosY = pos.y;
1925 
1926     ClientToScreen(window.win32.handle, &pos);
1927     SetCursorPos(pos.x, pos.y);
1928 }
1929 
1930 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) {
1931     if (mode == GLFW_CURSOR_DISABLED)
1932     {
1933         if (_glfwPlatformWindowFocused(window))
1934             disableCursor(window);
1935     }
1936     else if (_glfw.win32.disabledCursorWindow == window)
1937         enableCursor(window);
1938     else if (cursorInContentArea(window))
1939         updateCursorImage(window);
1940 }
1941 
1942 const(char)* _glfwPlatformGetScancodeName(int scancode) {
1943     if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) ||
1944         _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN)
1945     {
1946         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
1947         return null;
1948     }
1949 
1950     return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]].ptr;
1951 }
1952 
1953 int _glfwPlatformGetKeyScancode(int key) {
1954     return _glfw.win32.scancodes[key];
1955 }
1956 
1957 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const(GLFWimage)* image, int xhot, int yhot) {
1958     cursor.win32.handle = cast(HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
1959     if (!cursor.win32.handle)
1960         return GLFW_FALSE;
1961 
1962     return GLFW_TRUE;
1963 }
1964 
1965 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) {
1966     int id = 0;
1967 
1968     if (shape == GLFW_ARROW_CURSOR)
1969         id = OCR_NORMAL;
1970     else if (shape == GLFW_IBEAM_CURSOR)
1971         id = OCR_IBEAM;
1972     else if (shape == GLFW_CROSSHAIR_CURSOR)
1973         id = OCR_CROSS;
1974     else if (shape == GLFW_HAND_CURSOR)
1975         id = OCR_HAND;
1976     else if (shape == GLFW_HRESIZE_CURSOR)
1977         id = OCR_SIZEWE;
1978     else if (shape == GLFW_VRESIZE_CURSOR)
1979         id = OCR_SIZENS;
1980     else
1981         return GLFW_FALSE;
1982 
1983     cursor.win32.handle = LoadImageW(null,
1984                                       MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
1985                                       LR_DEFAULTSIZE | LR_SHARED);
1986     if (!cursor.win32.handle)
1987     {
1988         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
1989                              "Win32: Failed to create standard cursor");
1990         return GLFW_FALSE;
1991     }
1992 
1993     return GLFW_TRUE;
1994 }
1995 
1996 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) {
1997     if (cursor.win32.handle)
1998         DestroyIcon(cast(HICON) cursor.win32.handle);
1999 }
2000 
2001 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) {
2002     if (cursorInContentArea(window))
2003         updateCursorImage(window);
2004 }
2005 
2006 void _glfwPlatformSetClipboardString(const(char)* string) {
2007     int characterCount;
2008     HANDLE object;
2009     WCHAR* buffer;
2010 
2011     characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, null, 0);
2012     if (!characterCount)
2013         return;
2014 
2015     object = GlobalAlloc(GMEM_MOVEABLE, characterCount * WCHAR.sizeof);
2016     if (!object)
2017     {
2018         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2019                              "Win32: Failed to allocate global handle for clipboard");
2020         return;
2021     }
2022 
2023     buffer = cast(WCHAR*) GlobalLock(object); // needless re-use of buffer variable?
2024     if (!buffer)
2025     {
2026         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2027                              "Win32: Failed to lock global handle");
2028         GlobalFree(object);
2029         return;
2030     }
2031 
2032     MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
2033     GlobalUnlock(object);
2034 
2035     if (!OpenClipboard(_glfw.win32.helperWindowHandle))
2036     {
2037         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2038                              "Win32: Failed to open clipboard");
2039         GlobalFree(object);
2040         return;
2041     }
2042 
2043     EmptyClipboard();
2044     SetClipboardData(CF_UNICODETEXT, object);
2045     CloseClipboard();
2046 }
2047 
2048 const(char)* _glfwPlatformGetClipboardString() {
2049     HANDLE object;
2050     WCHAR* buffer;
2051 
2052     if (!OpenClipboard(_glfw.win32.helperWindowHandle))
2053     {
2054         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2055                              "Win32: Failed to open clipboard");
2056         return null;
2057     }
2058 
2059     object = GetClipboardData(CF_UNICODETEXT);
2060     if (!object)
2061     {
2062         _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE,
2063                              "Win32: Failed to convert clipboard to string");
2064         CloseClipboard();
2065         return null;
2066     }
2067 
2068     buffer = cast(WCHAR*) GlobalLock(object);
2069     if (!buffer)
2070     {
2071         _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
2072                              "Win32: Failed to lock global handle");
2073         CloseClipboard();
2074         return null;
2075     }
2076 
2077     free(_glfw.win32.clipboardString);
2078     _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer);
2079 
2080     GlobalUnlock(object);
2081     CloseClipboard();
2082 
2083     return _glfw.win32.clipboardString;
2084 }
2085 
2086 void _glfwPlatformGetRequiredInstanceExtensions(const(char)** extensions) {
2087     if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface)
2088         return;
2089 
2090     extensions[0] = "VK_KHR_surface".ptr;
2091     extensions[1] = "VK_KHR_win32_surface".ptr;
2092 }
2093 
2094 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint queuefamily) {
2095     PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = cast(PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
2096         mixin(vkGetInstanceProcAddr)(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
2097     if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
2098     {
2099         _glfwInputError(GLFW_API_UNAVAILABLE,
2100                         "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
2101         return GLFW_FALSE;
2102     }
2103 
2104     return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
2105 }
2106 
2107 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const(VkAllocationCallbacks)* allocator, VkSurfaceKHR* surface) {
2108     VkResult err;
2109     VkWin32SurfaceCreateInfoKHR sci;
2110     PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
2111 
2112     vkCreateWin32SurfaceKHR = cast(PFN_vkCreateWin32SurfaceKHR)
2113         mixin(vkGetInstanceProcAddr)(instance, "vkCreateWin32SurfaceKHR");
2114     if (!vkCreateWin32SurfaceKHR)
2115     {
2116         _glfwInputError(GLFW_API_UNAVAILABLE,
2117                         "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
2118         return VkResult.VK_ERROR_EXTENSION_NOT_PRESENT;
2119     }
2120 
2121     memset(&sci, 0, typeof(sci).sizeof);
2122     sci.sType = VkStructureType.VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2123     sci.hinstance = GetModuleHandle(null);
2124     sci.hwnd = window.win32.handle;
2125 
2126     err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
2127     if (err)
2128     {
2129         _glfwInputError(GLFW_PLATFORM_ERROR,
2130                         "Win32: Failed to create Vulkan surface: %s",
2131                         _glfwGetVulkanResultString(err));
2132     }
2133 
2134     return err;
2135 }
2136 
2137 
2138 //////////////////////////////////////////////////////////////////////////
2139 //////                        GLFW native API                       //////
2140 //////////////////////////////////////////////////////////////////////////
2141 
2142 HWND glfwGetWin32Window(GLFWwindow* handle) {
2143     _GLFWwindow* window = cast(_GLFWwindow*) handle;
2144     mixin(_GLFW_REQUIRE_INIT_OR_RETURN!"null");
2145     return window.win32.handle;
2146 }