win32_window.c 67.3 KB
Newer Older
Camilla Berglund's avatar
Camilla Berglund committed
1
//========================================================================
Camilla Berglund's avatar
Camilla Berglund committed
2
// GLFW 3.3 Win32 - www.glfw.org
Camilla Berglund's avatar
Camilla Berglund committed
3
4
//------------------------------------------------------------------------
// Copyright (c) 2002-2006 Marcus Geelnard
Camilla Löwy's avatar
Camilla Löwy committed
5
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
Camilla Berglund's avatar
Camilla Berglund committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would
//    be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
//    be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
//    distribution.
//
//========================================================================

#include "internal.h"

Camilla Berglund's avatar
Camilla Berglund committed
30
#include <limits.h>
Camilla Berglund's avatar
Camilla Berglund committed
31
#include <stdlib.h>
32
#include <malloc.h>
33
#include <string.h>
34
#include <windowsx.h>
Camilla Berglund's avatar
Camilla Berglund committed
35
#include <shellapi.h>
Camilla Berglund's avatar
Camilla Berglund committed
36

37
38
#define _GLFW_KEY_INVALID -2

39
40
// Returns the window style for the specified window
//
41
static DWORD getWindowStyle(const _GLFWwindow* window)
42
43
44
{
    DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

Camilla Berglund's avatar
Camilla Berglund committed
45
46
47
    if (window->monitor)
        style |= WS_POPUP;
    else
48
    {
49
50
        style |= WS_SYSMENU | WS_MINIMIZEBOX;

Camilla Berglund's avatar
Camilla Berglund committed
51
52
        if (window->decorated)
        {
53
            style |= WS_CAPTION;
54

Camilla Berglund's avatar
Camilla Berglund committed
55
56
57
58
59
            if (window->resizable)
                style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
        }
        else
            style |= WS_POPUP;
60
61
62
63
64
65
66
    }

    return style;
}

// Returns the extended window style for the specified window
//
67
static DWORD getWindowExStyle(const _GLFWwindow* window)
68
69
70
{
    DWORD style = WS_EX_APPWINDOW;

Camilla Berglund's avatar
Camilla Berglund committed
71
72
    if (window->monitor || window->floating)
        style |= WS_EX_TOPMOST;
73
74
75
76

    return style;
}

Camilla Berglund's avatar
Camilla Berglund committed
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Returns the image whose area most closely matches the desired one
//
static const GLFWimage* chooseImage(int count, const GLFWimage* images,
                                    int width, int height)
{
    int i, leastDiff = INT_MAX;
    const GLFWimage* closest = NULL;

    for (i = 0;  i < count;  i++)
    {
        const int currDiff = abs(images[i].width * images[i].height -
                                 width * height);
        if (currDiff < leastDiff)
        {
            closest = images + i;
            leastDiff = currDiff;
        }
    }

    return closest;
}

// Creates an RGBA icon or cursor
//
static HICON createIcon(const GLFWimage* image,
                        int xhot, int yhot, GLFWbool icon)
{
    int i;
    HDC dc;
    HICON handle;
    HBITMAP color, mask;
    BITMAPV5HEADER bi;
    ICONINFO ii;
    unsigned char* target = NULL;
    unsigned char* source = image->pixels;

    ZeroMemory(&bi, sizeof(bi));
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
114
    bi.bV5Size        = sizeof(bi);
Camilla Berglund's avatar
Camilla Berglund committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    bi.bV5Width       = image->width;
    bi.bV5Height      = -image->height;
    bi.bV5Planes      = 1;
    bi.bV5BitCount    = 32;
    bi.bV5Compression = BI_BITFIELDS;
    bi.bV5RedMask     = 0x00ff0000;
    bi.bV5GreenMask   = 0x0000ff00;
    bi.bV5BlueMask    = 0x000000ff;
    bi.bV5AlphaMask   = 0xff000000;

    dc = GetDC(NULL);
    color = CreateDIBSection(dc,
                             (BITMAPINFO*) &bi,
                             DIB_RGB_COLORS,
                             (void**) &target,
                             NULL,
                             (DWORD) 0);
    ReleaseDC(NULL, dc);

    if (!color)
    {
136
137
        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                             "Win32: Failed to create RGBA bitmap");
Camilla Berglund's avatar
Camilla Berglund committed
138
139
140
141
142
143
        return NULL;
    }

    mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
    if (!mask)
    {
144
145
        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                             "Win32: Failed to create mask bitmap");
Camilla Berglund's avatar
Camilla Berglund committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
        DeleteObject(color);
        return NULL;
    }

    for (i = 0;  i < image->width * image->height;  i++)
    {
        target[0] = source[2];
        target[1] = source[1];
        target[2] = source[0];
        target[3] = source[3];
        target += 4;
        source += 4;
    }

    ZeroMemory(&ii, sizeof(ii));
    ii.fIcon    = icon;
    ii.xHotspot = xhot;
    ii.yHotspot = yhot;
    ii.hbmMask  = mask;
    ii.hbmColor = color;

    handle = CreateIconIndirect(&ii);

    DeleteObject(color);
    DeleteObject(mask);

    if (!handle)
    {
        if (icon)
175
176
177
178
        {
            _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                                 "Win32: Failed to create icon");
        }
