CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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