mirror of
https://github.com/glfw/glfw.git
synced 2024-11-15 02:34:36 +00:00
de550b60e5
Operations that take an instance handle should be passed the handle of
whatever module we are inside instead of blindly passing the handle of
the executable.
This commit makes GLFW retrieve its own instance on initialization.
This makes the most difference for window classes, which are
per-instance. Using the executable instance led to name conflicts if
there were several copies of GLFW in a single process.
Note that having this is still a bad idea unless you know what things to
avoid, and those things are mostly platform-specific. This is partly
because the library wasn't designed for it and partly because it needs
to save, update and restore various per-process and per-session settings
like current context and video mode.
However, multiple simultaneous copies of GLFW in a single Win32 process
should now at least initialize, like is already the case on other
platforms.
Fixes #469
Fixes #1296
Fixes #1395
Related to #927
Related to #1885
(cherry picked from commit 07a5518c3e
)
2377 lines
72 KiB
C
2377 lines
72 KiB
C
//========================================================================
|
|
// GLFW 3.3 Win32 - www.glfw.org
|
|
//------------------------------------------------------------------------
|
|
// Copyright (c) 2002-2006 Marcus Geelnard
|
|
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
|
//
|
|
// 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.
|
|
//
|
|
//========================================================================
|
|
// Please use C89 style variable declarations in this file because VS 2010
|
|
//========================================================================
|
|
|
|
#include "internal.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <windowsx.h>
|
|
#include <shellapi.h>
|
|
|
|
// Returns the window style for the specified window
|
|
//
|
|
static DWORD getWindowStyle(const _GLFWwindow* window)
|
|
{
|
|
DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
|
|
|
|
if (window->monitor)
|
|
style |= WS_POPUP;
|
|
else
|
|
{
|
|
style |= WS_SYSMENU | WS_MINIMIZEBOX;
|
|
|
|
if (window->decorated)
|
|
{
|
|
style |= WS_CAPTION;
|
|
|
|
if (window->resizable)
|
|
style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
|
|
}
|
|
else
|
|
style |= WS_POPUP;
|
|
}
|
|
|
|
return style;
|
|
}
|
|
|
|
// Returns the extended window style for the specified window
|
|
//
|
|
static DWORD getWindowExStyle(const _GLFWwindow* window)
|
|
{
|
|
DWORD style = WS_EX_APPWINDOW;
|
|
|
|
if (window->monitor || window->floating)
|
|
style |= WS_EX_TOPMOST;
|
|
|
|
return style;
|
|
}
|
|
|
|
// 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));
|
|
bi.bV5Size = sizeof(bi);
|
|
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)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create RGBA bitmap");
|
|
return NULL;
|
|
}
|
|
|
|
mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
|
|
if (!mask)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create mask bitmap");
|
|
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)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create icon");
|
|
}
|
|
else
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create cursor");
|
|
}
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
// Translate content area size to full window size according to styles and DPI
|
|
//
|
|
static void getFullWindowSize(DWORD style, DWORD exStyle,
|
|
int contentWidth, int contentHeight,
|
|
int* fullWidth, int* fullHeight,
|
|
UINT dpi)
|
|
{
|
|
RECT rect = { 0, 0, contentWidth, contentHeight };
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
|
|
else
|
|
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
|
|
|
*fullWidth = rect.right - rect.left;
|
|
*fullHeight = rect.bottom - rect.top;
|
|
}
|
|
|
|
// Enforce the content area aspect ratio based on which edge is being dragged
|
|
//
|
|
static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
|
|
{
|
|
int xoff, yoff;
|
|
UINT dpi = USER_DEFAULT_SCREEN_DPI;
|
|
const float ratio = (float) window->numer / (float) window->denom;
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
dpi = GetDpiForWindow(window->win32.handle);
|
|
|
|
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
|
0, 0, &xoff, &yoff, dpi);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Updates the cursor image according to its cursor mode
|
|
//
|
|
static void updateCursorImage(_GLFWwindow* window)
|
|
{
|
|
if (window->cursorMode == GLFW_CURSOR_NORMAL)
|
|
{
|
|
if (window->cursor)
|
|
SetCursor(window->cursor->win32.handle);
|
|
else
|
|
SetCursor(LoadCursorW(NULL, IDC_ARROW));
|
|
}
|
|
else
|
|
SetCursor(NULL);
|
|
}
|
|
|
|
// Updates the cursor clip rect
|
|
//
|
|
static void updateClipRect(_GLFWwindow* window)
|
|
{
|
|
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);
|
|
}
|
|
|
|
// Enables WM_INPUT messages for the mouse for the specified window
|
|
//
|
|
static void enableRawMouseMotion(_GLFWwindow* window)
|
|
{
|
|
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
|
|
|
|
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)
|
|
{
|
|
_glfw.win32.disabledCursorWindow = window;
|
|
_glfwPlatformGetCursorPos(window,
|
|
&_glfw.win32.restoreCursorPosX,
|
|
&_glfw.win32.restoreCursorPosY);
|
|
updateCursorImage(window);
|
|
_glfwCenterCursorInContentArea(window);
|
|
updateClipRect(window);
|
|
|
|
if (window->rawMouseMotion)
|
|
enableRawMouseMotion(window);
|
|
}
|
|
|
|
// Exit disabled cursor mode for the specified window
|
|
//
|
|
static void enableCursor(_GLFWwindow* window)
|
|
{
|
|
if (window->rawMouseMotion)
|
|
disableRawMouseMotion(window);
|
|
|
|
_glfw.win32.disabledCursorWindow = NULL;
|
|
updateClipRect(NULL);
|
|
_glfwPlatformSetCursorPos(window,
|
|
_glfw.win32.restoreCursorPosX,
|
|
_glfw.win32.restoreCursorPosY);
|
|
updateCursorImage(window);
|
|
}
|
|
|
|
// Returns whether the cursor is in the content area of the specified window
|
|
//
|
|
static GLFWbool cursorInContentArea(_GLFWwindow* window)
|
|
{
|
|
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);
|
|
}
|
|
|
|
// 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);
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, style, FALSE,
|
|
getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
|
|
|
|
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);
|
|
}
|
|
|
|
// Update window framebuffer transparency
|
|
//
|
|
static void updateFramebufferTransparency(const _GLFWwindow* window)
|
|
{
|
|
BOOL composition, opaque;
|
|
DWORD color;
|
|
|
|
if (!IsWindowsVistaOrGreater())
|
|
return;
|
|
|
|
if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
|
|
return;
|
|
|
|
if (IsWindows8OrGreater() ||
|
|
(SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque))
|
|
{
|
|
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;
|
|
|
|
DwmEnableBlurBehindWindow(window->win32.handle, &bb);
|
|
DeleteObject(region);
|
|
}
|
|
else
|
|
{
|
|
// HACK: Disable framebuffer transparency on Windows 7 when the
|
|
// colorization color is opaque, because otherwise the window
|
|
// contents is blended additively with the previous frame instead
|
|
// of replacing it
|
|
DWM_BLURBEHIND bb = {0};
|
|
bb.dwFlags = DWM_BB_ENABLE;
|
|
DwmEnableBlurBehindWindow(window->win32.handle, &bb);
|
|
}
|
|
}
|
|
|
|
// Retrieves and translates modifier keys
|
|
//
|
|
static int getKeyMods(void)
|
|
{
|
|
int mods = 0;
|
|
|
|
if (GetKeyState(VK_SHIFT) & 0x8000)
|
|
mods |= GLFW_MOD_SHIFT;
|
|
if (GetKeyState(VK_CONTROL) & 0x8000)
|
|
mods |= GLFW_MOD_CONTROL;
|
|
if (GetKeyState(VK_MENU) & 0x8000)
|
|
mods |= GLFW_MOD_ALT;
|
|
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
|
|
mods |= GLFW_MOD_SUPER;
|
|
if (GetKeyState(VK_CAPITAL) & 1)
|
|
mods |= GLFW_MOD_CAPS_LOCK;
|
|
if (GetKeyState(VK_NUMLOCK) & 1)
|
|
mods |= GLFW_MOD_NUM_LOCK;
|
|
|
|
return mods;
|
|
}
|
|
|
|
static void fitToMonitor(_GLFWwindow* window)
|
|
{
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
GetMonitorInfoW(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);
|
|
}
|
|
|
|
// Make the specified window and its video mode active on its monitor
|
|
//
|
|
static void acquireMonitor(_GLFWwindow* window)
|
|
{
|
|
if (!_glfw.win32.acquiredMonitorCount)
|
|
{
|
|
SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
|
|
|
|
// HACK: When mouse trails are enabled the cursor becomes invisible when
|
|
// the OpenGL ICD switches to page flipping
|
|
if (IsWindowsXPOrGreater())
|
|
{
|
|
SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
|
|
SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
if (!window->monitor->window)
|
|
_glfw.win32.acquiredMonitorCount++;
|
|
|
|
_glfwSetVideoModeWin32(window->monitor, &window->videoMode);
|
|
_glfwInputMonitorWindow(window->monitor, window);
|
|
}
|
|
|
|
// Remove the window and restore the original video mode
|
|
//
|
|
static void releaseMonitor(_GLFWwindow* window)
|
|
{
|
|
if (window->monitor->window != window)
|
|
return;
|
|
|
|
_glfw.win32.acquiredMonitorCount--;
|
|
if (!_glfw.win32.acquiredMonitorCount)
|
|
{
|
|
SetThreadExecutionState(ES_CONTINUOUS);
|
|
|
|
// HACK: Restore mouse trail length saved in acquireMonitor
|
|
if (IsWindowsXPOrGreater())
|
|
SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
|
|
}
|
|
|
|
_glfwInputMonitorWindow(window->monitor, NULL);
|
|
_glfwRestoreVideoModeWin32(window->monitor);
|
|
}
|
|
|
|
// Manually maximize the window, for when SW_MAXIMIZE cannot be used
|
|
//
|
|
static void maximizeWindowManually(_GLFWwindow* window)
|
|
{
|
|
RECT rect;
|
|
DWORD style;
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
|
|
GetMonitorInfoW(MonitorFromWindow(window->win32.handle,
|
|
MONITOR_DEFAULTTONEAREST), &mi);
|
|
|
|
rect = mi.rcWork;
|
|
|
|
if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
|
|
{
|
|
rect.right = _glfw_min(rect.right, rect.left + window->maxwidth);
|
|
rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight);
|
|
}
|
|
|
|
style = GetWindowLongW(window->win32.handle, GWL_STYLE);
|
|
style |= WS_MAXIMIZE;
|
|
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
|
|
|
|
if (window->decorated)
|
|
{
|
|
const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
const UINT dpi = GetDpiForWindow(window->win32.handle);
|
|
AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
|
|
OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
|
OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION));
|
|
}
|
|
|
|
rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom);
|
|
}
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
|
|
// Window callback function (handles window messages)
|
|
//
|
|
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
_GLFWwindow* window = GetPropW(hWnd, L"GLFW");
|
|
if (!window)
|
|
{
|
|
// This is the message handling for the hidden helper window
|
|
// and for a regular window during its initial creation
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam;
|
|
const _GLFWwndconfig* wndconfig = cs->lpCreateParams;
|
|
|
|
// On per-monitor DPI aware V1 systems, only enable
|
|
// non-client scaling for windows that scale the client area
|
|
// We need WM_GETDPISCALEDSIZE from V2 to keep the client
|
|
// area static when the non-client area is scaled
|
|
if (wndconfig && wndconfig->scaleToMonitor)
|
|
EnableNonClientDpiScaling(hWnd);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
_glfwPollMonitorsWin32();
|
|
break;
|
|
|
|
case WM_DEVICECHANGE:
|
|
{
|
|
if (wParam == DBT_DEVICEARRIVAL)
|
|
{
|
|
DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
|
|
if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
|
_glfwDetectJoystickConnectionWin32();
|
|
}
|
|
else if (wParam == DBT_DEVICEREMOVECOMPLETE)
|
|
{
|
|
DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
|
|
if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
|
_glfwDetectJoystickDisconnectionWin32();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
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) != HTCLIENT)
|
|
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)
|
|
disableCursor(window);
|
|
|
|
window->win32.frameAction = GLFW_FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_SETFOCUS:
|
|
{
|
|
_glfwInputWindowFocus(window, GLFW_TRUE);
|
|
|
|
// HACK: Do not disable cursor while the user is interacting with
|
|
// a caption button
|
|
if (window->win32.frameAction)
|
|
break;
|
|
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
disableCursor(window);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_KILLFOCUS:
|
|
{
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
enableCursor(window);
|
|
|
|
if (window->monitor && window->autoIconify)
|
|
_glfwPlatformIconifyWindow(window);
|
|
|
|
_glfwInputWindowFocus(window, GLFW_FALSE);
|
|
return 0;
|
|
}
|
|
|
|
case WM_SYSCOMMAND:
|
|
{
|
|
switch (wParam & 0xfff0)
|
|
{
|
|
case SC_SCREENSAVE:
|
|
case SC_MONITORPOWER:
|
|
{
|
|
if (window->monitor)
|
|
{
|
|
// We are running in full screen mode, so disallow
|
|
// screen saver and screen blanking
|
|
return 0;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
// User trying to access application menu using ALT?
|
|
case SC_KEYMENU:
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CLOSE:
|
|
{
|
|
_glfwInputWindowCloseRequest(window);
|
|
return 0;
|
|
}
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
{
|
|
_glfwUpdateKeyNamesWin32();
|
|
break;
|
|
}
|
|
|
|
case WM_CHAR:
|
|
case WM_SYSCHAR:
|
|
{
|
|
if (wParam >= 0xd800 && wParam <= 0xdbff)
|
|
window->win32.highSurrogate = (WCHAR) wParam;
|
|
else
|
|
{
|
|
uint32_t codepoint = 0;
|
|
|
|
if (wParam >= 0xdc00 && wParam <= 0xdfff)
|
|
{
|
|
if (window->win32.highSurrogate)
|
|
{
|
|
codepoint += (window->win32.highSurrogate - 0xd800) << 10;
|
|
codepoint += (WCHAR) wParam - 0xdc00;
|
|
codepoint += 0x10000;
|
|
}
|
|
}
|
|
else
|
|
codepoint = (WCHAR) wParam;
|
|
|
|
window->win32.highSurrogate = 0;
|
|
_glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_UNICHAR:
|
|
{
|
|
if (wParam == UNICODE_NOCHAR)
|
|
{
|
|
// WM_UNICHAR is not sent by Windows, but is sent by some
|
|
// third-party input method engine
|
|
// Returning TRUE here announces support for this message
|
|
return TRUE;
|
|
}
|
|
|
|
_glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE);
|
|
return 0;
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYUP:
|
|
{
|
|
int key, scancode;
|
|
const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
|
|
const int mods = getKeyMods();
|
|
|
|
scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
|
|
if (!scancode)
|
|
{
|
|
// NOTE: Some synthetic key messages have a scancode of zero
|
|
// HACK: Map the virtual key back to a usable scancode
|
|
scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC);
|
|
}
|
|
|
|
// HACK: Alt+PrtSc has a different scancode than just PrtSc
|
|
if (scancode == 0x54)
|
|
scancode = 0x137;
|
|
|
|
// HACK: Ctrl+Pause has a different scancode than just Pause
|
|
if (scancode == 0x146)
|
|
scancode = 0x45;
|
|
|
|
key = _glfw.win32.keycodes[scancode];
|
|
|
|
// The Ctrl keys require special handling
|
|
if (wParam == VK_CONTROL)
|
|
{
|
|
if (HIWORD(lParam) & KF_EXTENDED)
|
|
{
|
|
// Right side keys have the extended key bit set
|
|
key = GLFW_KEY_RIGHT_CONTROL;
|
|
}
|
|
else
|
|
{
|
|
// NOTE: Alt Gr sends Left Ctrl followed by Right Alt
|
|
// HACK: We only want one event for Alt Gr, so if we detect
|
|
// this sequence we discard this Left Ctrl message now
|
|
// and later report Right Alt normally
|
|
MSG next;
|
|
const DWORD 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)
|
|
{
|
|
if (next.wParam == VK_MENU &&
|
|
(HIWORD(next.lParam) & KF_EXTENDED) &&
|
|
next.time == time)
|
|
{
|
|
// Next message is Right Alt down so discard this
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a regular Left Ctrl message
|
|
key = GLFW_KEY_LEFT_CONTROL;
|
|
}
|
|
}
|
|
else if (wParam == VK_PROCESSKEY)
|
|
{
|
|
// IME notifies that keys have been filtered by setting the
|
|
// virtual key-code to VK_PROCESSKEY
|
|
break;
|
|
}
|
|
|
|
if (action == GLFW_RELEASE && wParam == VK_SHIFT)
|
|
{
|
|
// 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
|
|
_glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
|
|
_glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
|
|
}
|
|
else if (wParam == VK_SNAPSHOT)
|
|
{
|
|
// HACK: Key down is not reported for the Print Screen key
|
|
_glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
|
|
_glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
|
|
}
|
|
else
|
|
_glfwInputKey(window, key, scancode, action, mods);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_XBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_XBUTTONUP:
|
|
{
|
|
int i, button, action;
|
|
|
|
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;
|
|
|
|
if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
|
|
uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
|
|
{
|
|
action = GLFW_PRESS;
|
|
}
|
|
else
|
|
action = GLFW_RELEASE;
|
|
|
|
for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
|
|
{
|
|
if (window->mouseButtons[i] == GLFW_PRESS)
|
|
break;
|
|
}
|
|
|
|
if (i > GLFW_MOUSE_BUTTON_LAST)
|
|
SetCapture(hWnd);
|
|
|
|
_glfwInputMouseClick(window, button, action, getKeyMods());
|
|
|
|
for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++)
|
|
{
|
|
if (window->mouseButtons[i] == GLFW_PRESS)
|
|
break;
|
|
}
|
|
|
|
if (i > GLFW_MOUSE_BUTTON_LAST)
|
|
ReleaseCapture();
|
|
|
|
if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
|
|
return TRUE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
const int x = GET_X_LPARAM(lParam);
|
|
const int y = GET_Y_LPARAM(lParam);
|
|
|
|
if (!window->win32.cursorTracked)
|
|
{
|
|
TRACKMOUSEEVENT tme;
|
|
ZeroMemory(&tme, sizeof(tme));
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = window->win32.handle;
|
|
TrackMouseEvent(&tme);
|
|
|
|
window->win32.cursorTracked = GLFW_TRUE;
|
|
_glfwInputCursorEnter(window, GLFW_TRUE);
|
|
}
|
|
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
{
|
|
const int dx = x - window->win32.lastCursorPosX;
|
|
const int dy = y - window->win32.lastCursorPosY;
|
|
|
|
if (_glfw.win32.disabledCursorWindow != window)
|
|
break;
|
|
if (window->rawMouseMotion)
|
|
break;
|
|
|
|
_glfwInputCursorPos(window,
|
|
window->virtualCursorPosX + dx,
|
|
window->virtualCursorPosY + dy);
|
|
}
|
|
else
|
|
_glfwInputCursorPos(window, x, y);
|
|
|
|
window->win32.lastCursorPosX = x;
|
|
window->win32.lastCursorPosY = y;
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_INPUT:
|
|
{
|
|
UINT size = 0;
|
|
HRAWINPUT ri = (HRAWINPUT) lParam;
|
|
RAWINPUT* data = NULL;
|
|
int dx, dy;
|
|
|
|
if (_glfw.win32.disabledCursorWindow != window)
|
|
break;
|
|
if (!window->rawMouseMotion)
|
|
break;
|
|
|
|
GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
|
|
if (size > (UINT) _glfw.win32.rawInputSize)
|
|
{
|
|
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;
|
|
}
|
|
|
|
case WM_MOUSELEAVE:
|
|
{
|
|
window->win32.cursorTracked = GLFW_FALSE;
|
|
_glfwInputCursorEnter(window, GLFW_FALSE);
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
_glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSEHWHEEL:
|
|
{
|
|
// This message is only sent on Windows Vista and later
|
|
// NOTE: The X-axis is inverted for consistency with macOS and X11
|
|
_glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
|
|
return 0;
|
|
}
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
case WM_ENTERMENULOOP:
|
|
{
|
|
if (window->win32.frameAction)
|
|
break;
|
|
|
|
// HACK: Enable the cursor while the user is moving or
|
|
// resizing the window or using the window menu
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
enableCursor(window);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
case WM_EXITMENULOOP:
|
|
{
|
|
if (window->win32.frameAction)
|
|
break;
|
|
|
|
// HACK: Disable the cursor once the user is done moving or
|
|
// resizing the window or using the menu
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
disableCursor(window);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
const int width = LOWORD(lParam);
|
|
const int height = HIWORD(lParam);
|
|
const GLFWbool iconified = wParam == SIZE_MINIMIZED;
|
|
const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
|
|
(window->win32.maximized &&
|
|
wParam != SIZE_RESTORED);
|
|
|
|
if (_glfw.win32.disabledCursorWindow == window)
|
|
updateClipRect(window);
|
|
|
|
if (window->win32.iconified != iconified)
|
|
_glfwInputWindowIconify(window, iconified);
|
|
|
|
if (window->win32.maximized != maximized)
|
|
_glfwInputWindowMaximize(window, maximized);
|
|
|
|
if (width != window->win32.width || height != window->win32.height)
|
|
{
|
|
window->win32.width = width;
|
|
window->win32.height = height;
|
|
|
|
_glfwInputFramebufferSize(window, width, height);
|
|
_glfwInputWindowSize(window, width, height);
|
|
}
|
|
|
|
if (window->monitor && window->win32.iconified != iconified)
|
|
{
|
|
if (iconified)
|
|
releaseMonitor(window);
|
|
else
|
|
{
|
|
acquireMonitor(window);
|
|
fitToMonitor(window);
|
|
}
|
|
}
|
|
|
|
window->win32.iconified = iconified;
|
|
window->win32.maximized = maximized;
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOVE:
|
|
{
|
|
if (_glfw.win32.disabledCursorWindow == window)
|
|
updateClipRect(window);
|
|
|
|
// NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
|
|
// those macros do not handle negative window positions correctly
|
|
_glfwInputWindowPos(window,
|
|
GET_X_LPARAM(lParam),
|
|
GET_Y_LPARAM(lParam));
|
|
return 0;
|
|
}
|
|
|
|
case WM_SIZING:
|
|
{
|
|
if (window->numer == GLFW_DONT_CARE ||
|
|
window->denom == GLFW_DONT_CARE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
applyAspectRatio(window, (int) wParam, (RECT*) lParam);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
int xoff, yoff;
|
|
UINT dpi = USER_DEFAULT_SCREEN_DPI;
|
|
MINMAXINFO* mmi = (MINMAXINFO*) lParam;
|
|
|
|
if (window->monitor)
|
|
break;
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
dpi = GetDpiForWindow(window->win32.handle);
|
|
|
|
getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
|
|
0, 0, &xoff, &yoff, dpi);
|
|
|
|
if (window->minwidth != GLFW_DONT_CARE &&
|
|
window->minheight != GLFW_DONT_CARE)
|
|
{
|
|
mmi->ptMinTrackSize.x = window->minwidth + xoff;
|
|
mmi->ptMinTrackSize.y = window->minheight + yoff;
|
|
}
|
|
|
|
if (window->maxwidth != GLFW_DONT_CARE &&
|
|
window->maxheight != GLFW_DONT_CARE)
|
|
{
|
|
mmi->ptMaxTrackSize.x = window->maxwidth + xoff;
|
|
mmi->ptMaxTrackSize.y = window->maxheight + yoff;
|
|
}
|
|
|
|
if (!window->decorated)
|
|
{
|
|
MONITORINFO mi;
|
|
const HMONITOR mh = MonitorFromWindow(window->win32.handle,
|
|
MONITOR_DEFAULTTONEAREST);
|
|
|
|
ZeroMemory(&mi, sizeof(mi));
|
|
mi.cbSize = sizeof(mi);
|
|
GetMonitorInfoW(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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_PAINT:
|
|
{
|
|
_glfwInputWindowDamage(window);
|
|
break;
|
|
}
|
|
|
|
case WM_ERASEBKGND:
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_NCACTIVATE:
|
|
case WM_NCPAINT:
|
|
{
|
|
// Prevent title bar from being drawn after restoring a minimized
|
|
// undecorated window
|
|
if (!window->decorated)
|
|
return TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DWMCOMPOSITIONCHANGED:
|
|
case WM_DWMCOLORIZATIONCOLORCHANGED:
|
|
{
|
|
if (window->win32.transparent)
|
|
updateFramebufferTransparency(window);
|
|
return 0;
|
|
}
|
|
|
|
case WM_GETDPISCALEDSIZE:
|
|
{
|
|
if (window->win32.scaleToMonitor)
|
|
break;
|
|
|
|
// Adjust the window size to keep the content area size constant
|
|
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;
|
|
}
|
|
|
|
case WM_DPICHANGED:
|
|
{
|
|
const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
|
|
const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
|
|
|
|
// Resize windowed mode windows that either permit rescaling or that
|
|
// need it to compensate for non-client area scaling
|
|
if (!window->monitor &&
|
|
(window->win32.scaleToMonitor ||
|
|
_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);
|
|
}
|
|
|
|
_glfwInputWindowContentScale(window, xscale, yscale);
|
|
break;
|
|
}
|
|
|
|
case WM_SETCURSOR:
|
|
{
|
|
if (LOWORD(lParam) == HTCLIENT)
|
|
{
|
|
updateCursorImage(window);
|
|
return TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DROPFILES:
|
|
{
|
|
HDROP drop = (HDROP) wParam;
|
|
POINT pt;
|
|
int i;
|
|
|
|
const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
|
|
char** paths = calloc(count, sizeof(char*));
|
|
|
|
// Move the mouse to the position of the drop
|
|
DragQueryPoint(drop, &pt);
|
|
_glfwInputCursorPos(window, pt.x, pt.y);
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
const UINT length = DragQueryFileW(drop, i, NULL, 0);
|
|
WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR));
|
|
|
|
DragQueryFileW(drop, i, buffer, length + 1);
|
|
paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
_glfwInputDrop(window, count, (const char**) paths);
|
|
|
|
for (i = 0; i < count; i++)
|
|
free(paths[i]);
|
|
free(paths);
|
|
|
|
DragFinish(drop);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// Creates the GLFW window
|
|
//
|
|
static int createNativeWindow(_GLFWwindow* window,
|
|
const _GLFWwndconfig* wndconfig,
|
|
const _GLFWfbconfig* fbconfig)
|
|
{
|
|
int xpos, ypos, fullWidth, fullHeight;
|
|
WCHAR* wideTitle;
|
|
DWORD style = getWindowStyle(window);
|
|
DWORD exStyle = getWindowExStyle(window);
|
|
|
|
if (window->monitor)
|
|
{
|
|
GLFWvidmode mode;
|
|
|
|
// NOTE: This window placement is temporary and approximate, as the
|
|
// correct position and size cannot be known until the monitor
|
|
// video mode has been picked in _glfwSetVideoModeWin32
|
|
_glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
|
|
_glfwPlatformGetVideoMode(window->monitor, &mode);
|
|
fullWidth = mode.width;
|
|
fullHeight = mode.height;
|
|
}
|
|
else
|
|
{
|
|
xpos = CW_USEDEFAULT;
|
|
ypos = CW_USEDEFAULT;
|
|
|
|
window->win32.maximized = wndconfig->maximized;
|
|
if (wndconfig->maximized)
|
|
style |= WS_MAXIMIZE;
|
|
|
|
getFullWindowSize(style, exStyle,
|
|
wndconfig->width, wndconfig->height,
|
|
&fullWidth, &fullHeight,
|
|
USER_DEFAULT_SCREEN_DPI);
|
|
}
|
|
|
|
wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
|
|
if (!wideTitle)
|
|
return GLFW_FALSE;
|
|
|
|
window->win32.handle = CreateWindowExW(exStyle,
|
|
_GLFW_WNDCLASSNAME,
|
|
wideTitle,
|
|
style,
|
|
xpos, ypos,
|
|
fullWidth, fullHeight,
|
|
NULL, // No parent window
|
|
NULL, // No window menu
|
|
_glfw.win32.instance,
|
|
(LPVOID) wndconfig);
|
|
|
|
free(wideTitle);
|
|
|
|
if (!window->win32.handle)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create window");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
SetPropW(window->win32.handle, L"GLFW", window);
|
|
|
|
if (IsWindows7OrGreater())
|
|
{
|
|
ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_DROPFILES, MSGFLT_ALLOW, NULL);
|
|
ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_COPYDATA, MSGFLT_ALLOW, NULL);
|
|
ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
|
|
}
|
|
|
|
window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
|
|
|
|
if (!window->monitor)
|
|
{
|
|
RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
|
|
WINDOWPLACEMENT wp = { sizeof(wp) };
|
|
const HMONITOR mh = MonitorFromWindow(window->win32.handle,
|
|
MONITOR_DEFAULTTONEAREST);
|
|
|
|
// Adjust window rect to account for DPI scaling of the window frame and
|
|
// (if enabled) DPI scaling of the content area
|
|
// This cannot be done until we know what monitor the window was placed on
|
|
// Only update the restored window rect as the window may be maximized
|
|
|
|
if (wndconfig->scaleToMonitor)
|
|
{
|
|
float xscale, yscale;
|
|
_glfwGetMonitorContentScaleWin32(mh, &xscale, &yscale);
|
|
|
|
if (xscale > 0.f && yscale > 0.f)
|
|
{
|
|
rect.right = (int) (rect.right * xscale);
|
|
rect.bottom = (int) (rect.bottom * yscale);
|
|
}
|
|
}
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
AdjustWindowRectEx(&rect, style, FALSE, exStyle);
|
|
|
|
GetWindowPlacement(window->win32.handle, &wp);
|
|
OffsetRect(&rect,
|
|
wp.rcNormalPosition.left - rect.left,
|
|
wp.rcNormalPosition.top - rect.top);
|
|
|
|
wp.rcNormalPosition = rect;
|
|
wp.showCmd = SW_HIDE;
|
|
SetWindowPlacement(window->win32.handle, &wp);
|
|
|
|
// Adjust rect of maximized undecorated window, because by default Windows will
|
|
// make such a window cover the whole monitor instead of its workarea
|
|
|
|
if (wndconfig->maximized && !wndconfig->decorated)
|
|
{
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
GetMonitorInfoW(mh, &mi);
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
mi.rcWork.left,
|
|
mi.rcWork.top,
|
|
mi.rcWork.right - mi.rcWork.left,
|
|
mi.rcWork.bottom - mi.rcWork.top,
|
|
SWP_NOACTIVATE | SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
DragAcceptFiles(window->win32.handle, TRUE);
|
|
|
|
if (fbconfig->transparent)
|
|
{
|
|
updateFramebufferTransparency(window);
|
|
window->win32.transparent = GLFW_TRUE;
|
|
}
|
|
|
|
_glfwPlatformGetWindowSize(window, &window->win32.width, &window->win32.height);
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW internal API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Registers the GLFW window class
|
|
//
|
|
GLFWbool _glfwRegisterWindowClassWin32(void)
|
|
{
|
|
WNDCLASSEXW wc;
|
|
|
|
ZeroMemory(&wc, sizeof(wc));
|
|
wc.cbSize = sizeof(wc);
|
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC) windowProc;
|
|
wc.hInstance = _glfw.win32.instance;
|
|
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
|
|
wc.lpszClassName = _GLFW_WNDCLASSNAME;
|
|
|
|
// Load user-provided icon if available
|
|
wc.hIcon = LoadImageW(GetModuleHandleW(NULL),
|
|
L"GLFW_ICON", IMAGE_ICON,
|
|
0, 0, LR_DEFAULTSIZE | LR_SHARED);
|
|
if (!wc.hIcon)
|
|
{
|
|
// No user-provided icon found, load default icon
|
|
wc.hIcon = LoadImageW(NULL,
|
|
IDI_APPLICATION, IMAGE_ICON,
|
|
0, 0, LR_DEFAULTSIZE | LR_SHARED);
|
|
}
|
|
|
|
if (!RegisterClassExW(&wc))
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to register window class");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
// Unregisters the GLFW window class
|
|
//
|
|
void _glfwUnregisterWindowClassWin32(void)
|
|
{
|
|
UnregisterClassW(_GLFW_WNDCLASSNAME, _glfw.win32.instance);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW platform API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|
const _GLFWwndconfig* wndconfig,
|
|
const _GLFWctxconfig* ctxconfig,
|
|
const _GLFWfbconfig* fbconfig)
|
|
{
|
|
if (!createNativeWindow(window, wndconfig, fbconfig))
|
|
return GLFW_FALSE;
|
|
|
|
if (ctxconfig->client != GLFW_NO_API)
|
|
{
|
|
if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
|
|
{
|
|
if (!_glfwInitWGL())
|
|
return GLFW_FALSE;
|
|
if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
|
|
return GLFW_FALSE;
|
|
}
|
|
else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
|
|
{
|
|
if (!_glfwInitEGL())
|
|
return GLFW_FALSE;
|
|
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
|
|
return GLFW_FALSE;
|
|
}
|
|
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
|
|
{
|
|
if (!_glfwInitOSMesa())
|
|
return GLFW_FALSE;
|
|
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
|
|
return GLFW_FALSE;
|
|
}
|
|
}
|
|
|
|
if (window->monitor)
|
|
{
|
|
_glfwPlatformShowWindow(window);
|
|
_glfwPlatformFocusWindow(window);
|
|
acquireMonitor(window);
|
|
fitToMonitor(window);
|
|
}
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|
{
|
|
if (window->monitor)
|
|
releaseMonitor(window);
|
|
|
|
if (window->context.destroy)
|
|
window->context.destroy(window);
|
|
|
|
if (_glfw.win32.disabledCursorWindow == window)
|
|
_glfw.win32.disabledCursorWindow = NULL;
|
|
|
|
if (window->win32.handle)
|
|
{
|
|
RemovePropW(window->win32.handle, L"GLFW");
|
|
DestroyWindow(window->win32.handle);
|
|
window->win32.handle = NULL;
|
|
}
|
|
|
|
if (window->win32.bigIcon)
|
|
DestroyIcon(window->win32.bigIcon);
|
|
|
|
if (window->win32.smallIcon)
|
|
DestroyIcon(window->win32.smallIcon);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
|
|
{
|
|
WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
|
|
if (!wideTitle)
|
|
return;
|
|
|
|
SetWindowTextW(window->win32.handle, wideTitle);
|
|
free(wideTitle);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
|
|
int count, const GLFWimage* images)
|
|
{
|
|
HICON bigIcon = NULL, smallIcon = NULL;
|
|
|
|
if (count)
|
|
{
|
|
const GLFWimage* bigImage = chooseImage(count, images,
|
|
GetSystemMetrics(SM_CXICON),
|
|
GetSystemMetrics(SM_CYICON));
|
|
const GLFWimage* smallImage = chooseImage(count, images,
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON));
|
|
|
|
bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
|
|
smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
|
|
}
|
|
else
|
|
{
|
|
bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON);
|
|
smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM);
|
|
}
|
|
|
|
SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
|
|
SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
|
|
|
|
if (window->win32.bigIcon)
|
|
DestroyIcon(window->win32.bigIcon);
|
|
|
|
if (window->win32.smallIcon)
|
|
DestroyIcon(window->win32.smallIcon);
|
|
|
|
if (count)
|
|
{
|
|
window->win32.bigIcon = bigIcon;
|
|
window->win32.smallIcon = smallIcon;
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
|
|
{
|
|
POINT pos = { 0, 0 };
|
|
ClientToScreen(window->win32.handle, &pos);
|
|
|
|
if (xpos)
|
|
*xpos = pos.x;
|
|
if (ypos)
|
|
*ypos = pos.y;
|
|
}
|
|
|
|
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
|
|
{
|
|
RECT rect = { xpos, ypos, xpos, ypos };
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window));
|
|
}
|
|
|
|
SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
|
|
}
|
|
|
|
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
|
{
|
|
RECT area;
|
|
GetClientRect(window->win32.handle, &area);
|
|
|
|
if (width)
|
|
*width = area.right;
|
|
if (height)
|
|
*height = area.bottom;
|
|
}
|
|
|
|
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
|
{
|
|
if (window->monitor)
|
|
{
|
|
if (window->monitor->window == window)
|
|
{
|
|
acquireMonitor(window);
|
|
fitToMonitor(window);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RECT rect = { 0, 0, width, height };
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window));
|
|
}
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
0, 0, rect.right - rect.left, rect.bottom - rect.top,
|
|
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
|
int minwidth, int minheight,
|
|
int maxwidth, int maxheight)
|
|
{
|
|
RECT area;
|
|
|
|
if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
|
|
(maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(window->win32.handle, &area);
|
|
MoveWindow(window->win32.handle,
|
|
area.left, area.top,
|
|
area.right - area.left,
|
|
area.bottom - area.top, TRUE);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
|
|
{
|
|
RECT area;
|
|
|
|
if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
|
|
return;
|
|
|
|
GetWindowRect(window->win32.handle, &area);
|
|
applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
|
|
MoveWindow(window->win32.handle,
|
|
area.left, area.top,
|
|
area.right - area.left,
|
|
area.bottom - area.top, TRUE);
|
|
}
|
|
|
|
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
|
|
{
|
|
_glfwPlatformGetWindowSize(window, width, height);
|
|
}
|
|
|
|
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
|
int* left, int* top,
|
|
int* right, int* bottom)
|
|
{
|
|
RECT rect;
|
|
int width, height;
|
|
|
|
_glfwPlatformGetWindowSize(window, &width, &height);
|
|
SetRect(&rect, 0, 0, width, height);
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window));
|
|
}
|
|
|
|
if (left)
|
|
*left = -rect.left;
|
|
if (top)
|
|
*top = -rect.top;
|
|
if (right)
|
|
*right = rect.right - width;
|
|
if (bottom)
|
|
*bottom = rect.bottom - height;
|
|
}
|
|
|
|
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
|
float* xscale, float* yscale)
|
|
{
|
|
const HANDLE handle = MonitorFromWindow(window->win32.handle,
|
|
MONITOR_DEFAULTTONEAREST);
|
|
_glfwGetMonitorContentScaleWin32(handle, xscale, yscale);
|
|
}
|
|
|
|
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_MINIMIZE);
|
|
}
|
|
|
|
void _glfwPlatformRestoreWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_RESTORE);
|
|
}
|
|
|
|
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
|
{
|
|
if (IsWindowVisible(window->win32.handle))
|
|
ShowWindow(window->win32.handle, SW_MAXIMIZE);
|
|
else
|
|
maximizeWindowManually(window);
|
|
}
|
|
|
|
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_SHOWNA);
|
|
}
|
|
|
|
void _glfwPlatformHideWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_HIDE);
|
|
}
|
|
|
|
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
|
|
{
|
|
FlashWindow(window->win32.handle, TRUE);
|
|
}
|
|
|
|
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
|
{
|
|
BringWindowToTop(window->win32.handle);
|
|
SetForegroundWindow(window->win32.handle);
|
|
SetFocus(window->win32.handle);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
|
_GLFWmonitor* monitor,
|
|
int xpos, int ypos,
|
|
int width, int height,
|
|
int refreshRate)
|
|
{
|
|
if (window->monitor == monitor)
|
|
{
|
|
if (monitor)
|
|
{
|
|
if (monitor->window == window)
|
|
{
|
|
acquireMonitor(window);
|
|
fitToMonitor(window);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RECT rect = { xpos, ypos, xpos + width, ypos + height };
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window));
|
|
}
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
rect.left, rect.top,
|
|
rect.right - rect.left, rect.bottom - rect.top,
|
|
SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (window->monitor)
|
|
releaseMonitor(window);
|
|
|
|
_glfwInputWindowMonitor(window, monitor);
|
|
|
|
if (window->monitor)
|
|
{
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
|
|
|
|
if (window->decorated)
|
|
{
|
|
DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
|
|
style &= ~WS_OVERLAPPEDWINDOW;
|
|
style |= getWindowStyle(window);
|
|
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
|
|
flags |= SWP_FRAMECHANGED;
|
|
}
|
|
|
|
acquireMonitor(window);
|
|
|
|
GetMonitorInfoW(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,
|
|
flags);
|
|
}
|
|
else
|
|
{
|
|
HWND after;
|
|
RECT rect = { xpos, ypos, xpos + width, ypos + height };
|
|
DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
|
|
UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
|
|
|
|
if (window->decorated)
|
|
{
|
|
style &= ~WS_POPUP;
|
|
style |= getWindowStyle(window);
|
|
SetWindowLongW(window->win32.handle, GWL_STYLE, style);
|
|
|
|
flags |= SWP_FRAMECHANGED;
|
|
}
|
|
|
|
if (window->floating)
|
|
after = HWND_TOPMOST;
|
|
else
|
|
after = HWND_NOTOPMOST;
|
|
|
|
if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32())
|
|
{
|
|
AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window),
|
|
GetDpiForWindow(window->win32.handle));
|
|
}
|
|
else
|
|
{
|
|
AdjustWindowRectEx(&rect, getWindowStyle(window),
|
|
FALSE, getWindowExStyle(window));
|
|
}
|
|
|
|
SetWindowPos(window->win32.handle, after,
|
|
rect.left, rect.top,
|
|
rect.right - rect.left, rect.bottom - rect.top,
|
|
flags);
|
|
}
|
|
}
|
|
|
|
int _glfwPlatformWindowFocused(_GLFWwindow* window)
|
|
{
|
|
return window->win32.handle == GetActiveWindow();
|
|
}
|
|
|
|
int _glfwPlatformWindowIconified(_GLFWwindow* window)
|
|
{
|
|
return IsIconic(window->win32.handle);
|
|
}
|
|
|
|
int _glfwPlatformWindowVisible(_GLFWwindow* window)
|
|
{
|
|
return IsWindowVisible(window->win32.handle);
|
|
}
|
|
|
|
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
|
|
{
|
|
return IsZoomed(window->win32.handle);
|
|
}
|
|
|
|
int _glfwPlatformWindowHovered(_GLFWwindow* window)
|
|
{
|
|
return cursorInContentArea(window);
|
|
}
|
|
|
|
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
|
|
{
|
|
BOOL composition, opaque;
|
|
DWORD color;
|
|
|
|
if (!window->win32.transparent)
|
|
return GLFW_FALSE;
|
|
|
|
if (!IsWindowsVistaOrGreater())
|
|
return GLFW_FALSE;
|
|
|
|
if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
|
|
return GLFW_FALSE;
|
|
|
|
if (!IsWindows8OrGreater())
|
|
{
|
|
// HACK: Disable framebuffer transparency on Windows 7 when the
|
|
// colorization color is opaque, because otherwise the window
|
|
// contents is blended additively with the previous frame instead
|
|
// of replacing it
|
|
if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque)
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
|
|
{
|
|
updateWindowStyles(window);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
|
|
{
|
|
updateWindowStyles(window);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
|
|
{
|
|
const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST;
|
|
SetWindowPos(window->win32.handle, after, 0, 0, 0, 0,
|
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
|
|
}
|
|
|
|
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
|
|
{
|
|
BYTE alpha;
|
|
DWORD flags;
|
|
|
|
if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
|
|
GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
|
|
{
|
|
if (flags & LWA_ALPHA)
|
|
return alpha / 255.f;
|
|
}
|
|
|
|
return 1.f;
|
|
}
|
|
|
|
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
|
{
|
|
if (opacity < 1.f)
|
|
{
|
|
const BYTE alpha = (BYTE) (255 * opacity);
|
|
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
|
|
style |= WS_EX_LAYERED;
|
|
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
|
|
SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
|
|
}
|
|
else
|
|
{
|
|
DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
|
|
style &= ~WS_EX_LAYERED;
|
|
SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style);
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
|
|
{
|
|
if (_glfw.win32.disabledCursorWindow != window)
|
|
return;
|
|
|
|
if (enabled)
|
|
enableRawMouseMotion(window);
|
|
else
|
|
disableRawMouseMotion(window);
|
|
}
|
|
|
|
GLFWbool _glfwPlatformRawMouseMotionSupported(void)
|
|
{
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformPollEvents(void)
|
|
{
|
|
MSG msg;
|
|
HWND handle;
|
|
_GLFWwindow* window;
|
|
|
|
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
// NOTE: While GLFW does not itself post WM_QUIT, other processes
|
|
// may post it to this one, for example Task Manager
|
|
// HACK: Treat WM_QUIT as a close on all windows
|
|
|
|
window = _glfw.windowListHead;
|
|
while (window)
|
|
{
|
|
_glfwInputWindowCloseRequest(window);
|
|
window = window->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
}
|
|
|
|
// HACK: Release modifier keys that the system did not emit KEYUP for
|
|
// NOTE: Shift keys on Windows tend to "stick" when both are pressed as
|
|
// no key up message is generated by the first key release
|
|
// NOTE: Windows key is not reported as released by the Win+V hotkey
|
|
// Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
|
|
// because they change the input focus
|
|
// NOTE: The other half of this is in the WM_*KEY* handler in windowProc
|
|
handle = GetActiveWindow();
|
|
if (handle)
|
|
{
|
|
window = GetPropW(handle, L"GLFW");
|
|
if (window)
|
|
{
|
|
int i;
|
|
const int keys[4][2] =
|
|
{
|
|
{ VK_LSHIFT, GLFW_KEY_LEFT_SHIFT },
|
|
{ VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT },
|
|
{ VK_LWIN, GLFW_KEY_LEFT_SUPER },
|
|
{ VK_RWIN, GLFW_KEY_RIGHT_SUPER }
|
|
};
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
const int vk = keys[i][0];
|
|
const int key = keys[i][1];
|
|
const int scancode = _glfw.win32.scancodes[key];
|
|
|
|
if ((GetKeyState(vk) & 0x8000))
|
|
continue;
|
|
if (window->keys[key] != GLFW_PRESS)
|
|
continue;
|
|
|
|
_glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
|
|
}
|
|
}
|
|
}
|
|
|
|
window = _glfw.win32.disabledCursorWindow;
|
|
if (window)
|
|
{
|
|
int width, height;
|
|
_glfwPlatformGetWindowSize(window, &width, &height);
|
|
|
|
// NOTE: Re-center the cursor only if it has moved since the last call,
|
|
// to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
|
|
if (window->win32.lastCursorPosX != width / 2 ||
|
|
window->win32.lastCursorPosY != height / 2)
|
|
{
|
|
_glfwPlatformSetCursorPos(window, width / 2, height / 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformWaitEvents(void)
|
|
{
|
|
WaitMessage();
|
|
|
|
_glfwPlatformPollEvents();
|
|
}
|
|
|
|
void _glfwPlatformWaitEventsTimeout(double timeout)
|
|
{
|
|
MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS);
|
|
|
|
_glfwPlatformPollEvents();
|
|
}
|
|
|
|
void _glfwPlatformPostEmptyEvent(void)
|
|
{
|
|
PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0);
|
|
}
|
|
|
|
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
|
|
{
|
|
POINT pos;
|
|
|
|
if (GetCursorPos(&pos))
|
|
{
|
|
ScreenToClient(window->win32.handle, &pos);
|
|
|
|
if (xpos)
|
|
*xpos = pos.x;
|
|
if (ypos)
|
|
*ypos = pos.y;
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos)
|
|
{
|
|
POINT pos = { (int) xpos, (int) ypos };
|
|
|
|
// Store the new position so it can be recognized later
|
|
window->win32.lastCursorPosX = pos.x;
|
|
window->win32.lastCursorPosY = pos.y;
|
|
|
|
ClientToScreen(window->win32.handle, &pos);
|
|
SetCursorPos(pos.x, pos.y);
|
|
}
|
|
|
|
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
|
|
{
|
|
if (mode == GLFW_CURSOR_DISABLED)
|
|
{
|
|
if (_glfwPlatformWindowFocused(window))
|
|
disableCursor(window);
|
|
}
|
|
else if (_glfw.win32.disabledCursorWindow == window)
|
|
enableCursor(window);
|
|
else if (cursorInContentArea(window))
|
|
updateCursorImage(window);
|
|
}
|
|
|
|
const char* _glfwPlatformGetScancodeName(int scancode)
|
|
{
|
|
if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) ||
|
|
_glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN)
|
|
{
|
|
_glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode);
|
|
return NULL;
|
|
}
|
|
|
|
return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]];
|
|
}
|
|
|
|
int _glfwPlatformGetKeyScancode(int key)
|
|
{
|
|
return _glfw.win32.scancodes[key];
|
|
}
|
|
|
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
|
const GLFWimage* image,
|
|
int xhot, int yhot)
|
|
{
|
|
cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
|
|
if (!cursor->win32.handle)
|
|
return GLFW_FALSE;
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
|
|
{
|
|
int id = 0;
|
|
|
|
if (shape == GLFW_ARROW_CURSOR)
|
|
id = OCR_NORMAL;
|
|
else if (shape == GLFW_IBEAM_CURSOR)
|
|
id = OCR_IBEAM;
|
|
else if (shape == GLFW_CROSSHAIR_CURSOR)
|
|
id = OCR_CROSS;
|
|
else if (shape == GLFW_HAND_CURSOR)
|
|
id = OCR_HAND;
|
|
else if (shape == GLFW_HRESIZE_CURSOR)
|
|
id = OCR_SIZEWE;
|
|
else if (shape == GLFW_VRESIZE_CURSOR)
|
|
id = OCR_SIZENS;
|
|
else
|
|
return GLFW_FALSE;
|
|
|
|
cursor->win32.handle = LoadImageW(NULL,
|
|
MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
|
|
LR_DEFAULTSIZE | LR_SHARED);
|
|
if (!cursor->win32.handle)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create standard cursor");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
return GLFW_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
|
|
{
|
|
if (cursor->win32.handle)
|
|
DestroyIcon((HICON) cursor->win32.handle);
|
|
}
|
|
|
|
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
|
{
|
|
if (cursorInContentArea(window))
|
|
updateCursorImage(window);
|
|
}
|
|
|
|
void _glfwPlatformSetClipboardString(const char* string)
|
|
{
|
|
int characterCount;
|
|
HANDLE object;
|
|
WCHAR* buffer;
|
|
|
|
characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
|
|
if (!characterCount)
|
|
return;
|
|
|
|
object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR));
|
|
if (!object)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to allocate global handle for clipboard");
|
|
return;
|
|
}
|
|
|
|
buffer = GlobalLock(object);
|
|
if (!buffer)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to lock global handle");
|
|
GlobalFree(object);
|
|
return;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
|
|
GlobalUnlock(object);
|
|
|
|
if (!OpenClipboard(_glfw.win32.helperWindowHandle))
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to open clipboard");
|
|
GlobalFree(object);
|
|
return;
|
|
}
|
|
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_UNICODETEXT, object);
|
|
CloseClipboard();
|
|
}
|
|
|
|
const char* _glfwPlatformGetClipboardString(void)
|
|
{
|
|
HANDLE object;
|
|
WCHAR* buffer;
|
|
|
|
if (!OpenClipboard(_glfw.win32.helperWindowHandle))
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to open clipboard");
|
|
return NULL;
|
|
}
|
|
|
|
object = GetClipboardData(CF_UNICODETEXT);
|
|
if (!object)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE,
|
|
"Win32: Failed to convert clipboard to string");
|
|
CloseClipboard();
|
|
return NULL;
|
|
}
|
|
|
|
buffer = GlobalLock(object);
|
|
if (!buffer)
|
|
{
|
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to lock global handle");
|
|
CloseClipboard();
|
|
return NULL;
|
|
}
|
|
|
|
free(_glfw.win32.clipboardString);
|
|
_glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer);
|
|
|
|
GlobalUnlock(object);
|
|
CloseClipboard();
|
|
|
|
return _glfw.win32.clipboardString;
|
|
}
|
|
|
|
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
|
|
{
|
|
if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface)
|
|
return;
|
|
|
|
extensions[0] = "VK_KHR_surface";
|
|
extensions[1] = "VK_KHR_win32_surface";
|
|
}
|
|
|
|
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
|
|
VkPhysicalDevice device,
|
|
uint32_t queuefamily)
|
|
{
|
|
PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
|
|
vkGetPhysicalDeviceWin32PresentationSupportKHR =
|
|
(PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
|
|
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
|
|
if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
|
|
{
|
|
_glfwInputError(GLFW_API_UNAVAILABLE,
|
|
"Win32: Vulkan instance missing VK_KHR_win32_surface extension");
|
|
return GLFW_FALSE;
|
|
}
|
|
|
|
return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
|
|
}
|
|
|
|
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|
_GLFWwindow* window,
|
|
const VkAllocationCallbacks* allocator,
|
|
VkSurfaceKHR* surface)
|
|
{
|
|
VkResult err;
|
|
VkWin32SurfaceCreateInfoKHR sci;
|
|
PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
|
|
|
|
vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
|
|
vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
|
|
if (!vkCreateWin32SurfaceKHR)
|
|
{
|
|
_glfwInputError(GLFW_API_UNAVAILABLE,
|
|
"Win32: Vulkan instance missing VK_KHR_win32_surface extension");
|
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
}
|
|
|
|
memset(&sci, 0, sizeof(sci));
|
|
sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
|
sci.hinstance = _glfw.win32.instance;
|
|
sci.hwnd = window->win32.handle;
|
|
|
|
err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
|
|
if (err)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to create Vulkan surface: %s",
|
|
_glfwGetVulkanResultString(err));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW native API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
|
{
|
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
return window->win32.handle;
|
|
}
|
|
|