Camilla Berglund's avatar
Camilla Berglund committed
179
        else
180
181
182
183
        {
            _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                                 "Win32: Failed to create cursor");
        }
Camilla Berglund's avatar
Camilla Berglund committed
184
185
186
187
188
    }

    return handle;
}

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
189
// Translate content area size to full window size according to styles and DPI
190
//
191
static void getFullWindowSize(DWORD style, DWORD exStyle,
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
192
                              int contentWidth, int contentHeight,
193
194
                              int* fullWidth, int* fullHeight,
                              UINT dpi)
195
{
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
196
    RECT rect = { 0, 0, contentWidth, contentHeight };
197
198
199
200
201
202

    if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
        AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
    else
        AdjustWindowRectEx(&rect, style, FALSE, exStyle);

203
204
205
206
    *fullWidth = rect.right - rect.left;
    *fullHeight = rect.bottom - rect.top;
}

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
207
// Enforce the content area aspect ratio based on which edge is being dragged
208
209
210
211
//
static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
{
    int xoff, yoff;
212
    UINT dpi = USER_DEFAULT_SCREEN_DPI;
Camilla Berglund's avatar
Camilla Berglund committed
213
    const float ratio = (float) window->numer / (float) window->denom;
214

215
216
217
    if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
        dpi = GetDpiForWindow(window->win32.handle);

218
    getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
219
                      0, 0, &xoff, &yoff, dpi);
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

    if (edge == WMSZ_LEFT  || edge == WMSZ_BOTTOMLEFT ||
        edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
    {
        area->bottom = area->top + yoff +
            (int) ((area->right - area->left - xoff) / ratio);
    }
    else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
    {
        area->top = area->bottom - yoff -
            (int) ((area->right - area->left - xoff) / ratio);
    }
    else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
    {
        area->right = area->left + xoff +
            (int) ((area->bottom - area->top - yoff) * ratio);
    }
}

239
// Updates the cursor image according to its cursor mode
240
//
241
static void updateCursorImage(_GLFWwindow* window)
242
{
243
    if (window->cursorMode == GLFW_CURSOR_NORMAL)
244
245
246
247
248
249
250
    {
        if (window->cursor)
            SetCursor(window->cursor->win32.handle);
        else
            SetCursor(LoadCursorW(NULL, IDC_ARROW));
    }
    else
251
        SetCursor(NULL);
252
253
}

254
255
256
257
// Updates the cursor clip rect
//
static void updateClipRect(_GLFWwindow* window)
{
258
259
260
261
262
263
264
265
266
267
    if (window)
    {
        RECT clipRect;
        GetClientRect(window->win32.handle, &clipRect);
        ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
        ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
        ClipCursor(&clipRect);
    }
    else
        ClipCursor(NULL);
268
269
}

270
// Enables WM_INPUT messages for the mouse for the specified window
271
//
272
static void enableRawMouseMotion(_GLFWwindow* window)
273
274
275
{
    const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
    if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
    {
        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                             "Win32: Failed to register raw input device");
    }
}

// Disables WM_INPUT messages for the mouse
//
static void disableRawMouseMotion(_GLFWwindow* window)
{
    const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };

    if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
    {
        _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
                             "Win32: Failed to remove raw input device");
    }
}

// Apply disabled cursor mode to a focused window
//
static void disableCursor(_GLFWwindow* window)
{
300
301
302
303
304
    _glfw.win32.disabledCursorWindow = window;
    _glfwPlatformGetCursorPos(window,
                              &_glfw.win32.restoreCursorPosX,
                              &_glfw.win32.restoreCursorPosY);
    updateCursorImage(window);
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
305
    _glfwCenterCursorInContentArea(window);
306
307
    updateClipRect(window);

308
309
    if (window->rawMouseMotion)
        enableRawMouseMotion(window);
310
311
312
313
314
315
}

// Exit disabled cursor mode for the specified window
//
static void enableCursor(_GLFWwindow* window)
{
316
317
    if (window->rawMouseMotion)
        disableRawMouseMotion(window);
318
319
320
321
322
323
324
325
326

    _glfw.win32.disabledCursorWindow = NULL;
    updateClipRect(NULL);
    _glfwPlatformSetCursorPos(window,
                              _glfw.win32.restoreCursorPosX,
                              _glfw.win32.restoreCursorPosY);
    updateCursorImage(window);
}

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
327
// Returns whether the cursor is in the content area of the specified window
328
//
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
329
static GLFWbool cursorInContentArea(_GLFWwindow* window)
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
{
    RECT area;
    POINT pos;

    if (!GetCursorPos(&pos))
        return GLFW_FALSE;

    if (WindowFromPoint(pos) != window->win32.handle)
        return GLFW_FALSE;

    GetClientRect(window->win32.handle, &area);
    ClientToScreen(window->win32.handle, (POINT*) &area.left);
    ClientToScreen(window->win32.handle, (POINT*) &area.right);

    return PtInRect(&area, pos);
}

Camilla Löwy's avatar
Camilla Löwy committed
347
348
349
350
351
352
353
354
355
356
// Update native window styles to match attributes
//
static void updateWindowStyles(const _GLFWwindow* window)
{
    RECT rect;
    DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
    style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP);
    style |= getWindowStyle(window);

    GetClientRect(window->win32.handle, &rect);
