mirror of
https://github.com/glfw/glfw.git
synced 2024-11-26 14:24:35 +00:00
25e7ff1196
By default, full screen windows that lose focus will be iconified and the video mode will be restored. This makes it impossible to create applications spanning multiple monitors. The GLFW_AUTO_ICONIFY window hint will allow users to disable this behavior. Fixes #143.
1370 lines
43 KiB
C
1370 lines
43 KiB
C
//========================================================================
|
|
// GLFW 3.1 Win32 - www.glfw.org
|
|
//------------------------------------------------------------------------
|
|
// Copyright (c) 2002-2006 Marcus Geelnard
|
|
// Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.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.
|
|
//
|
|
//========================================================================
|
|
|
|
#include "internal.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <windowsx.h>
|
|
#include <shellapi.h>
|
|
|
|
#define _GLFW_KEY_INVALID -2
|
|
|
|
|
|
// Updates the cursor clip rect
|
|
//
|
|
static void updateClipRect(_GLFWwindow* window)
|
|
{
|
|
RECT clipRect;
|
|
GetClientRect(window->win32.handle, &clipRect);
|
|
ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
|
|
ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
|
|
ClipCursor(&clipRect);
|
|
}
|
|
|
|
// Hide the mouse cursor
|
|
//
|
|
static void hideCursor(_GLFWwindow* window)
|
|
{
|
|
POINT pos;
|
|
|
|
ReleaseCapture();
|
|
ClipCursor(NULL);
|
|
|
|
if (window->win32.cursorHidden)
|
|
{
|
|
ShowCursor(TRUE);
|
|
window->win32.cursorHidden = GL_FALSE;
|
|
}
|
|
|
|
if (GetCursorPos(&pos))
|
|
{
|
|
if (WindowFromPoint(pos) == window->win32.handle)
|
|
SetCursor(NULL);
|
|
}
|
|
}
|
|
|
|
// Disable the mouse cursor
|
|
//
|
|
static void disableCursor(_GLFWwindow* window)
|
|
{
|
|
if (!window->win32.cursorHidden)
|
|
{
|
|
ShowCursor(FALSE);
|
|
window->win32.cursorHidden = GL_TRUE;
|
|
}
|
|
|
|
updateClipRect(window);
|
|
SetCapture(window->win32.handle);
|
|
}
|
|
|
|
// Restores the mouse cursor
|
|
//
|
|
static void restoreCursor(_GLFWwindow* window)
|
|
{
|
|
POINT pos;
|
|
|
|
ReleaseCapture();
|
|
ClipCursor(NULL);
|
|
|
|
if (window->win32.cursorHidden)
|
|
{
|
|
ShowCursor(TRUE);
|
|
window->win32.cursorHidden = GL_FALSE;
|
|
}
|
|
|
|
if (GetCursorPos(&pos))
|
|
{
|
|
if (WindowFromPoint(pos) == window->win32.handle)
|
|
{
|
|
if (window->cursor)
|
|
SetCursor(window->cursor->win32.handle);
|
|
else
|
|
SetCursor(LoadCursorW(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Retrieves and translates modifier keys
|
|
//
|
|
static int getKeyMods(void)
|
|
{
|
|
int mods = 0;
|
|
|
|
if (GetKeyState(VK_SHIFT) & (1 << 31))
|
|
mods |= GLFW_MOD_SHIFT;
|
|
if (GetKeyState(VK_CONTROL) & (1 << 31))
|
|
mods |= GLFW_MOD_CONTROL;
|
|
if (GetKeyState(VK_MENU) & (1 << 31))
|
|
mods |= GLFW_MOD_ALT;
|
|
if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31))
|
|
mods |= GLFW_MOD_SUPER;
|
|
|
|
return mods;
|
|
}
|
|
|
|
// Retrieves and translates modifier keys
|
|
//
|
|
static int getAsyncKeyMods(void)
|
|
{
|
|
int mods = 0;
|
|
|
|
if (GetAsyncKeyState(VK_SHIFT) & (1 << 31))
|
|
mods |= GLFW_MOD_SHIFT;
|
|
if (GetAsyncKeyState(VK_CONTROL) & (1 << 31))
|
|
mods |= GLFW_MOD_CONTROL;
|
|
if (GetAsyncKeyState(VK_MENU) & (1 << 31))
|
|
mods |= GLFW_MOD_ALT;
|
|
if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31))
|
|
mods |= GLFW_MOD_SUPER;
|
|
|
|
return mods;
|
|
}
|
|
|
|
// Translates a Windows key to the corresponding GLFW key
|
|
//
|
|
static int translateKey(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Check for numeric keypad keys
|
|
// NOTE: This way we always force "NumLock = ON", which is intentional since
|
|
// the returned key code should correspond to a physical location.
|
|
if ((HIWORD(lParam) & 0x100) == 0)
|
|
{
|
|
switch (MapVirtualKey(HIWORD(lParam) & 0xFF, 1))
|
|
{
|
|
case VK_INSERT: return GLFW_KEY_KP_0;
|
|
case VK_END: return GLFW_KEY_KP_1;
|
|
case VK_DOWN: return GLFW_KEY_KP_2;
|
|
case VK_NEXT: return GLFW_KEY_KP_3;
|
|
case VK_LEFT: return GLFW_KEY_KP_4;
|
|
case VK_CLEAR: return GLFW_KEY_KP_5;
|
|
case VK_RIGHT: return GLFW_KEY_KP_6;
|
|
case VK_HOME: return GLFW_KEY_KP_7;
|
|
case VK_UP: return GLFW_KEY_KP_8;
|
|
case VK_PRIOR: return GLFW_KEY_KP_9;
|
|
case VK_DIVIDE: return GLFW_KEY_KP_DIVIDE;
|
|
case VK_MULTIPLY: return GLFW_KEY_KP_MULTIPLY;
|
|
case VK_SUBTRACT: return GLFW_KEY_KP_SUBTRACT;
|
|
case VK_ADD: return GLFW_KEY_KP_ADD;
|
|
case VK_DELETE: return GLFW_KEY_KP_DECIMAL;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
switch (HIWORD(lParam) & 0xFF)
|
|
{
|
|
// handle printable chars except space in a language independent way,
|
|
// using scancodes rather than virtual keys
|
|
// as virtual keys are language dependent.
|
|
// Printable keys are mapped according to US layout.
|
|
|
|
// Row 0:
|
|
case 0x29: return GLFW_KEY_GRAVE_ACCENT;
|
|
case 0x02: return GLFW_KEY_1;
|
|
case 0x03: return GLFW_KEY_2;
|
|
case 0x04: return GLFW_KEY_3;
|
|
case 0x05: return GLFW_KEY_4;
|
|
case 0x06: return GLFW_KEY_5;
|
|
case 0x07: return GLFW_KEY_6;
|
|
case 0x08: return GLFW_KEY_7;
|
|
case 0x09: return GLFW_KEY_8;
|
|
case 0x0A: return GLFW_KEY_9;
|
|
case 0x0B: return GLFW_KEY_0;
|
|
case 0x0C: return GLFW_KEY_MINUS;
|
|
case 0x0D: return GLFW_KEY_EQUAL;
|
|
|
|
// Row 1:
|
|
case 0x10: return GLFW_KEY_Q;
|
|
case 0x11: return GLFW_KEY_W;
|
|
case 0x12: return GLFW_KEY_E;
|
|
case 0x13: return GLFW_KEY_R;
|
|
case 0x14: return GLFW_KEY_T;
|
|
case 0x15: return GLFW_KEY_Y;
|
|
case 0x16: return GLFW_KEY_U;
|
|
case 0x17: return GLFW_KEY_I;
|
|
case 0x18: return GLFW_KEY_O;
|
|
case 0x19: return GLFW_KEY_P;
|
|
case 0x1A: return GLFW_KEY_LEFT_BRACKET;
|
|
case 0x1B: return GLFW_KEY_RIGHT_BRACKET;
|
|
// We do not map 0x2B as this is only on US - use vKeys for this to prevent confusion with 0x56
|
|
|
|
// Row 2:
|
|
case 0x1E: return GLFW_KEY_A;
|
|
case 0x1F: return GLFW_KEY_S;
|
|
case 0x20: return GLFW_KEY_D;
|
|
case 0x21: return GLFW_KEY_F;
|
|
case 0x22: return GLFW_KEY_G;
|
|
case 0x23: return GLFW_KEY_H;
|
|
case 0x24: return GLFW_KEY_J;
|
|
case 0x25: return GLFW_KEY_K;
|
|
case 0x26: return GLFW_KEY_L;
|
|
case 0x27: return GLFW_KEY_SEMICOLON;
|
|
case 0x28: return GLFW_KEY_APOSTROPHE;
|
|
|
|
// Row 3:
|
|
case 0x2C: return GLFW_KEY_Z;
|
|
case 0x2D: return GLFW_KEY_X;
|
|
case 0x2E: return GLFW_KEY_C;
|
|
case 0x2F: return GLFW_KEY_V;
|
|
case 0x30: return GLFW_KEY_B;
|
|
case 0x31: return GLFW_KEY_N;
|
|
case 0x32: return GLFW_KEY_M;
|
|
case 0x33: return GLFW_KEY_COMMA;
|
|
case 0x34: return GLFW_KEY_PERIOD;
|
|
case 0x35: return GLFW_KEY_SLASH;
|
|
default: break;
|
|
}
|
|
|
|
// Check which key was pressed or released
|
|
switch (wParam)
|
|
{
|
|
// The SHIFT keys require special handling
|
|
case VK_SHIFT:
|
|
{
|
|
// Compare scan code for this key with that of VK_RSHIFT in
|
|
// order to determine which shift key was pressed (left or
|
|
// right)
|
|
const DWORD scancode = MapVirtualKey(VK_RSHIFT, 0);
|
|
if ((DWORD) ((lParam & 0x01ff0000) >> 16) == scancode)
|
|
return GLFW_KEY_RIGHT_SHIFT;
|
|
|
|
return GLFW_KEY_LEFT_SHIFT;
|
|
}
|
|
|
|
// The CTRL keys require special handling
|
|
case VK_CONTROL:
|
|
{
|
|
MSG next;
|
|
DWORD time;
|
|
|
|
// Is this an extended key (i.e. right key)?
|
|
if (lParam & 0x01000000)
|
|
return GLFW_KEY_RIGHT_CONTROL;
|
|
|
|
// Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only
|
|
// want the RALT message, so we try to see if the next message
|
|
// is a RALT message. In that case, this is a false LCTRL!
|
|
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 &&
|
|
(next.lParam & 0x01000000) &&
|
|
next.time == time)
|
|
{
|
|
// Next message is a RALT down message, which
|
|
// means that this is not a proper LCTRL message
|
|
return _GLFW_KEY_INVALID;
|
|
}
|
|
}
|
|
}
|
|
|
|
return GLFW_KEY_LEFT_CONTROL;
|
|
}
|
|
|
|
// The ALT keys require special handling
|
|
case VK_MENU:
|
|
{
|
|
// Is this an extended key (i.e. right key)?
|
|
if (lParam & 0x01000000)
|
|
return GLFW_KEY_RIGHT_ALT;
|
|
|
|
return GLFW_KEY_LEFT_ALT;
|
|
}
|
|
|
|
// The ENTER keys require special handling
|
|
case VK_RETURN:
|
|
{
|
|
// Is this an extended key (i.e. right key)?
|
|
if (lParam & 0x01000000)
|
|
return GLFW_KEY_KP_ENTER;
|
|
|
|
return GLFW_KEY_ENTER;
|
|
}
|
|
|
|
// Funcion keys (non-printable keys)
|
|
case VK_ESCAPE: return GLFW_KEY_ESCAPE;
|
|
case VK_TAB: return GLFW_KEY_TAB;
|
|
case VK_BACK: return GLFW_KEY_BACKSPACE;
|
|
case VK_HOME: return GLFW_KEY_HOME;
|
|
case VK_END: return GLFW_KEY_END;
|
|
case VK_PRIOR: return GLFW_KEY_PAGE_UP;
|
|
case VK_NEXT: return GLFW_KEY_PAGE_DOWN;
|
|
case VK_INSERT: return GLFW_KEY_INSERT;
|
|
case VK_DELETE: return GLFW_KEY_DELETE;
|
|
case VK_LEFT: return GLFW_KEY_LEFT;
|
|
case VK_UP: return GLFW_KEY_UP;
|
|
case VK_RIGHT: return GLFW_KEY_RIGHT;
|
|
case VK_DOWN: return GLFW_KEY_DOWN;
|
|
case VK_F1: return GLFW_KEY_F1;
|
|
case VK_F2: return GLFW_KEY_F2;
|
|
case VK_F3: return GLFW_KEY_F3;
|
|
case VK_F4: return GLFW_KEY_F4;
|
|
case VK_F5: return GLFW_KEY_F5;
|
|
case VK_F6: return GLFW_KEY_F6;
|
|
case VK_F7: return GLFW_KEY_F7;
|
|
case VK_F8: return GLFW_KEY_F8;
|
|
case VK_F9: return GLFW_KEY_F9;
|
|
case VK_F10: return GLFW_KEY_F10;
|
|
case VK_F11: return GLFW_KEY_F11;
|
|
case VK_F12: return GLFW_KEY_F12;
|
|
case VK_F13: return GLFW_KEY_F13;
|
|
case VK_F14: return GLFW_KEY_F14;
|
|
case VK_F15: return GLFW_KEY_F15;
|
|
case VK_F16: return GLFW_KEY_F16;
|
|
case VK_F17: return GLFW_KEY_F17;
|
|
case VK_F18: return GLFW_KEY_F18;
|
|
case VK_F19: return GLFW_KEY_F19;
|
|
case VK_F20: return GLFW_KEY_F20;
|
|
case VK_F21: return GLFW_KEY_F21;
|
|
case VK_F22: return GLFW_KEY_F22;
|
|
case VK_F23: return GLFW_KEY_F23;
|
|
case VK_F24: return GLFW_KEY_F24;
|
|
case VK_NUMLOCK: return GLFW_KEY_NUM_LOCK;
|
|
case VK_CAPITAL: return GLFW_KEY_CAPS_LOCK;
|
|
case VK_SNAPSHOT: return GLFW_KEY_PRINT_SCREEN;
|
|
case VK_SCROLL: return GLFW_KEY_SCROLL_LOCK;
|
|
case VK_PAUSE: return GLFW_KEY_PAUSE;
|
|
case VK_LWIN: return GLFW_KEY_LEFT_SUPER;
|
|
case VK_RWIN: return GLFW_KEY_RIGHT_SUPER;
|
|
case VK_APPS: return GLFW_KEY_MENU;
|
|
|
|
// Numeric keypad
|
|
case VK_NUMPAD0: return GLFW_KEY_KP_0;
|
|
case VK_NUMPAD1: return GLFW_KEY_KP_1;
|
|
case VK_NUMPAD2: return GLFW_KEY_KP_2;
|
|
case VK_NUMPAD3: return GLFW_KEY_KP_3;
|
|
case VK_NUMPAD4: return GLFW_KEY_KP_4;
|
|
case VK_NUMPAD5: return GLFW_KEY_KP_5;
|
|
case VK_NUMPAD6: return GLFW_KEY_KP_6;
|
|
case VK_NUMPAD7: return GLFW_KEY_KP_7;
|
|
case VK_NUMPAD8: return GLFW_KEY_KP_8;
|
|
case VK_NUMPAD9: return GLFW_KEY_KP_9;
|
|
case VK_DIVIDE: return GLFW_KEY_KP_DIVIDE;
|
|
case VK_MULTIPLY: return GLFW_KEY_KP_MULTIPLY;
|
|
case VK_SUBTRACT: return GLFW_KEY_KP_SUBTRACT;
|
|
case VK_ADD: return GLFW_KEY_KP_ADD;
|
|
case VK_DECIMAL: return GLFW_KEY_KP_DECIMAL;
|
|
|
|
// Printable keys are mapped according to US layout
|
|
case VK_SPACE: return GLFW_KEY_SPACE;
|
|
case 0xDC: return GLFW_KEY_BACKSLASH;
|
|
case 0xDF: return GLFW_KEY_WORLD_1;
|
|
case 0xE2: return GLFW_KEY_WORLD_2;
|
|
default: break;
|
|
}
|
|
|
|
// No matching translation was found
|
|
return GLFW_KEY_UNKNOWN;
|
|
}
|
|
|
|
// Window callback function (handles window events)
|
|
//
|
|
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
_GLFWwindow* window = (_GLFWwindow*) GetWindowLongPtrW(hWnd, 0);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
CREATESTRUCTW* cs = (CREATESTRUCTW*) lParam;
|
|
SetWindowLongPtrW(hWnd, 0, (LONG_PTR) cs->lpCreateParams);
|
|
break;
|
|
}
|
|
|
|
case WM_ACTIVATE:
|
|
{
|
|
// Window was (de)focused and/or (de)iconified
|
|
|
|
BOOL focused = LOWORD(wParam) != WA_INACTIVE;
|
|
BOOL iconified = HIWORD(wParam) ? TRUE : FALSE;
|
|
|
|
if (focused && iconified)
|
|
{
|
|
if (window->iconified && _glfw.focusedWindow != window)
|
|
{
|
|
// This is a workaround for window restoration using the
|
|
// Win+D hot key leading to windows being told they're
|
|
// focused and iconified and then never told they're
|
|
// restored
|
|
iconified = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// This is a workaround for window iconification using the
|
|
// taskbar leading to windows being told they're focused and
|
|
// iconified and then never told they're defocused
|
|
focused = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!focused && _glfw.focusedWindow == window)
|
|
{
|
|
// The window was defocused (or iconified, see above)
|
|
|
|
if (window->cursorMode != GLFW_CURSOR_NORMAL)
|
|
restoreCursor(window);
|
|
|
|
if (window->monitor && window->autoIconify)
|
|
{
|
|
if (!iconified)
|
|
{
|
|
// Iconify the (on top, borderless, oddly positioned)
|
|
// window or the user will be annoyed
|
|
_glfwPlatformIconifyWindow(window);
|
|
}
|
|
|
|
_glfwRestoreVideoMode(window->monitor);
|
|
}
|
|
}
|
|
else if (focused && _glfw.focusedWindow != window)
|
|
{
|
|
// The window was focused
|
|
|
|
if (window->cursorMode != GLFW_CURSOR_NORMAL)
|
|
_glfwPlatformApplyCursorMode(window);
|
|
|
|
if (window->monitor && window->autoIconify)
|
|
_glfwSetVideoMode(window->monitor, &window->videoMode);
|
|
}
|
|
|
|
_glfwInputWindowFocus(window, focused);
|
|
_glfwInputWindowIconify(window, iconified);
|
|
return 0;
|
|
}
|
|
|
|
case WM_ACTIVATEAPP:
|
|
{
|
|
if (!wParam && IsIconic(hWnd))
|
|
{
|
|
// This is a workaround for full screen windows losing focus
|
|
// through Alt+Tab leading to windows being told they're
|
|
// unfocused and restored and then never told they're iconified
|
|
_glfwInputWindowIconify(window, GL_TRUE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_SHOWWINDOW:
|
|
{
|
|
_glfwInputWindowVisibility(window, wParam ? GL_TRUE : GL_FALSE);
|
|
break;
|
|
}
|
|
|
|
case WM_SYSCOMMAND:
|
|
{
|
|
switch (wParam & 0xfff0)
|
|
{
|
|
case SC_SCREENSAVE:
|
|
case SC_MONITORPOWER:
|
|
{
|
|
if (window->monitor)
|
|
{
|
|
// We are running in fullscreen 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_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
const int scancode = (lParam >> 16) & 0x1ff;
|
|
const int key = translateKey(wParam, lParam);
|
|
if (key == _GLFW_KEY_INVALID)
|
|
break;
|
|
|
|
_glfwInputKey(window, key, scancode, GLFW_PRESS, getKeyMods());
|
|
break;
|
|
}
|
|
|
|
case WM_CHAR:
|
|
case WM_SYSCHAR:
|
|
{
|
|
_glfwInputChar(window, (unsigned int) wParam);
|
|
return 0;
|
|
}
|
|
|
|
case WM_UNICHAR:
|
|
{
|
|
// This message is not sent by Windows, but is sent by some
|
|
// third-party input method engines
|
|
|
|
if (wParam == UNICODE_NOCHAR)
|
|
{
|
|
// Returning TRUE here announces support for this message
|
|
return TRUE;
|
|
}
|
|
|
|
_glfwInputChar(window, (unsigned int) wParam);
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYUP:
|
|
{
|
|
const int mods = getKeyMods();
|
|
const int scancode = (lParam >> 16) & 0x1ff;
|
|
const int key = translateKey(wParam, lParam);
|
|
if (key == _GLFW_KEY_INVALID)
|
|
break;
|
|
|
|
if (wParam == VK_SHIFT)
|
|
{
|
|
// Release both Shift keys on Shift up event, as only one event
|
|
// is sent even if both keys are released
|
|
_glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, GLFW_RELEASE, mods);
|
|
_glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, GLFW_RELEASE, mods);
|
|
}
|
|
else if (wParam == VK_SNAPSHOT)
|
|
{
|
|
// 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, GLFW_RELEASE, mods);
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_XBUTTONDOWN:
|
|
{
|
|
const int mods = getKeyMods();
|
|
|
|
SetCapture(hWnd);
|
|
|
|
if (uMsg == WM_LBUTTONDOWN)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
|
|
else if (uMsg == WM_RBUTTONDOWN)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
|
|
else if (uMsg == WM_MBUTTONDOWN)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
|
|
else
|
|
{
|
|
if (HIWORD(wParam) == XBUTTON1)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_4, GLFW_PRESS, mods);
|
|
else if (HIWORD(wParam) == XBUTTON2)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_5, GLFW_PRESS, mods);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
case WM_XBUTTONUP:
|
|
{
|
|
const int mods = getKeyMods();
|
|
|
|
ReleaseCapture();
|
|
|
|
if (uMsg == WM_LBUTTONUP)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, mods);
|
|
else if (uMsg == WM_RBUTTONUP)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, mods);
|
|
else if (uMsg == WM_MBUTTONUP)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, mods);
|
|
else
|
|
{
|
|
if (HIWORD(wParam) == XBUTTON1)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_4, GLFW_RELEASE, mods);
|
|
else if (HIWORD(wParam) == XBUTTON2)
|
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_5, GLFW_RELEASE, mods);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
const int newCursorX = GET_X_LPARAM(lParam);
|
|
const int newCursorY = GET_Y_LPARAM(lParam);
|
|
|
|
if (newCursorX != window->win32.oldCursorX ||
|
|
newCursorY != window->win32.oldCursorY)
|
|
{
|
|
int x, y;
|
|
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
{
|
|
if (_glfw.focusedWindow != window)
|
|
return 0;
|
|
|
|
x = newCursorX - window->win32.oldCursorX;
|
|
y = newCursorY - window->win32.oldCursorY;
|
|
}
|
|
else
|
|
{
|
|
x = newCursorX;
|
|
y = newCursorY;
|
|
}
|
|
|
|
window->win32.oldCursorX = newCursorX;
|
|
window->win32.oldCursorY = newCursorY;
|
|
window->win32.cursorCentered = GL_FALSE;
|
|
|
|
_glfwInputCursorMotion(window, x, y);
|
|
}
|
|
|
|
if (!window->win32.cursorInside)
|
|
{
|
|
TRACKMOUSEEVENT tme;
|
|
ZeroMemory(&tme, sizeof(tme));
|
|
tme.cbSize = sizeof(tme);
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = window->win32.handle;
|
|
TrackMouseEvent(&tme);
|
|
|
|
window->win32.cursorInside = GL_TRUE;
|
|
_glfwInputCursorEnter(window, GL_TRUE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOUSELEAVE:
|
|
{
|
|
window->win32.cursorInside = GL_FALSE;
|
|
_glfwInputCursorEnter(window, GL_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
|
|
_glfwInputScroll(window, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA, 0.0);
|
|
return 0;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
if (_glfw.focusedWindow == window)
|
|
{
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
updateClipRect(window);
|
|
}
|
|
|
|
_glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam));
|
|
_glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam));
|
|
return 0;
|
|
}
|
|
|
|
case WM_MOVE:
|
|
{
|
|
if (_glfw.focusedWindow == window)
|
|
{
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
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_PAINT:
|
|
{
|
|
_glfwInputWindowDamage(window);
|
|
break;
|
|
}
|
|
|
|
case WM_ERASEBKGND:
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_SETCURSOR:
|
|
{
|
|
if (_glfw.focusedWindow == window && LOWORD(lParam) == HTCLIENT)
|
|
{
|
|
if (window->cursorMode == GLFW_CURSOR_HIDDEN ||
|
|
window->cursorMode == GLFW_CURSOR_DISABLED)
|
|
{
|
|
SetCursor(NULL);
|
|
return TRUE;
|
|
}
|
|
else if (window->cursor)
|
|
{
|
|
SetCursor(window->cursor->win32.handle);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_DEVICECHANGE:
|
|
{
|
|
if (DBT_DEVNODES_CHANGED == wParam)
|
|
{
|
|
_glfwInputMonitorChange();
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_DWMCOMPOSITIONCHANGED:
|
|
{
|
|
if (_glfwIsCompositionEnabled())
|
|
{
|
|
_GLFWwindow* previous = _glfwPlatformGetCurrentContext();
|
|
_glfwPlatformMakeContextCurrent(window);
|
|
_glfwPlatformSwapInterval(0);
|
|
_glfwPlatformMakeContextCurrent(previous);
|
|
}
|
|
|
|
// TODO: Restore vsync if compositing was disabled
|
|
break;
|
|
}
|
|
|
|
case WM_DROPFILES:
|
|
{
|
|
HDROP hDrop = (HDROP) wParam;
|
|
POINT pt;
|
|
int i;
|
|
|
|
const int count = DragQueryFileW(hDrop, 0xffffffff, NULL, 0);
|
|
char** names = calloc(count, sizeof(char*));
|
|
|
|
// Move the mouse to the position of the drop
|
|
DragQueryPoint(hDrop, &pt);
|
|
_glfwInputCursorMotion(window, pt.x, pt.y);
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
const UINT length = DragQueryFileW(hDrop, i, NULL, 0);
|
|
WCHAR* buffer = calloc(length + 1, sizeof(WCHAR));
|
|
|
|
DragQueryFileW(hDrop, i, buffer, length + 1);
|
|
names[i] = _glfwCreateUTF8FromWideString(buffer);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
_glfwInputDrop(window, count, (const char**) names);
|
|
|
|
for (i = 0; i < count; i++)
|
|
free(names[i]);
|
|
free(names);
|
|
|
|
DragFinish(hDrop);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// Translate client window size to full window size (including window borders)
|
|
//
|
|
static void getFullWindowSize(_GLFWwindow* window,
|
|
int clientWidth, int clientHeight,
|
|
int* fullWidth, int* fullHeight)
|
|
{
|
|
RECT rect = { 0, 0, clientWidth, clientHeight };
|
|
AdjustWindowRectEx(&rect, window->win32.dwStyle,
|
|
FALSE, window->win32.dwExStyle);
|
|
*fullWidth = rect.right - rect.left;
|
|
*fullHeight = rect.bottom - rect.top;
|
|
}
|
|
|
|
// Registers the GLFW window class
|
|
//
|
|
static ATOM registerWindowClass(void)
|
|
{
|
|
WNDCLASSW wc;
|
|
ATOM classAtom;
|
|
|
|
// Set window class parameters
|
|
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wc.lpfnWndProc = (WNDPROC) windowProc;
|
|
wc.cbClsExtra = 0; // No extra class data
|
|
wc.cbWndExtra = sizeof(void*) + sizeof(int); // Make room for one pointer
|
|
wc.hInstance = GetModuleHandleW(NULL);
|
|
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
|
|
wc.hbrBackground = NULL; // No background
|
|
wc.lpszMenuName = NULL; // No menu
|
|
wc.lpszClassName = _GLFW_WNDCLASSNAME;
|
|
|
|
// Load user-provided icon if available
|
|
wc.hIcon = LoadIconW(GetModuleHandleW(NULL), L"GLFW_ICON");
|
|
if (!wc.hIcon)
|
|
{
|
|
// No user-provided icon found, load default icon
|
|
wc.hIcon = LoadIconW(NULL, IDI_WINLOGO);
|
|
}
|
|
|
|
classAtom = RegisterClassW(&wc);
|
|
if (!classAtom)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to register window class");
|
|
return 0;
|
|
}
|
|
|
|
return classAtom;
|
|
}
|
|
|
|
// Creates the GLFW window and rendering context
|
|
//
|
|
static int createWindow(_GLFWwindow* window,
|
|
const _GLFWwndconfig* wndconfig,
|
|
const _GLFWctxconfig* ctxconfig,
|
|
const _GLFWfbconfig* fbconfig)
|
|
{
|
|
int xpos, ypos, fullWidth, fullHeight;
|
|
WCHAR* wideTitle;
|
|
|
|
window->win32.dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
|
|
window->win32.dwExStyle = WS_EX_APPWINDOW;
|
|
|
|
if (window->monitor)
|
|
{
|
|
window->win32.dwStyle |= WS_POPUP;
|
|
|
|
_glfwPlatformGetMonitorPos(wndconfig->monitor, &xpos, &ypos);
|
|
fullWidth = wndconfig->width;
|
|
fullHeight = wndconfig->height;
|
|
}
|
|
else
|
|
{
|
|
if (wndconfig->decorated)
|
|
{
|
|
window->win32.dwStyle |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
|
|
if (wndconfig->resizable)
|
|
{
|
|
window->win32.dwStyle |= WS_MAXIMIZEBOX | WS_SIZEBOX;
|
|
window->win32.dwExStyle |= WS_EX_WINDOWEDGE;
|
|
}
|
|
}
|
|
else
|
|
window->win32.dwStyle |= WS_POPUP;
|
|
|
|
xpos = CW_USEDEFAULT;
|
|
ypos = CW_USEDEFAULT;
|
|
|
|
getFullWindowSize(window,
|
|
wndconfig->width, wndconfig->height,
|
|
&fullWidth, &fullHeight);
|
|
}
|
|
|
|
wideTitle = _glfwCreateWideStringFromUTF8(wndconfig->title);
|
|
if (!wideTitle)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to convert title to wide string");
|
|
return GL_FALSE;
|
|
}
|
|
|
|
window->win32.handle = CreateWindowExW(window->win32.dwExStyle,
|
|
_GLFW_WNDCLASSNAME,
|
|
wideTitle,
|
|
window->win32.dwStyle,
|
|
xpos, ypos,
|
|
fullWidth, fullHeight,
|
|
NULL, // No parent window
|
|
NULL, // No window menu
|
|
GetModuleHandleW(NULL),
|
|
window); // Pass object to WM_CREATE
|
|
|
|
free(wideTitle);
|
|
|
|
if (!window->win32.handle)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window");
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (_glfw_ChangeWindowMessageFilterEx)
|
|
{
|
|
_glfw_ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_DROPFILES, MSGFLT_ALLOW, NULL);
|
|
_glfw_ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_COPYDATA, MSGFLT_ALLOW, NULL);
|
|
_glfw_ChangeWindowMessageFilterEx(window->win32.handle,
|
|
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
|
|
}
|
|
|
|
DragAcceptFiles(window->win32.handle, TRUE);
|
|
|
|
if (!_glfwCreateContext(window, ctxconfig, fbconfig))
|
|
return GL_FALSE;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
// Destroys the GLFW window and rendering context
|
|
//
|
|
static void destroyWindow(_GLFWwindow* window)
|
|
{
|
|
_glfwDestroyContext(window);
|
|
|
|
if (window->win32.handle)
|
|
{
|
|
DestroyWindow(window->win32.handle);
|
|
window->win32.handle = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW platform API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|
const _GLFWwndconfig* wndconfig,
|
|
const _GLFWctxconfig* ctxconfig,
|
|
const _GLFWfbconfig* fbconfig)
|
|
{
|
|
int status;
|
|
|
|
if (!_glfw.win32.classAtom)
|
|
{
|
|
_glfw.win32.classAtom = registerWindowClass();
|
|
if (!_glfw.win32.classAtom)
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (!createWindow(window, wndconfig, ctxconfig, fbconfig))
|
|
return GL_FALSE;
|
|
|
|
status = _glfwAnalyzeContext(window, ctxconfig, fbconfig);
|
|
|
|
if (status == _GLFW_RECREATION_IMPOSSIBLE)
|
|
return GL_FALSE;
|
|
|
|
if (status == _GLFW_RECREATION_REQUIRED)
|
|
{
|
|
// Some window hints require us to re-create the context using WGL
|
|
// extensions retrieved through the current context, as we cannot check
|
|
// for WGL extensions or retrieve WGL entry points before we have a
|
|
// current context (actually until we have implicitly loaded the ICD)
|
|
|
|
// Yes, this is strange, and yes, this is the proper way on Win32
|
|
|
|
// As Windows only allows you to set the pixel format once for a
|
|
// window, we need to destroy the current window and create a new one
|
|
// to be able to use the new pixel format
|
|
|
|
// Technically, it may be possible to keep the old window around if
|
|
// we're just creating an OpenGL 3.0+ context with the same pixel
|
|
// format, but it's not worth the added code complexity
|
|
|
|
// First we clear the current context (the one we just created)
|
|
// This is usually done by glfwDestroyWindow, but as we're not doing
|
|
// full GLFW window destruction, it's duplicated here
|
|
_glfwPlatformMakeContextCurrent(NULL);
|
|
|
|
// Next destroy the Win32 window and WGL context (without resetting or
|
|
// destroying the GLFW window object)
|
|
destroyWindow(window);
|
|
|
|
// ...and then create them again, this time with better APIs
|
|
if (!createWindow(window, wndconfig, ctxconfig, fbconfig))
|
|
return GL_FALSE;
|
|
}
|
|
|
|
if (window->monitor)
|
|
{
|
|
if (!_glfwSetVideoMode(window->monitor, &window->videoMode))
|
|
return GL_FALSE;
|
|
|
|
// Place the window above all topmost windows
|
|
_glfwPlatformShowWindow(window);
|
|
SetWindowPos(window->win32.handle, HWND_TOPMOST, 0,0,0,0,
|
|
SWP_NOMOVE | SWP_NOSIZE);
|
|
}
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|
{
|
|
destroyWindow(window);
|
|
|
|
if (window->monitor)
|
|
_glfwRestoreVideoMode(window->monitor);
|
|
}
|
|
|
|
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
|
|
{
|
|
WCHAR* wideTitle = _glfwCreateWideStringFromUTF8(title);
|
|
if (!wideTitle)
|
|
{
|
|
_glfwInputError(GLFW_PLATFORM_ERROR,
|
|
"Win32: Failed to convert title to wide string");
|
|
return;
|
|
}
|
|
|
|
SetWindowTextW(window->win32.handle, wideTitle);
|
|
free(wideTitle);
|
|
}
|
|
|
|
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 };
|
|
AdjustWindowRectEx(&rect, window->win32.dwStyle,
|
|
FALSE, window->win32.dwExStyle);
|
|
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)
|
|
{
|
|
GLFWvidmode mode;
|
|
_glfwSetVideoMode(window->monitor, &window->videoMode);
|
|
_glfwPlatformGetVideoMode(window->monitor, &mode);
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
0, 0, mode.width, mode.height,
|
|
SWP_NOMOVE);
|
|
}
|
|
else
|
|
{
|
|
int fullWidth, fullHeight;
|
|
getFullWindowSize(window, width, height, &fullWidth, &fullHeight);
|
|
|
|
SetWindowPos(window->win32.handle, HWND_TOP,
|
|
0, 0, fullWidth, fullHeight,
|
|
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
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);
|
|
AdjustWindowRectEx(&rect, window->win32.dwStyle,
|
|
FALSE, window->win32.dwExStyle);
|
|
|
|
if (left)
|
|
*left = -rect.left;
|
|
if (top)
|
|
*top = -rect.top;
|
|
if (right)
|
|
*right = rect.right - width;
|
|
if (bottom)
|
|
*bottom = rect.bottom - height;
|
|
}
|
|
|
|
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_MINIMIZE);
|
|
}
|
|
|
|
void _glfwPlatformRestoreWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_RESTORE);
|
|
}
|
|
|
|
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_SHOW);
|
|
BringWindowToTop(window->win32.handle);
|
|
SetForegroundWindow(window->win32.handle);
|
|
SetFocus(window->win32.handle);
|
|
}
|
|
|
|
void _glfwPlatformHideWindow(_GLFWwindow* window)
|
|
{
|
|
ShowWindow(window->win32.handle, SW_HIDE);
|
|
}
|
|
|
|
void _glfwPlatformPollEvents(void)
|
|
{
|
|
MSG msg;
|
|
_GLFWwindow* window;
|
|
|
|
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
// Treat WM_QUIT as a close on all windows
|
|
|
|
window = _glfw.windowListHead;
|
|
while (window)
|
|
{
|
|
_glfwInputWindowCloseRequest(window);
|
|
window = window->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
}
|
|
|
|
window = _glfw.focusedWindow;
|
|
if (window)
|
|
{
|
|
// LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix)
|
|
// This is the only async event handling in GLFW, but it solves some
|
|
// nasty problems
|
|
{
|
|
const int mods = getAsyncKeyMods();
|
|
|
|
// Get current state of left and right shift keys
|
|
const int lshiftDown = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1;
|
|
const int rshiftDown = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1;
|
|
|
|
// See if this differs from our belief of what has happened
|
|
// (we only have to check for lost key up events)
|
|
if (!lshiftDown && window->key[GLFW_KEY_LEFT_SHIFT] == 1)
|
|
_glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, 0, GLFW_RELEASE, mods);
|
|
|
|
if (!rshiftDown && window->key[GLFW_KEY_RIGHT_SHIFT] == 1)
|
|
_glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, 0, GLFW_RELEASE, mods);
|
|
}
|
|
|
|
// Did the cursor move in an focused window that has disabled the cursor
|
|
if (window->cursorMode == GLFW_CURSOR_DISABLED &&
|
|
!window->win32.cursorCentered)
|
|
{
|
|
int width, height;
|
|
_glfwPlatformGetWindowSize(window, &width, &height);
|
|
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
|
|
window->win32.cursorCentered = GL_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void _glfwPlatformWaitEvents(void)
|
|
{
|
|
WaitMessage();
|
|
|
|
_glfwPlatformPollEvents();
|
|
}
|
|
|
|
void _glfwPlatformPostEmptyEvent(void)
|
|
{
|
|
_GLFWwindow* window = _glfw.windowListHead;
|
|
PostMessage(window->win32.handle, WM_NULL, 0, 0);
|
|
}
|
|
|
|
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos)
|
|
{
|
|
POINT pos = { (int) xpos, (int) ypos };
|
|
ClientToScreen(window->win32.handle, &pos);
|
|
SetCursorPos(pos.x, pos.y);
|
|
|
|
window->win32.oldCursorX = (int) xpos;
|
|
window->win32.oldCursorY = (int) ypos;
|
|
}
|
|
|
|
void _glfwPlatformApplyCursorMode(_GLFWwindow* window)
|
|
{
|
|
switch (window->cursorMode)
|
|
{
|
|
case GLFW_CURSOR_NORMAL:
|
|
restoreCursor(window);
|
|
break;
|
|
case GLFW_CURSOR_HIDDEN:
|
|
hideCursor(window);
|
|
break;
|
|
case GLFW_CURSOR_DISABLED:
|
|
disableCursor(window);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
|
const GLFWimage* image,
|
|
int xhot, int yhot)
|
|
{
|
|
HDC dc;
|
|
HBITMAP bitmap, mask;
|
|
BITMAPV5HEADER bi;
|
|
ICONINFO ii;
|
|
DWORD* target = 0;
|
|
BYTE* source = (BYTE*) image->pixels;
|
|
int i;
|
|
|
|
ZeroMemory(&bi, sizeof(bi));
|
|
bi.bV5Size = sizeof(BITMAPV5HEADER);
|
|
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);
|
|
bitmap = CreateDIBSection(dc, (BITMAPINFO*) &bi, DIB_RGB_COLORS,
|
|
(void**) &target, NULL, (DWORD) 0);
|
|
ReleaseDC(NULL, dc);
|
|
|
|
if (!bitmap)
|
|
return GL_FALSE;
|
|
|
|
mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
|
|
if (!mask)
|
|
{
|
|
DeleteObject(bitmap);
|
|
return GL_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < image->width * image->height; i++, target++, source += 4)
|
|
{
|
|
*target = (source[3] << 24) |
|
|
(source[0] << 16) |
|
|
(source[1] << 8) |
|
|
source[2];
|
|
}
|
|
|
|
ZeroMemory(&ii, sizeof(ii));
|
|
ii.fIcon = FALSE;
|
|
ii.xHotspot = xhot;
|
|
ii.yHotspot = yhot;
|
|
ii.hbmMask = mask;
|
|
ii.hbmColor = bitmap;
|
|
|
|
cursor->win32.handle = (HCURSOR) CreateIconIndirect(&ii);
|
|
|
|
DeleteObject(bitmap);
|
|
DeleteObject(mask);
|
|
|
|
if (!cursor->win32.handle)
|
|
return GL_FALSE;
|
|
|
|
return GL_TRUE;
|
|
}
|
|
|
|
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
|
|
{
|
|
if (cursor->win32.handle)
|
|
DestroyIcon((HICON) cursor->win32.handle);
|
|
}
|
|
|
|
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
|
{
|
|
// It should be guaranteed that the cursor is not being used by this window if
|
|
// the following condition is not met. That way it should be safe to destroy the
|
|
// cursor after calling glfwSetCursor(window, NULL) on all windows using the cursor.
|
|
|
|
if (window->cursorMode == GLFW_CURSOR_NORMAL && _glfw.focusedWindow == window &&
|
|
window->win32.cursorInside)
|
|
{
|
|
if (cursor)
|
|
SetCursor(cursor->win32.handle);
|
|
else
|
|
SetCursor(LoadCursorW(NULL, IDC_ARROW));
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
////// GLFW native API //////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
|
|
{
|
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
|
return window->win32.handle;
|
|
}
|
|
|