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