357
358
359
360
361
362
363
364
365
366

    if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
    {
        AdjustWindowRectExForDpi(&rect, style, FALSE,
                                 getWindowExStyle(window),
                                 GetDpiForWindow(window->win32.handle));
    }
    else
        AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));

Camilla Löwy's avatar
Camilla Löwy committed
367
368
369
370
371
372
373
374
375
    ClientToScreen(window->win32.handle, (POINT*) &rect.left);
    ClientToScreen(window->win32.handle, (POINT*) &rect.right);
    SetWindowLongW(window->win32.handle, GWL_STYLE, style);
    SetWindowPos(window->win32.handle, HWND_TOP,
                 rect.left, rect.top,
                 rect.right - rect.left, rect.bottom - rect.top,
                 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
}

376
377
378
379
// Update window framebuffer transparency
//
static void updateFramebufferTransparency(const _GLFWwindow* window)
{
Camilla Löwy's avatar
Camilla Löwy committed
380
381
    BOOL enabled;

382
383
384
    if (!IsWindowsVistaOrGreater())
        return;

Camilla Löwy's avatar
Camilla Löwy committed
385
    if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    {
        HRGN region = CreateRectRgn(0, 0, -1, -1);
        DWM_BLURBEHIND bb = {0};
        bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
        bb.hRgnBlur = region;
        bb.fEnable = TRUE;

        if (SUCCEEDED(DwmEnableBlurBehindWindow(window->win32.handle, &bb)))
        {
            // Decorated windows don't repaint the transparent background
            // leaving a trail behind animations
            // HACK: Making the window layered with a transparency color key
            //       seems to fix this.  Normally, when specifying
            //       a transparency color key to be used when composing the
            //       layered window, all pixels painted by the window in this
            //       color will be transparent.  That doesn't seem to be the
            //       case anymore, at least when used with blur behind window
            //       plus negative region.
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
404
405
406
            LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
            exStyle |= WS_EX_LAYERED;
            SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
407
408
409
410
411
412
413
414
415
416
417
418

            // Using a color key not equal to black to fix the trailing
            // issue.  When set to black, something is making the hit test
            // not resize with the window frame.
            SetLayeredWindowAttributes(window->win32.handle,
                                       RGB(0, 193, 48), 255, LWA_COLORKEY);
        }

        DeleteObject(region);
    }
    else
    {
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
419
420
421
        LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
        exStyle &= ~WS_EX_LAYERED;
        SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
422
423
424
425
426
        RedrawWindow(window->win32.handle, NULL, NULL,
                     RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
    }
}

427
428
429
430
431
432
// Retrieves and translates modifier keys
//
static int getKeyMods(void)
{
    int mods = 0;

433
    if (GetKeyState(VK_SHIFT) & 0x8000)
434
        mods |= GLFW_MOD_SHIFT;
435
    if (GetKeyState(VK_CONTROL) & 0x8000)
436
        mods |= GLFW_MOD_CONTROL;
437
    if (GetKeyState(VK_MENU) & 0x8000)
438
        mods |= GLFW_MOD_ALT;
439
    if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
Noel Cower's avatar
Noel Cower committed
440
        mods |= GLFW_MOD_SUPER;
441
442
443
444
    if (GetKeyState(VK_CAPITAL) & 1)
        mods |= GLFW_MOD_CAPS_LOCK;
    if (GetKeyState(VK_NUMLOCK) & 1)
        mods |= GLFW_MOD_NUM_LOCK;
445
446
447
448
449
450
451
452
453
454

    return mods;
}

// Retrieves and translates modifier keys
//
static int getAsyncKeyMods(void)
{
    int mods = 0;

455
    if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
456
        mods |= GLFW_MOD_SHIFT;
457
    if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
458
        mods |= GLFW_MOD_CONTROL;
459
    if (GetAsyncKeyState(VK_MENU) & 0x8000)
460
        mods |= GLFW_MOD_ALT;
461
    if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000)
Noel Cower's avatar
Noel Cower committed
462
        mods |= GLFW_MOD_SUPER;
463
464
465
466
    if (GetAsyncKeyState(VK_CAPITAL) & 1)
        mods |= GLFW_MOD_CAPS_LOCK;
    if (GetAsyncKeyState(VK_NUMLOCK) & 1)
        mods |= GLFW_MOD_NUM_LOCK;
467
468
469
470

    return mods;
}

