Преглед изворни кода

device: windows: use raw input

Daniele Bartolini пре 4 месеци
родитељ
комит
0ffa1c07e0
2 измењених фајлова са 124 додато и 12 уклоњено
  1. 4 0
      docs/changelog.rst
  2. 120 12
      src/device/main_windows.cpp

+ 4 - 0
docs/changelog.rst

@@ -4,6 +4,10 @@ Changelog
 0.60.0 --- DD MMM YYYY
 0.60.0 --- DD MMM YYYY
 ----------------------
 ----------------------
 
 
+**Fixes**
+
+* Runtime: Windows: fixed a number of misdetected keyboard keys.
+
 0.59.0 --- 02 Oct 2025
 0.59.0 --- 02 Oct 2025
 ----------------------
 ----------------------
 
 

+ 120 - 12
src/device/main_windows.cpp

@@ -71,7 +71,6 @@ static KeyboardButton::Enum win_translate_key(s32 winkey)
 	case VK_LWIN:     return KeyboardButton::SUPER_LEFT;
 	case VK_LWIN:     return KeyboardButton::SUPER_LEFT;
 	case VK_RWIN:     return KeyboardButton::SUPER_RIGHT;
 	case VK_RWIN:     return KeyboardButton::SUPER_RIGHT;
 	case VK_NUMLOCK:  return KeyboardButton::NUM_LOCK;
 	case VK_NUMLOCK:  return KeyboardButton::NUM_LOCK;
-	// case VK_RETURN:   return KeyboardButton::NUMPAD_ENTER;
 	case VK_DECIMAL:  return KeyboardButton::NUMPAD_DELETE;
 	case VK_DECIMAL:  return KeyboardButton::NUMPAD_DELETE;
 	case VK_MULTIPLY: return KeyboardButton::NUMPAD_MULTIPLY;
 	case VK_MULTIPLY: return KeyboardButton::NUMPAD_MULTIPLY;
 	case VK_ADD:      return KeyboardButton::NUMPAD_ADD;
 	case VK_ADD:      return KeyboardButton::NUMPAD_ADD;
@@ -400,6 +399,13 @@ struct WindowsDevice
 		if (_options->_parent_window != 0)
 		if (_options->_parent_window != 0)
 			SetParent(_hwnd, (HWND)(UINT_PTR)_options->_parent_window);
 			SetParent(_hwnd, (HWND)(UINT_PTR)_options->_parent_window);
 
 
+		RAWINPUTDEVICE rid;
+		rid.usUsagePage = 0x01;
+		rid.usUsage = 0x06;
+		rid.dwFlags = RIDEV_NOLEGACY;
+		rid.hwndTarget = _hwnd;
+		RegisterRawInputDevices(&rid, 1, sizeof(rid));
+
 		_hcursor = LoadCursorA(NULL, IDC_ARROW);
 		_hcursor = LoadCursorA(NULL, IDC_ARROW);
 
 
 		// Create standard cursors
 		// Create standard cursors
@@ -588,18 +594,120 @@ struct WindowsDevice
 			break;
 			break;
 		}
 		}
 
 
