CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/RawInput.cpp
Views: 1401
// Copyright (c) 2014- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <set>18#include <algorithm>19#include <vector>2021#include "Common/System/NativeApp.h"22#include "Common/System/Display.h"23#include "Common/Input/InputState.h"24#include "Common/Log.h"25#include "Windows/RawInput.h"26#include "Windows/MainWindow.h"27#include "Windows/WindowsHost.h"28#include "Common/CommonFuncs.h"29#include "Common/SysError.h"30#include "Core/Config.h"31#include "Core/HLE/Plugins.h"3233#ifndef HID_USAGE_PAGE_GENERIC34#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)35#endif36#ifndef HID_USAGE_GENERIC_POINTER37#define HID_USAGE_GENERIC_POINTER ((USHORT) 0x01)38#endif39#ifndef HID_USAGE_GENERIC_MOUSE40#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)41#endif42#ifndef HID_USAGE_GENERIC_JOYSTICK43#define HID_USAGE_GENERIC_JOYSTICK ((USHORT) 0x04)44#endif45#ifndef HID_USAGE_GENERIC_GAMEPAD46#define HID_USAGE_GENERIC_GAMEPAD ((USHORT) 0x05)47#endif48#ifndef HID_USAGE_GENERIC_KEYBOARD49#define HID_USAGE_GENERIC_KEYBOARD ((USHORT) 0x06)50#endif51#ifndef HID_USAGE_GENERIC_KEYPAD52#define HID_USAGE_GENERIC_KEYPAD ((USHORT) 0x07)53#endif54#ifndef HID_USAGE_GENERIC_MULTIAXIS55#define HID_USAGE_GENERIC_MULTIAXIS ((USHORT) 0x07)56#endif5758namespace WindowsRawInput {59static std::set<InputKeyCode> keyboardKeysDown;60static void *rawInputBuffer;61static size_t rawInputBufferSize;62static bool menuActive;63static bool focused = true;64static bool mouseDown[5] = { false, false, false, false, false }; //left, right, middle, 4, 565static float mouseX = 0.0f;66static float mouseY = 0.0f;6768// TODO: More keys need to be added, but this is more than69// a fair start.70static std::map<int, InputKeyCode> windowsTransTable = {71{ 'A', NKCODE_A },72{ 'B', NKCODE_B },73{ 'C', NKCODE_C },74{ 'D', NKCODE_D },75{ 'E', NKCODE_E },76{ 'F', NKCODE_F },77{ 'G', NKCODE_G },78{ 'H', NKCODE_H },79{ 'I', NKCODE_I },80{ 'J', NKCODE_J },81{ 'K', NKCODE_K },82{ 'L', NKCODE_L },83{ 'M', NKCODE_M },84{ 'N', NKCODE_N },85{ 'O', NKCODE_O },86{ 'P', NKCODE_P },87{ 'Q', NKCODE_Q },88{ 'R', NKCODE_R },89{ 'S', NKCODE_S },90{ 'T', NKCODE_T },91{ 'U', NKCODE_U },92{ 'V', NKCODE_V },93{ 'W', NKCODE_W },94{ 'X', NKCODE_X },95{ 'Y', NKCODE_Y },96{ 'Z', NKCODE_Z },97{ '0', NKCODE_0 },98{ '1', NKCODE_1 },99{ '2', NKCODE_2 },100{ '3', NKCODE_3 },101{ '4', NKCODE_4 },102{ '5', NKCODE_5 },103{ '6', NKCODE_6 },104{ '7', NKCODE_7 },105{ '8', NKCODE_8 },106{ '9', NKCODE_9 },107{ VK_OEM_PERIOD, NKCODE_PERIOD },108{ VK_OEM_COMMA, NKCODE_COMMA },109{ VK_NUMPAD0, NKCODE_NUMPAD_0 },110{ VK_NUMPAD1, NKCODE_NUMPAD_1 },111{ VK_NUMPAD2, NKCODE_NUMPAD_2 },112{ VK_NUMPAD3, NKCODE_NUMPAD_3 },113{ VK_NUMPAD4, NKCODE_NUMPAD_4 },114{ VK_NUMPAD5, NKCODE_NUMPAD_5 },115{ VK_NUMPAD6, NKCODE_NUMPAD_6 },116{ VK_NUMPAD7, NKCODE_NUMPAD_7 },117{ VK_NUMPAD8, NKCODE_NUMPAD_8 },118{ VK_NUMPAD9, NKCODE_NUMPAD_9 },119{ VK_DECIMAL, NKCODE_NUMPAD_DOT },120{ VK_DIVIDE, NKCODE_NUMPAD_DIVIDE },121{ VK_MULTIPLY, NKCODE_NUMPAD_MULTIPLY },122{ VK_SUBTRACT, NKCODE_NUMPAD_SUBTRACT },123{ VK_ADD, NKCODE_NUMPAD_ADD },124{ VK_SEPARATOR, NKCODE_NUMPAD_COMMA },125{ VK_OEM_MINUS, NKCODE_MINUS },126{ VK_OEM_PLUS, NKCODE_PLUS },127{ VK_LCONTROL, NKCODE_CTRL_LEFT },128{ VK_RCONTROL, NKCODE_CTRL_RIGHT },129{ VK_LSHIFT, NKCODE_SHIFT_LEFT },130{ VK_RSHIFT, NKCODE_SHIFT_RIGHT },131{ VK_LMENU, NKCODE_ALT_LEFT },132{ VK_RMENU, NKCODE_ALT_RIGHT },133{ VK_BACK, NKCODE_DEL }, // yes! http://stackoverflow.com/questions/4886858/android-edittext-deletebackspace-key-event134{ VK_SPACE, NKCODE_SPACE },135{ VK_ESCAPE, NKCODE_ESCAPE },136{ VK_UP, NKCODE_DPAD_UP },137{ VK_INSERT, NKCODE_INSERT },138{ VK_HOME, NKCODE_MOVE_HOME },139{ VK_PRIOR, NKCODE_PAGE_UP },140{ VK_NEXT, NKCODE_PAGE_DOWN },141{ VK_DELETE, NKCODE_FORWARD_DEL },142{ VK_END, NKCODE_MOVE_END },143{ VK_TAB, NKCODE_TAB },144{ VK_DOWN, NKCODE_DPAD_DOWN },145{ VK_LEFT, NKCODE_DPAD_LEFT },146{ VK_RIGHT, NKCODE_DPAD_RIGHT },147{ VK_CAPITAL, NKCODE_CAPS_LOCK },148{ VK_CLEAR, NKCODE_CLEAR },149{ VK_SNAPSHOT, NKCODE_SYSRQ },150{ VK_SCROLL, NKCODE_SCROLL_LOCK },151{ VK_OEM_1, NKCODE_SEMICOLON },152{ VK_OEM_2, NKCODE_SLASH },153{ VK_OEM_3, NKCODE_GRAVE },154{ VK_OEM_4, NKCODE_LEFT_BRACKET },155{ VK_OEM_5, NKCODE_BACKSLASH },156{ VK_OEM_6, NKCODE_RIGHT_BRACKET },157{ VK_OEM_7, NKCODE_APOSTROPHE },158{ VK_RETURN, NKCODE_ENTER },159{ VK_APPS, NKCODE_MENU }, // Context menu key, let's call this "menu".160{ VK_PAUSE, NKCODE_BREAK },161{ VK_F1, NKCODE_F1 },162{ VK_F2, NKCODE_F2 },163{ VK_F3, NKCODE_F3 },164{ VK_F4, NKCODE_F4 },165{ VK_F5, NKCODE_F5 },166{ VK_F6, NKCODE_F6 },167{ VK_F7, NKCODE_F7 },168{ VK_F8, NKCODE_F8 },169{ VK_F9, NKCODE_F9 },170{ VK_F10, NKCODE_F10 },171{ VK_F11, NKCODE_F11 },172{ VK_F12, NKCODE_F12 },173{ VK_OEM_102, NKCODE_EXT_PIPE },174{ VK_LBUTTON, NKCODE_EXT_MOUSEBUTTON_1 },175{ VK_RBUTTON, NKCODE_EXT_MOUSEBUTTON_2 },176{ VK_MBUTTON, NKCODE_EXT_MOUSEBUTTON_3 },177{ VK_XBUTTON1, NKCODE_EXT_MOUSEBUTTON_4 },178{ VK_XBUTTON2, NKCODE_EXT_MOUSEBUTTON_5 },179};180181void Init() {182RAWINPUTDEVICE dev[3];183memset(dev, 0, sizeof(dev));184185dev[0].usUsagePage = HID_USAGE_PAGE_GENERIC;186dev[0].usUsage = HID_USAGE_GENERIC_KEYBOARD;187dev[0].dwFlags = g_Config.bIgnoreWindowsKey ? RIDEV_NOHOTKEYS : 0;188189dev[1].usUsagePage = HID_USAGE_PAGE_GENERIC;190dev[1].usUsage = HID_USAGE_GENERIC_MOUSE;191dev[1].dwFlags = 0;192193dev[2].usUsagePage = HID_USAGE_PAGE_GENERIC;194dev[2].usUsage = HID_USAGE_GENERIC_JOYSTICK;195dev[2].dwFlags = 0;196197if (!RegisterRawInputDevices(dev, 3, sizeof(RAWINPUTDEVICE))) {198WARN_LOG(Log::System, "Unable to register raw input devices: %s", GetLastErrorMsg().c_str());199}200}201202bool UpdateMenuActive() {203MENUBARINFO info;204memset(&info, 0, sizeof(info));205info.cbSize = sizeof(info);206if (GetMenuBarInfo(MainWindow::GetHWND(), OBJID_MENU, 0, &info) != 0) {207menuActive = info.fBarFocused != FALSE;208} else {209// In fullscreen mode, we remove the menu210menuActive = false;211}212return menuActive;213}214215static InputKeyCode GetTrueVKey(const RAWKEYBOARD &kb) {216int vKey = kb.VKey;217switch (kb.VKey) {218case VK_SHIFT:219vKey = MapVirtualKey(kb.MakeCode, MAPVK_VSC_TO_VK_EX);220break;221222case VK_CONTROL:223if (kb.Flags & RI_KEY_E0)224vKey = VK_RCONTROL;225else226vKey = VK_LCONTROL;227break;228229case VK_MENU:230if (kb.Flags & RI_KEY_E0)231vKey = VK_RMENU; // Right Alt / AltGr232else233vKey = VK_LMENU; // Left Alt234break;235236//case VK_RETURN:237// if (kb.Flags & RI_KEY_E0)238// vKey = VK_RETURN; // Numeric return - no code for this. Can special case.239// break;240241// Source: http://molecularmusings.wordpress.com/2011/09/05/properly-handling-keyboard-input/242case VK_NUMLOCK:243// correct PAUSE/BREAK and NUM LOCK silliness, and set the extended bit244vKey = MapVirtualKey(kb.VKey, MAPVK_VK_TO_VSC) | 0x100;245break;246247default:248break;249}250251return windowsTransTable[vKey];252}253254void ProcessKeyboard(const RAWINPUT *raw, bool foreground) {255if (menuActive && UpdateMenuActive()) {256// Ignore keyboard input while a menu is active, it's probably interacting with the menu.257return;258}259260KeyInput key;261key.deviceId = DEVICE_ID_KEYBOARD;262263if (raw->data.keyboard.Message == WM_KEYDOWN || raw->data.keyboard.Message == WM_SYSKEYDOWN) {264key.flags = KEY_DOWN;265key.keyCode = GetTrueVKey(raw->data.keyboard);266267if (key.keyCode) {268NativeKey(key);269keyboardKeysDown.insert(key.keyCode);270}271} else if (raw->data.keyboard.Message == WM_KEYUP) {272key.flags = KEY_UP;273key.keyCode = GetTrueVKey(raw->data.keyboard);274275if (key.keyCode) {276NativeKey(key);277auto keyDown = keyboardKeysDown.find(key.keyCode);278if (keyDown != keyboardKeysDown.end())279keyboardKeysDown.erase(keyDown);280}281}282}283284LRESULT ProcessChar(HWND hWnd, WPARAM wParam, LPARAM lParam) {285KeyInput key;286key.unicodeChar = (int)wParam; // Note that this is NOT a NKCODE but a Unicode character!287key.flags = KEY_CHAR;288key.deviceId = DEVICE_ID_KEYBOARD;289NativeKey(key);290return 0;291}292293static bool MouseInWindow(HWND hWnd) {294POINT pt;295if (GetCursorPos(&pt) != 0) {296RECT rt;297if (GetWindowRect(hWnd, &rt) != 0) {298return PtInRect(&rt, pt) != 0;299}300}301return true;302}303304void ProcessMouse(HWND hWnd, const RAWINPUT *raw, bool foreground) {305if (menuActive && UpdateMenuActive()) {306// Ignore mouse input while a menu is active, it's probably interacting with the menu.307return;308}309310TouchInput touch;311touch.id = 0;312touch.flags = TOUCH_MOVE;313touch.x = mouseX;314touch.y = mouseY;315316KeyInput key;317key.deviceId = DEVICE_ID_MOUSE;318319NativeMouseDelta(raw->data.mouse.lLastX, raw->data.mouse.lLastY);320321static const int rawInputDownID[5] = {322RI_MOUSE_LEFT_BUTTON_DOWN,323RI_MOUSE_RIGHT_BUTTON_DOWN,324RI_MOUSE_BUTTON_3_DOWN,325RI_MOUSE_BUTTON_4_DOWN,326RI_MOUSE_BUTTON_5_DOWN327};328static const int rawInputUpID[5] = {329RI_MOUSE_LEFT_BUTTON_UP,330RI_MOUSE_RIGHT_BUTTON_UP,331RI_MOUSE_BUTTON_3_UP,332RI_MOUSE_BUTTON_4_UP,333RI_MOUSE_BUTTON_5_UP334};335static const int vkInputID[5] = {336VK_LBUTTON,337VK_RBUTTON,338VK_MBUTTON,339VK_XBUTTON1,340VK_XBUTTON2341};342343for (int i = 0; i < 5; i++) {344if (i > 0 || (g_Config.bMouseControl && (GetUIState() == UISTATE_INGAME || g_Config.bMapMouse))) {345if (raw->data.mouse.usButtonFlags & rawInputDownID[i]) {346key.flags = KEY_DOWN;347key.keyCode = windowsTransTable[vkInputID[i]];348NativeTouch(touch);349if (MouseInWindow(hWnd)) {350NativeKey(key);351}352mouseDown[i] = true;353} else if (raw->data.mouse.usButtonFlags & rawInputUpID[i]) {354key.flags = KEY_UP;355key.keyCode = windowsTransTable[vkInputID[i]];356NativeTouch(touch);357if (MouseInWindow(hWnd)) {358if (!mouseDown[i]) {359// This means they were focused outside, and clicked inside.360// Seems intentional, so send a down first.361key.flags = KEY_DOWN;362NativeKey(key);363key.flags = KEY_UP;364NativeKey(key);365} else {366NativeKey(key);367}368}369mouseDown[i] = false;370}371}372}373}374375void ProcessHID(RAWINPUT *raw, bool foreground) {376// TODO: Use hidparse or something to understand the data.377}378379LRESULT Process(HWND hWnd, WPARAM wParam, LPARAM lParam) {380UINT dwSize = 0;381GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));382if (!rawInputBuffer) {383rawInputBuffer = malloc(dwSize);384if (!rawInputBuffer)385return DefWindowProc(hWnd, WM_INPUT, wParam, lParam);386memset(rawInputBuffer, 0, dwSize);387rawInputBufferSize = dwSize;388}389if (dwSize > rawInputBufferSize) {390void *newBuf = realloc(rawInputBuffer, dwSize);391if (!newBuf)392return DefWindowProc(hWnd, WM_INPUT, wParam, lParam);393rawInputBuffer = newBuf;394rawInputBufferSize = dwSize;395memset(rawInputBuffer, 0, dwSize);396}397GetRawInputData((HRAWINPUT)lParam, RID_INPUT, rawInputBuffer, &dwSize, sizeof(RAWINPUTHEADER));398RAWINPUT *raw = (RAWINPUT *)rawInputBuffer;399bool foreground = GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT;400401switch (raw->header.dwType) {402case RIM_TYPEKEYBOARD:403ProcessKeyboard(raw, foreground);404break;405406case RIM_TYPEMOUSE:407ProcessMouse(hWnd, raw, foreground);408break;409410case RIM_TYPEHID:411ProcessHID(raw, foreground);412break;413}414415// Docs say to call DefWindowProc to perform necessary cleanup.416return DefWindowProc(hWnd, WM_INPUT, wParam, lParam);417}418419void SetMousePos(float x, float y) {420mouseX = x;421mouseY = y;422}423424void GainFocus() {425focused = true;426}427428void LoseFocus() {429// Force-release all held keys on the keyboard to prevent annoying stray inputs.430KeyInput key;431key.deviceId = DEVICE_ID_KEYBOARD;432key.flags = KEY_UP;433for (auto i = keyboardKeysDown.begin(); i != keyboardKeysDown.end(); ++i) {434key.keyCode = *i;435NativeKey(key);436}437focused = false;438}439440void NotifyMenu() {441UpdateMenuActive();442}443444void Shutdown() {445free(rawInputBuffer);446rawInputBuffer = 0;447}448};449450451