Camilla Berglund's avatar
Camilla Berglund committed
471
// Translates a Windows key to the corresponding GLFW key
472
//
Camilla Berglund's avatar
Camilla Berglund committed
473
static int translateKey(WPARAM wParam, LPARAM lParam)
Camilla Berglund's avatar
Camilla Berglund committed
474
{
Camilla Löwy's avatar
Camilla Löwy committed
475
    // The Ctrl keys require special handling
476
    if (wParam == VK_CONTROL)
Camilla Berglund's avatar
Camilla Berglund committed
477
    {
478
479
        MSG next;
        DWORD time;
Camilla Berglund's avatar
Camilla Berglund committed
480

Camilla Löwy's avatar
Camilla Löwy committed
481
        // Right side keys have the extended key bit set
482
483
        if (lParam & 0x01000000)
            return GLFW_KEY_RIGHT_CONTROL;
Camilla Berglund's avatar
Camilla Berglund committed
484

Camilla Löwy's avatar
Camilla Löwy committed
485
486
487
        // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence
        //       We only want the Right Alt message, so if the next message is
        //       Right Alt we ignore this (synthetic) Left Ctrl message
488
489
490
491
492
493
494
495
        time = GetMessageTime();

        if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
        {
            if (next.message == WM_KEYDOWN ||
                next.message == WM_SYSKEYDOWN ||
                next.message == WM_KEYUP ||
                next.message == WM_SYSKEYUP)
Camilla Berglund's avatar
Camilla Berglund committed
496
            {
497
498
499
                if (next.wParam == VK_MENU &&
                    (next.lParam & 0x01000000) &&
                    next.time == time)
Camilla Berglund's avatar
Camilla Berglund committed
500
                {
Camilla Löwy's avatar
Camilla Löwy committed
501
                    // Next message is Right Alt down so discard this
502
                    return _GLFW_KEY_INVALID;
Camilla Berglund's avatar
Camilla Berglund committed
503
504
505
506
                }
            }
        }

507
        return GLFW_KEY_LEFT_CONTROL;
Camilla Berglund's avatar
Camilla Berglund committed
508
    }
509

510
511
512
513
514
515
516
    if (wParam == VK_PROCESSKEY)
    {
        // IME notifies that keys have been filtered by setting the virtual
        // key-code to VK_PROCESSKEY
        return _GLFW_KEY_INVALID;
    }

Camilla Berglund's avatar
Cleanup    
Camilla Berglund committed
517
    return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF];
Camilla Berglund's avatar
Camilla Berglund committed
518
519
}

520
521
522
523
524
525
526
527
528
529
530
531
static void fitToMonitor(_GLFWwindow* window)
{
    MONITORINFO mi = { sizeof(mi) };
    GetMonitorInfo(window->monitor->win32.handle, &mi);
    SetWindowPos(window->win32.handle, HWND_TOPMOST,
                 mi.rcMonitor.left,
                 mi.rcMonitor.top,
                 mi.rcMonitor.right - mi.rcMonitor.left,
                 mi.rcMonitor.bottom - mi.rcMonitor.top,
                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
}

532
// Make the specified window and its video mode active on its monitor
533
//
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
534
static void acquireMonitor(_GLFWwindow* window)
535
{
536
    if (!_glfw.win32.acquiredMonitorCount)
537
    {
538
        SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
539
540
541
542
543
544
545
546
547
548

        // HACK: When mouse trails are enabled the cursor becomes invisible when
        //       the OpenGL ICD switches to page flipping
        if (IsWindowsXPOrGreater())
        {
            SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
            SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0);
        }
    }

549
550
551
    if (!window->monitor->window)
        _glfw.win32.acquiredMonitorCount++;

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
552
    _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
553
    _glfwInputMonitorWindow(window->monitor, window);
554
555
}

556
// Remove the window and restore the original video mode
557
//
558
static void releaseMonitor(_GLFWwindow* window)
559
{
560
561
562
    if (window->monitor->window != window)
        return;

563
564
    _glfw.win32.acquiredMonitorCount--;
    if (!_glfw.win32.acquiredMonitorCount)
565
    {
566
567
        SetThreadExecutionState(ES_CONTINUOUS);

568
569
570
571
572
        // HACK: Restore mouse trail length saved in acquireMonitor
        if (IsWindowsXPOrGreater())
            SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
    }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
573
    _glfwInputMonitorWindow(window->monitor, NULL);
574
    _glfwRestoreVideoModeWin32(window->monitor);
575
576
}

Camilla Berglund's avatar
Camilla Berglund committed
577
// Window callback function (handles window messages)
578
//
Camilla Berglund's avatar
Camilla Berglund committed
579
580
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
                                   WPARAM wParam, LPARAM lParam)