-		case WM_KEYDOWN:
-		case WM_SYSKEYDOWN:
-		case WM_KEYUP:
-		case WM_SYSKEYUP: {
-			KeyboardButton::Enum kb = win_translate_key(wparam & 0xff);
+		case WM_INPUT: {
+			// See: https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input
+			char data[sizeof(RAWINPUT)] = {};
+			UINT size = sizeof(RAWINPUT);
+			GetRawInputData(reinterpret_cast<HRAWINPUT>(lparam), RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
+
+			// Extract keyboard raw input data.
+			RAWINPUT *raw = reinterpret_cast<RAWINPUT *>(data);
+			if (raw->header.dwType == RIM_TYPEKEYBOARD) {
+				const RAWKEYBOARD &keyboard = raw->data.keyboard;
+				UINT vk = keyboard.VKey;
+				UINT sc = keyboard.MakeCode;
+				UINT flags = keyboard.Flags;
+				KeyboardButton::Enum kb = KeyboardButton::COUNT;
+
+				if (vk == 255) {
+					// Discard "fake keys" which are part of an escaped sequence.
+					break;
+				} else if (vk == VK_SHIFT) {
+					// Correct left-hand / right-hand SHIFT.
+					vk = MapVirtualKey(sc, MAPVK_VSC_TO_VK_EX);
+				} else if (vk == VK_NUMLOCK) {
+					// Correct PAUSE/BREAK and NUM LOCK silliness, and set the extended bit.
+					sc = MapVirtualKey(vk, MAPVK_VK_TO_VSC) | 0x100;
+				}
 
 
-			if (kb != KeyboardButton::COUNT) {
-				_queue.push_button_event(InputDeviceType::KEYBOARD
-					, 0
-					, kb
-					, (id == WM_KEYDOWN || id == WM_SYSKEYDOWN)
-					);
+				// e0 and e1 are escape sequences used for certain special keys, such as PRINT and
+				// PAUSE/BREAK. See http://www.win.tue.nl/~aeb/linux/kbd/scs-1.html
+				const bool e0 = (flags & RI_KEY_E0) != 0;
+				const bool e1 = (flags & RI_KEY_E1) != 0;
+
+				if (e1) {
+					// For escaped sequences, turn the virtual key into the correct scan code using
+					// MapVirtualKey. however, MapVirtualKey is unable to map VK_PAUSE (this is a
+					// known bug), hence we map that by hand.
+					if (vk == VK_PAUSE)
+						sc = 0x45;
+					else
+						sc = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
+				}
+
+				switch (vk) {
+				// Right-hand CONTROL and ALT have their e0 bit set.
+				case VK_CONTROL:
+					vk = e0 ? VK_RCONTROL : VK_LCONTROL;
+					break;
+				case VK_MENU:
+					vk = e0 ? VK_RMENU : VK_LMENU;
+					break;
+				// NUMPAD ENTER has its e0 bit set.
+				case VK_RETURN:
+					if (e0)
+						kb = KeyboardButton::NUMPAD_ENTER;
+					break;
+				// The standard INSERT, DELETE, HOME, END, PRIOR and NEXT keys will always have
+				// their e0 bit set, but the corresponding keys on the NUMPAD will not.
+				case VK_INSERT:
+					if (!e0) vk = VK_NUMPAD0;
+					break;
+				case VK_DELETE:
+					if (!e0)
+						vk = VK_DECIMAL;
+					break;
+				case VK_HOME:
+					if (!e0)
+						vk = VK_NUMPAD7;
+					break;
+				case VK_END:
+					if (!e0)
+						vk = VK_NUMPAD1;
+					break;
+				case VK_PRIOR:
+					if (!e0)
+						vk = VK_NUMPAD9;
+					break;
+				case VK_NEXT:
+					if (!e0)
+						vk = VK_NUMPAD3;
+					break;
+				// The standard arrow keys will always have their e0 bit set, but the corresponding
+				// keys on the NUMPAD will not.
+				case VK_LEFT:
+					if (!e0)
+						vk = VK_NUMPAD4;
+					break;
+				case VK_RIGHT:
+					if (!e0)
+						vk = VK_NUMPAD6;
+					break;
+				case VK_UP:
+					if (!e0)
+						vk = VK_NUMPAD8;
+					break;
+				case VK_DOWN:
+					if (!e0)
+						vk = VK_NUMPAD2;
+					break;
+				// NUMPAD5 doesn't have its e0 bit set.
+				case VK_CLEAR:
+					if (!e0)
+						vk = VK_NUMPAD5;
+					break;
+				}
+
+				if (kb == KeyboardButton::COUNT)
+					kb = win_translate_key(vk);
+
+				if (kb != KeyboardButton::COUNT) {
+					_queue.push_button_event(InputDeviceType::KEYBOARD
+						, 0
+						, kb
+						, (flags & RI_KEY_BREAK) == 0
+						);
+				}
 			}
 			}
 			break;
 			break;
 		}
 		}