Camilla Berglund's avatar
Camilla Berglund committed
581
{
582
    _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
583
    if (!window)
Camilla Berglund's avatar
Camilla Berglund committed
584
    {
585
        // This is the message handling for the hidden helper window
586
        // and for a regular window during its initial creation
587

Camilla Berglund's avatar
Camilla Berglund committed
588
        switch (uMsg)
Camilla Berglund's avatar
Camilla Berglund committed
589
        {
590
591
592
593
594
595
596
597
            case WM_NCCREATE:
            {
                if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
                    EnableNonClientDpiScaling(hWnd);

                break;
            }

598
599
600
601
            case WM_DISPLAYCHANGE:
                _glfwPollMonitorsWin32();
                break;

Camilla Berglund's avatar
Camilla Berglund committed
602
            case WM_DEVICECHANGE:
Camilla Berglund's avatar
Camilla Berglund committed
603
            {
604
                if (wParam == DBT_DEVICEARRIVAL)
Camilla Berglund's avatar
Camilla Berglund committed
605
606
                {
                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
607
608
                    if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                        _glfwDetectJoystickConnectionWin32();
Camilla Berglund's avatar
Camilla Berglund committed
609
610
611
612
                }
                else if (wParam == DBT_DEVICEREMOVECOMPLETE)
                {
                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
613
614
                    if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                        _glfwDetectJoystickDisconnectionWin32();
Camilla Berglund's avatar
Camilla Berglund committed
615
616
617
                }

                break;
Camilla Berglund's avatar
Camilla Berglund committed
618
            }
Camilla Berglund's avatar
Camilla Berglund committed
619
620
        }

621
622
623
624
625
        return DefWindowProcW(hWnd, uMsg, wParam, lParam);
    }

    switch (uMsg)
    {
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
        case WM_MOUSEACTIVATE:
        {
            // HACK: Postpone cursor disabling when the window was activated by
            //       clicking a caption button
            if (HIWORD(lParam) == WM_LBUTTONDOWN)
            {
                if (LOWORD(lParam) == HTCLOSE ||
                    LOWORD(lParam) == HTMINBUTTON ||
                    LOWORD(lParam) == HTMAXBUTTON)
                {
                    window->win32.frameAction = GLFW_TRUE;
                }
            }

            break;
        }

        case WM_CAPTURECHANGED:
        {
            // HACK: Disable the cursor once the caption button action has been
            //       completed or cancelled
            if (lParam == 0 && window->win32.frameAction)
            {
                if (window->cursorMode == GLFW_CURSOR_DISABLED)
650
                    disableCursor(window);
651
652
653
654
655
656
657

                window->win32.frameAction = GLFW_FALSE;
            }

            break;
        }

658
        case WM_SETFOCUS:
Camilla Berglund's avatar
Camilla Berglund committed
659
        {
660
661
            _glfwInputWindowFocus(window, GLFW_TRUE);

662
663
664
665
666
            // HACK: Do not disable cursor while the user is interacting with
            //       a caption button
            if (window->win32.frameAction)
                break;

667
            if (window->cursorMode == GLFW_CURSOR_DISABLED)
668
                disableCursor(window);
Camilla Berglund's avatar
Camilla Berglund committed
669
670
671
672

            return 0;
        }

673
        case WM_KILLFOCUS:
674
        {
675
            if (window->cursorMode == GLFW_CURSOR_DISABLED)
676
                enableCursor(window);
677
678
679

            if (window->monitor && window->autoIconify)
                _glfwPlatformIconifyWindow(window);
680

681
            _glfwInputWindowFocus(window, GLFW_FALSE);
682
683
684
            return 0;
        }

Camilla Berglund's avatar
Camilla Berglund committed
685
686
        case WM_SYSCOMMAND:
        {
Camilla Berglund's avatar
Camilla Berglund committed
687
            switch (wParam & 0xfff0)
Camilla Berglund's avatar
Camilla Berglund committed
688
689
690
691
            {
                case SC_SCREENSAVE:
                case SC_MONITORPOWER:
                {
692
                    if (window->monitor)
Camilla Berglund's avatar
Camilla Berglund committed
693
                    {
694
                        // We are running in full screen mode, so disallow
Camilla Berglund's avatar
Camilla Berglund committed
695
                        // screen saver and screen blanking
Camilla Berglund's avatar
Camilla Berglund committed
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
                        return 0;
                    }
                    else
                        break;
                }

                // User trying to access application menu using ALT?
                case SC_KEYMENU:
                    return 0;
            }
            break;
        }

        case WM_CLOSE:
        {
711
            _glfwInputWindowCloseRequest(window);
Camilla Berglund's avatar
Camilla Berglund committed
712
713
714
            return 0;
        }

715
716
717
718
719
720
        case WM_INPUTLANGCHANGE:
        {
            _glfwUpdateKeyNamesWin32();
            break;
        }

Camilla Berglund's avatar
Camilla Berglund committed
721
        case WM_CHAR:
722
        case WM_SYSCHAR:
723
724
        case WM_UNICHAR:
        {
725
            const GLFWbool plain = (uMsg != WM_SYSCHAR);
726

727
            if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR)
728
            {
729
730
                // WM_UNICHAR is not sent by Windows, but is sent by some
                // third-party input method engine
731
732
733
734
                // Returning TRUE here announces support for this message
                return TRUE;
            }

735
736
737
738
739
740
            _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
            return 0;
        }

        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
Camilla Berglund's avatar
Camilla Berglund committed
741
742
743
        case WM_KEYUP:
        case WM_SYSKEYUP:
        {
744
            const int key = translateKey(wParam, lParam);
745
746
747
748
            const int scancode = (lParam >> 16) & 0x1ff;
            const int action = ((lParam >> 31) & 1) ? GLFW_RELEASE : GLFW_PRESS;
            const int mods = getKeyMods();

749
750
            if (key == _GLFW_KEY_INVALID)
                break;
751

752
            if (action == GLFW_RELEASE && wParam == VK_SHIFT)
Camilla Berglund's avatar
Camilla Berglund committed
753
            {
Camilla Löwy's avatar
Camilla Löwy committed
754
755
756
                // HACK: Release both Shift keys on Shift up event, as when both
                //       are pressed the first release does not emit any event
                // NOTE: The other half of this is in _glfwPlatformPollEvents
757
758
                _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
                _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
Camilla Berglund's avatar
Camilla Berglund committed
759
            }
760
761
            else if (wParam == VK_SNAPSHOT)
            {
Camilla Löwy's avatar
Camilla Löwy committed
762
                // HACK: Key down is not reported for the Print Screen key
763
764
                _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
                _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
765
            }
Camilla Berglund's avatar
Camilla Berglund committed
766
            else
767
                _glfwInputKey(window, key, scancode, action, mods);
Camilla Berglund's avatar
Camilla Berglund committed
768

769
            break;
Camilla Berglund's avatar
Camilla Berglund committed
770
771
772
773
774
        }

        case WM_LBUTTONDOWN:
        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
775
776
        case WM_XBUTTONDOWN:
        case WM_LBUTTONUP:
Camilla Berglund's avatar
Camilla Berglund committed
777
778
        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
779
        case WM_XBUTTONUP:
Camilla Berglund's avatar
Camilla Berglund committed
780
        {
781
            int i, button, action;
782
783
784
785
786
787
788
789
790
791
792

            if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
                button = GLFW_MOUSE_BUTTON_LEFT;
            else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
                button = GLFW_MOUSE_BUTTON_RIGHT;
            else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
                button = GLFW_MOUSE_BUTTON_MIDDLE;
            else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
                button = GLFW_MOUSE_BUTTON_4;
            else
                button = GLFW_MOUSE_BUTTON_5;
Camilla Berglund's avatar
Camilla Berglund committed
793

794
795
796
797
798
            if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
                uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
            {
                action = GLFW_PRESS;
            }
799
            else
800
                action = GLFW_RELEASE;
801

802
            for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
803
804
805
            {
                if (window->mouseButtons[i] == GLFW_PRESS)
                    break;
806
            }
807

808
            if (i > GLFW_MOUSE_BUTTON_LAST)
809
810
                SetCapture(hWnd);

811
812
            _glfwInputMouseClick(window, button, action, getKeyMods());

813
            for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
814
815
816
817
818
            {
                if (window->mouseButtons[i] == GLFW_PRESS)
                    break;
            }

819
            if (i > GLFW_MOUSE_BUTTON_LAST)
820
821
                ReleaseCapture();

822
            if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
823
                return TRUE;
Camilla Berglund's avatar
Camilla Berglund committed
824

825
            return 0;
Camilla Berglund's avatar
Camilla Berglund committed
826
827
828
829
        }

        case WM_MOUSEMOVE:
        {
830
831
            const int x = GET_X_LPARAM(lParam);
            const int y = GET_Y_LPARAM(lParam);
Camilla Berglund's avatar
Camilla Berglund committed
832

833
            // Disabled cursor motion input is provided by WM_INPUT
834
            if (window->cursorMode == GLFW_CURSOR_DISABLED)
835
836
837
            {
                const int dx = x - window->win32.lastCursorPosX;
                const int dy = y - window->win32.lastCursorPosY;
838
839
840
841
842
843

                if (_glfw.win32.disabledCursorWindow != window)
                    break;
                if (window->rawMouseMotion)
                    break;

844
845
846
847
848
849
                _glfwInputCursorPos(window,
                                    window->virtualCursorPosX + dx,
                                    window->virtualCursorPosY + dy);
            }
            else
                _glfwInputCursorPos(window, x, y);
850

851
852
            window->win32.lastCursorPosX = x;
            window->win32.lastCursorPosY = y;
Camilla Berglund's avatar
Camilla Berglund committed
853

854
            if (!window->win32.cursorTracked)
855
856
857
858
859
            {
                TRACKMOUSEEVENT tme;
                ZeroMemory(&tme, sizeof(tme));
                tme.cbSize = sizeof(tme);
                tme.dwFlags = TME_LEAVE;
860
                tme.hwndTrack = window->win32.handle;
861
862
                TrackMouseEvent(&tme);

863
864
                window->win32.cursorTracked = GLFW_TRUE;
                _glfwInputCursorEnter(window, GLFW_TRUE);
865
866
867
868
869
            }

            return 0;
        }

870
871
        case WM_INPUT:
        {
872
            UINT size = 0;
873
            HRAWINPUT ri = (HRAWINPUT) lParam;
874
            RAWINPUT* data = NULL;
875
876
            int dx, dy;

877
878
879
            if (_glfw.win32.disabledCursorWindow != window)
                break;
            if (!window->rawMouseMotion)
880
881
882
                break;

            GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
Camilla Löwy's avatar
Camilla Löwy committed
883
            if (size > (UINT) _glfw.win32.rawInputSize)
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
            {
                free(_glfw.win32.rawInput);
                _glfw.win32.rawInput = calloc(size, 1);
                _glfw.win32.rawInputSize = size;
            }

            size = _glfw.win32.rawInputSize;
            if (GetRawInputData(ri, RID_INPUT,
                                _glfw.win32.rawInput, &size,
                                sizeof(RAWINPUTHEADER)) == (UINT) -1)
            {
                _glfwInputError(GLFW_PLATFORM_ERROR,
                                "Win32: Failed to retrieve raw input data");
                break;
            }

            data = _glfw.win32.rawInput;
            if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
            {
                dx = data->data.mouse.lLastX - window->win32.lastCursorPosX;
                dy = data->data.mouse.lLastY - window->win32.lastCursorPosY;
            }
            else
            {
                dx = data->data.mouse.lLastX;
                dy = data->data.mouse.lLastY;
            }

            _glfwInputCursorPos(window,
                                window->virtualCursorPosX + dx,
                                window->virtualCursorPosY + dy);

            window->win32.lastCursorPosX += dx;
            window->win32.lastCursorPosY += dy;
            break;
        }

921
922
        case WM_MOUSELEAVE:
        {
923
924
            window->win32.cursorTracked = GLFW_FALSE;
            _glfwInputCursorEnter(window, GLFW_FALSE);
Camilla Berglund's avatar
Camilla Berglund committed
925
926
927
928
929
            return 0;
        }

        case WM_MOUSEWHEEL:
        {
930
            _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
931
932
            return 0;
        }
Camilla Berglund's avatar
Camilla Berglund committed
933

934
935
936
        case WM_MOUSEHWHEEL:
        {
            // This message is only sent on Windows Vista and later
937
            // NOTE: The X-axis is inverted for consistency with macOS and X11
938
            _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
Camilla Berglund's avatar
Camilla Berglund committed
939
            return 0;
Camilla Berglund's avatar
Camilla Berglund committed
940
941
        }

942
943
944
        case WM_ENTERSIZEMOVE:
        case WM_ENTERMENULOOP:
        {
945
946
            // HACK: Enable the cursor while the user is moving or
            //       resizing the window or using the window menu
947
            if (window->cursorMode == GLFW_CURSOR_DISABLED)
948
                enableCursor(window);
949
950
951
952
953
954
955

            break;
        }

        case WM_EXITSIZEMOVE:
        case WM_EXITMENULOOP:
        {
956
957
            // HACK: Disable the cursor once the user is done moving or
            //       resizing the window or using the menu
958
            if (window->cursorMode == GLFW_CURSOR_DISABLED)
959
                disableCursor(window);
960
961
962
963

            break;
        }

Camilla Berglund's avatar
Camilla Berglund committed
964
965
        case WM_SIZE:
        {
966
967
968
969
            const GLFWbool iconified = wParam == SIZE_MINIMIZED;
            const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
                                       (window->win32.maximized &&
                                        wParam != SIZE_RESTORED);
970

971
972
            if (_glfw.win32.disabledCursorWindow == window)
                updateClipRect(window);
Camilla Berglund's avatar
Camilla Berglund committed
973

974
975
976
977
978
            if (window->win32.iconified != iconified)
                _glfwInputWindowIconify(window, iconified);

            if (window->win32.maximized != maximized)
                _glfwInputWindowMaximize(window, maximized);
979
980
981
982

            _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam));
            _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam));

983
            if (window->monitor && window->win32.iconified != iconified)
984
            {
985
                if (iconified)
986
                    releaseMonitor(window);
987
                else
988
                {
989
                    acquireMonitor(window);
990
991
                    fitToMonitor(window);
                }
992
            }
Camilla Berglund's avatar
Camilla Berglund committed
993

994
995
            window->win32.iconified = iconified;
            window->win32.maximized = maximized;
Camilla Berglund's avatar
Camilla Berglund committed
996
997
998
999
1000
            return 0;
        }

        case WM_MOVE:
        {
1001
1002
            if (_glfw.win32.disabledCursorWindow == window)
                updateClipRect(window);
1003

Camilla Berglund's avatar
Camilla Berglund committed
1004
1005
            // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
            // those macros do not handle negative window positions correctly
1006
1007
1008
            _glfwInputWindowPos(window,
                                GET_X_LPARAM(lParam),
                                GET_Y_LPARAM(lParam));
Camilla Berglund's avatar
Camilla Berglund committed
1009
1010
1011
            return 0;
        }

1012
1013
        case WM_SIZING:
        {
Camilla Berglund's avatar
Camilla Berglund committed
1014
1015
            if (window->numer == GLFW_DONT_CARE ||
                window->denom == GLFW_DONT_CARE)
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
            {
                break;
            }

            applyAspectRatio(window, (int) wParam, (RECT*) lParam);
            return TRUE;
        }

        case WM_GETMINMAXINFO:
        {
            int xoff, yoff;
1027
            UINT dpi = USER_DEFAULT_SCREEN_DPI;
1028
            MINMAXINFO* mmi = (MINMAXINFO*) lParam;
1029

Camilla Berglund's avatar
Camilla Berglund committed
1030
1031
1032
            if (window->monitor)
                break;

1033
1034
1035
            if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
                dpi = GetDpiForWindow(window->win32.handle);

1036
            getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
1037
                              0, 0, &xoff, &yoff, dpi);
1038

Camilla Berglund's avatar
Camilla Berglund committed
1039
1040
            if (window->minwidth != GLFW_DONT_CARE &&
                window->minheight != GLFW_DONT_CARE)
1041
            {
Camilla Berglund's avatar
Camilla Berglund committed
1042
1043
                mmi->ptMinTrackSize.x = window->minwidth + xoff;
                mmi->ptMinTrackSize.y = window->minheight + yoff;
1044
1045
            }

Camilla Berglund's avatar
Camilla Berglund committed
1046
1047
            if (window->maxwidth != GLFW_DONT_CARE &&
                window->maxheight != GLFW_DONT_CARE)
1048
            {
Camilla Berglund's avatar
Camilla Berglund committed
1049
1050
                mmi->ptMaxTrackSize.x = window->maxwidth + xoff;
                mmi->ptMaxTrackSize.y = window->maxheight + yoff;
1051
1052
            }

1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
            if (!window->decorated)
            {
                MONITORINFO mi;
                const HMONITOR mh = MonitorFromWindow(window->win32.handle,
                                                      MONITOR_DEFAULTTONEAREST);

                ZeroMemory(&mi, sizeof(mi));
                mi.cbSize = sizeof(mi);
                GetMonitorInfo(mh, &mi);

                mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
                mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
                mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
                mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
            }

1069
1070
1071
            return 0;
        }

Camilla Berglund's avatar
Camilla Berglund committed
1072
1073
        case WM_PAINT:
        {
1074
            _glfwInputWindowDamage(window);
Camilla Berglund's avatar
Camilla Berglund committed
1075
1076
1077
            break;
        }

Camilla Berglund's avatar
Camilla Berglund committed
1078
1079
1080
1081
1082
        case WM_ERASEBKGND:
        {
            return TRUE;
        }

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
        case WM_NCACTIVATE:
        case WM_NCPAINT:
        {
            // Prevent title bar from being drawn after restoring a minimized
            // undecorated window
            if (!window->decorated)
                return TRUE;

            break;
        }

1094
1095
1096
1097
1098
1099
1100
        case WM_DWMCOMPOSITIONCHANGED:
        {
            if (window->win32.transparent)
                updateFramebufferTransparency(window);
            return 0;
        }

1101
1102
        case WM_GETDPISCALEDSIZE:
        {
Camilla Löwy's avatar
Camilla Löwy committed
1103
1104
1105
            if (window->win32.scaleToMonitor)
                break;

Camilla Löwy's avatar
Cleanup    
Camilla Löwy committed
1106
            // Adjust the window size to keep the content area size constant
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
            if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
            {
                RECT source = {0}, target = {0};
                SIZE* size = (SIZE*) lParam;

                AdjustWindowRectExForDpi(&source, getWindowStyle(window),
                                         FALSE, getWindowExStyle(window),
                                         GetDpiForWindow(window->win32.handle));
                AdjustWindowRectExForDpi(&target, getWindowStyle(window),
                                         FALSE, getWindowExStyle(window),
                                         LOWORD(wParam));

                size->cx += (target.right - target.left) -
                            (source.right - source.left);
                size->cy += (target.bottom - target.top) -
                            (source.bottom - source.top);
                return TRUE;
            }

            break;
        }

1129
1130
        case WM_DPICHANGED:
        {
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
            const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
            const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;

            // Only apply the suggested size if the OS is new enough to have
            // sent a WM_GETDPISCALEDSIZE before this
            if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32())
            {
                RECT* suggested = (RECT*) lParam;
                SetWindowPos(window->win32.handle, HWND_TOP,
                             suggested->left,
                             suggested->top,
                             suggested->right - suggested->left,
                             suggested->bottom - suggested->top,
                             SWP_NOACTIVATE | SWP_NOZORDER);
            }

1147
1148
1149
1150
            _glfwInputWindowContentScale(window, xscale, yscale);
            break;
        }

1151
1152
        case WM_SETCURSOR:
        {
1153
            if (LOWORD(lParam) == HTCLIENT)
1154
            {
1155
                updateCursorImage(window);
1156
                return TRUE;
1157
1158
1159
1160
1161
            }

            break;
        }

arturo's avatar
arturo committed
1162
1163
        case WM_DROPFILES:
        {
Camilla Berglund's avatar
Camilla Berglund committed
1164
            HDROP drop = (HDROP) wParam;
Camilla Berglund's avatar
Camilla Berglund committed
1165
            POINT pt;
1166
1167
            int i;

Camilla Berglund's avatar
Camilla Berglund committed
1168
            const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
1169
            char** paths = calloc(count, sizeof(char*));
Camilla Berglund's avatar
Camilla Berglund committed
1170
1171

            // Move the mouse to the position of the drop
Camilla Berglund's avatar
Camilla Berglund committed
1172
            DragQueryPoint(drop, &pt);
1173
            _glfwInputCursorPos(window, pt.x, pt.y);
Camilla Berglund's avatar
Camilla Berglund committed
1174

1175
            for (i = 0;  i < count;  i++)
Camilla Berglund's avatar
Camilla Berglund committed
1176
            {
Camilla Berglund's avatar
Camilla Berglund committed
1177
                const UINT length = DragQueryFileW(drop, i, NULL, 0);
1178
                WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR));
Camilla Berglund's avatar
Camilla Berglund committed
1179

Camilla Berglund's avatar
Camilla Berglund committed
1180
                DragQueryFileW(drop, i, buffer, length + 1);
1181
                paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
Camilla Berglund's avatar
Camilla Berglund committed
1182

1183
                free(buffer);
Camilla Berglund's avatar
Camilla Berglund committed
1184
1185
            }

1186
            _glfwInputDrop(window, count, (const char**) paths);
1187
1188

            for (i = 0;  i < count;  i++)
1189
1190
                free(paths[i]);
            free(paths);
1191

Camilla Berglund's avatar
Camilla Berglund committed