Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/KeyMap.cpp
5654 views
1
// Copyright (c) 2013- 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 <algorithm>
19
#include <set>
20
#include <unordered_map>
21
#include <mutex>
22
23
#include "ppsspp_config.h"
24
25
#include "Common/System/NativeApp.h"
26
#include "Common/System/System.h"
27
#include "Common/System/OSD.h"
28
#include "Common/Data/Text/I18n.h"
29
#include "Common/Data/Format/IniFile.h"
30
#include "Common/Input/InputState.h"
31
#include "Common/VR/PPSSPPVR.h"
32
#include "Common/Log.h"
33
#include "Common/TimeUtil.h"
34
#include "Common/StringUtils.h"
35
#include "Core/HLE/sceUtility.h"
36
#include "Core/HLE/sceCtrl.h" // psp keys
37
#include "Core/Config.h"
38
#include "Core/KeyMap.h"
39
#include "Core/KeyMapDefaults.h"
40
41
namespace KeyMap {
42
43
// We actually need to lock g_controllerMap since it can be modified! Crashes will probably be rare though,
44
// but I've seen one. Let's just protect it with a mutex.
45
std::recursive_mutex g_controllerMapLock;
46
KeyMapping g_controllerMap;
47
48
// Incremented on modification, so we know when to update menus.
49
int g_controllerMapGeneration = 0;
50
std::set<std::string> g_seenPads;
51
std::map<InputDeviceID, std::string> g_padNames;
52
std::set<InputDeviceID> g_seenDeviceIds;
53
54
AxisType GetAxisType(InputAxis input) {
55
switch (input) {
56
case JOYSTICK_AXIS_GAS:
57
case JOYSTICK_AXIS_BRAKE:
58
case JOYSTICK_AXIS_LTRIGGER:
59
case JOYSTICK_AXIS_RTRIGGER:
60
return AxisType::TRIGGER;
61
case JOYSTICK_AXIS_X:
62
case JOYSTICK_AXIS_Y:
63
case JOYSTICK_AXIS_Z:
64
case JOYSTICK_AXIS_RX:
65
case JOYSTICK_AXIS_RY:
66
case JOYSTICK_AXIS_RZ:
67
return AxisType::STICK;
68
default:
69
return AxisType::OTHER;
70
}
71
}
72
73
// Utility for UI navigation
74
void SingleInputMappingFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) {
75
std::vector<MultiInputMapping> multiMappings;
76
InputMappingsFromPspButton(btn, &multiMappings, ignoreMouse);
77
mappings->clear();
78
for (auto &mapping : multiMappings) {
79
if (!mapping.empty()) {
80
mappings->push_back(mapping.mappings[0]);
81
} else {
82
WARN_LOG(Log::Common, "Encountered empty mapping in multi-mapping for button %d", btn);
83
}
84
}
85
}
86
87
// TODO: This is such a mess...
88
void UpdateNativeMenuKeys() {
89
std::vector<InputMapping> confirmKeys, cancelKeys;
90
std::vector<InputMapping> tabLeft, tabRight;
91
std::vector<InputMapping> upKeys, downKeys, leftKeys, rightKeys;
92
std::vector<InputMapping> infoKeys;
93
94
// Mouse mapping might be problematic in UI, so let's ignore mouse for UI
95
96
int confirmKey = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CROSS : CTRL_CIRCLE;
97
int cancelKey = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CIRCLE : CTRL_CROSS;
98
99
SingleInputMappingFromPspButton(confirmKey, &confirmKeys, true);
100
SingleInputMappingFromPspButton(cancelKey, &cancelKeys, true);
101
SingleInputMappingFromPspButton(CTRL_TRIANGLE, &infoKeys, true);
102
SingleInputMappingFromPspButton(CTRL_LTRIGGER, &tabLeft, true);
103
SingleInputMappingFromPspButton(CTRL_RTRIGGER, &tabRight, true);
104
SingleInputMappingFromPspButton(CTRL_UP, &upKeys, true);
105
SingleInputMappingFromPspButton(CTRL_DOWN, &downKeys, true);
106
SingleInputMappingFromPspButton(CTRL_LEFT, &leftKeys, true);
107
SingleInputMappingFromPspButton(CTRL_RIGHT, &rightKeys, true);
108
109
#ifdef __ANDROID__
110
// Hardcode DPAD on Android
111
upKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_UP));
112
downKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_DOWN));
113
leftKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_LEFT));
114
rightKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_RIGHT));
115
#endif
116
117
// Push several hard-coded keys before submitting to native.
118
const InputMapping hardcodedConfirmKeys[] = {
119
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_SPACE),
120
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ENTER),
121
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_NUMPAD_ENTER),
122
InputMapping(DEVICE_ID_ANY, NKCODE_BUTTON_A),
123
InputMapping(DEVICE_ID_PAD_0, NKCODE_DPAD_CENTER), // A number of Android devices.
124
};
125
126
// If they're not already bound, add them in.
127
for (size_t i = 0; i < ARRAY_SIZE(hardcodedConfirmKeys); i++) {
128
if (std::find(confirmKeys.begin(), confirmKeys.end(), hardcodedConfirmKeys[i]) == confirmKeys.end())
129
confirmKeys.push_back(hardcodedConfirmKeys[i]);
130
}
131
132
const InputMapping hardcodedCancelKeys[] = {
133
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE),
134
InputMapping(DEVICE_ID_ANY, NKCODE_BACK),
135
InputMapping(DEVICE_ID_ANY, NKCODE_BUTTON_B),
136
InputMapping(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_4),
137
};
138
139
for (size_t i = 0; i < ARRAY_SIZE(hardcodedCancelKeys); i++) {
140
if (std::find(cancelKeys.begin(), cancelKeys.end(), hardcodedCancelKeys[i]) == cancelKeys.end())
141
cancelKeys.push_back(hardcodedCancelKeys[i]);
142
}
143
144
const InputMapping hardcodedInfoKeys[] = {
145
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_S),
146
InputMapping(DEVICE_ID_KEYBOARD, NKCODE_NUMPAD_ADD),
147
InputMapping(DEVICE_ID_PAD_0, NKCODE_BUTTON_Y), // Also triangle
148
};
149
150
for (size_t i = 0; i < ARRAY_SIZE(hardcodedInfoKeys); i++) {
151
if (std::find(infoKeys.begin(), infoKeys.end(), hardcodedInfoKeys[i]) == infoKeys.end())
152
infoKeys.push_back(hardcodedInfoKeys[i]);
153
}
154
155
SetDPadKeys(upKeys, downKeys, leftKeys, rightKeys);
156
SetConfirmCancelKeys(confirmKeys, cancelKeys);
157
SetTabLeftRightKeys(tabLeft, tabRight);
158
SetInfoKeys(infoKeys);
159
160
std::unordered_map<InputDeviceID, int> flipYByDeviceId;
161
for (InputDeviceID deviceId : g_seenDeviceIds) {
162
auto analogs = MappedAxesForDevice(deviceId);
163
flipYByDeviceId[deviceId] = analogs.leftY.direction;
164
}
165
SetAnalogFlipY(flipYByDeviceId);
166
}
167
168
static const KeyMap_IntStrPair key_names[] = {
169
{NKCODE_A, "A"},
170
{NKCODE_B, "B"},
171
{NKCODE_C, "C"},
172
{NKCODE_D, "D"},
173
{NKCODE_E, "E"},
174
{NKCODE_F, "F"},
175
{NKCODE_G, "G"},
176
{NKCODE_H, "H"},
177
{NKCODE_I, "I"},
178
{NKCODE_J, "J"},
179
{NKCODE_K, "K"},
180
{NKCODE_L, "L"},
181
{NKCODE_M, "M"},
182
{NKCODE_N, "N"},
183
{NKCODE_O, "O"},
184
{NKCODE_P, "P"},
185
{NKCODE_Q, "Q"},
186
{NKCODE_R, "R"},
187
{NKCODE_S, "S"},
188
{NKCODE_T, "T"},
189
{NKCODE_U, "U"},
190
{NKCODE_V, "V"},
191
{NKCODE_W, "W"},
192
{NKCODE_X, "X"},
193
{NKCODE_Y, "Y"},
194
{NKCODE_Z, "Z"},
195
196
{NKCODE_0, "0"},
197
{NKCODE_1, "1"},
198
{NKCODE_2, "2"},
199
{NKCODE_3, "3"},
200
{NKCODE_4, "4"},
201
{NKCODE_5, "5"},
202
{NKCODE_6, "6"},
203
{NKCODE_7, "7"},
204
{NKCODE_8, "8"},
205
{NKCODE_9, "9"},
206
207
{NKCODE_F1, "F1"},
208
{NKCODE_F2, "F2"},
209
{NKCODE_F3, "F3"},
210
{NKCODE_F4, "F4"},
211
{NKCODE_F5, "F5"},
212
{NKCODE_F6, "F6"},
213
{NKCODE_F7, "F7"},
214
{NKCODE_F8, "F8"},
215
{NKCODE_F9, "F9"},
216
{NKCODE_F10, "F10"},
217
{NKCODE_F11, "F11"},
218
{NKCODE_F12, "F12"},
219
220
{NKCODE_GRAVE, "`"},
221
{NKCODE_SLASH, "/"},
222
{NKCODE_BACKSLASH, "\\"},
223
{NKCODE_SEMICOLON, ";"},
224
{NKCODE_COMMA, ","},
225
{NKCODE_PERIOD, "."},
226
{NKCODE_LEFT_BRACKET, "["},
227
{NKCODE_RIGHT_BRACKET, "]"},
228
{NKCODE_APOSTROPHE, "'"},
229
{NKCODE_MINUS, "-"},
230
{NKCODE_PLUS, "+"},
231
{NKCODE_PRINTSCREEN, "Print"},
232
{NKCODE_SCROLL_LOCK, "ScrLock"},
233
{NKCODE_BREAK, "Pause"},
234
235
{NKCODE_BACK, "Back"},
236
{NKCODE_TAB, "Tab"},
237
{NKCODE_ENTER, "Enter"},
238
{NKCODE_SHIFT_LEFT, "LShift"},
239
{NKCODE_SHIFT_RIGHT, "RShift"},
240
{NKCODE_CTRL_LEFT, "LCtrl"},
241
{NKCODE_CTRL_RIGHT, "RCtrl"},
242
{NKCODE_ALT_LEFT, "LAlt"},
243
{NKCODE_ALT_RIGHT, "RAlt"},
244
{NKCODE_META_LEFT, "LMeta"},
245
{NKCODE_META_RIGHT, "RMeta"},
246
{NKCODE_SPACE, "Space"},
247
{NKCODE_WINDOW, "Windows"},
248
{NKCODE_DEL, "Backspace"},
249
{NKCODE_FORWARD_DEL, "Delete"},
250
{NKCODE_MOVE_HOME, "Home"},
251
{NKCODE_MOVE_END, "End"},
252
{NKCODE_ESCAPE, "Esc"},
253
{NKCODE_CAPS_LOCK, "CapsLock"},
254
255
{NKCODE_VOLUME_UP, "Vol +"},
256
{NKCODE_VOLUME_DOWN, "Vol -"},
257
{NKCODE_HOME, "Home"},
258
{NKCODE_INSERT, "Ins"},
259
{NKCODE_PAGE_UP, "PgUp"},
260
{NKCODE_PAGE_DOWN, "PgDn"},
261
{NKCODE_CLEAR, "Clear"}, // 5 when numlock off
262
{NKCODE_CALL, "Call"},
263
{NKCODE_ENDCALL, "End Call"},
264
265
{NKCODE_DPAD_LEFT, "Left"},
266
{NKCODE_DPAD_UP, "Up"},
267
{NKCODE_DPAD_RIGHT, "Right"},
268
{NKCODE_DPAD_DOWN, "Down"},
269
270
{NKCODE_BUTTON_L1, "L1"},
271
{NKCODE_BUTTON_L2, "L2"},
272
{NKCODE_BUTTON_R1, "R1"},
273
{NKCODE_BUTTON_R2, "R2"},
274
275
{NKCODE_BUTTON_A, "[A]"},
276
{NKCODE_BUTTON_B, "[B]"},
277
{NKCODE_BUTTON_C, "[C]"},
278
{NKCODE_BUTTON_X, "[X]"},
279
{NKCODE_BUTTON_Y, "[Y]"},
280
{NKCODE_BUTTON_Z, "[Z]"},
281
{NKCODE_BUTTON_1, "b1"},
282
{NKCODE_BUTTON_2, "b2"},
283
{NKCODE_BUTTON_3, "b3"},
284
{NKCODE_BUTTON_4, "b4"},
285
{NKCODE_BUTTON_5, "b5"},
286
{NKCODE_BUTTON_6, "b6"},
287
{NKCODE_BUTTON_7, "b7"},
288
{NKCODE_BUTTON_8, "b8"},
289
{NKCODE_BUTTON_9, "b9"},
290
{NKCODE_BUTTON_10, "b10"},
291
{NKCODE_BUTTON_11, "b11"},
292
{NKCODE_BUTTON_12, "b12"},
293
{NKCODE_BUTTON_13, "b13"},
294
{NKCODE_BUTTON_14, "b14"},
295
{NKCODE_BUTTON_15, "b15"},
296
{NKCODE_BUTTON_16, "b16"},
297
{NKCODE_BUTTON_START, "Start"},
298
{NKCODE_BUTTON_SELECT, "Select"},
299
{NKCODE_BUTTON_CIRCLE, "Circle"},
300
{NKCODE_BUTTON_CIRCLE_PS3, "Circle3"},
301
{NKCODE_BUTTON_CROSS, "Cross"},
302
{NKCODE_BUTTON_CROSS_PS3, "Cross3"},
303
{NKCODE_BUTTON_TRIANGLE, "Triangle"},
304
{NKCODE_BUTTON_SQUARE, "Square"},
305
{NKCODE_BUTTON_THUMBL, "ThumbL"},
306
{NKCODE_BUTTON_THUMBR, "ThumbR"},
307
{NKCODE_BUTTON_MODE, "Mode"},
308
309
{NKCODE_EXT_PIPE, "|"},
310
{NKCODE_NUMPAD_DIVIDE, "Num/"},
311
{NKCODE_NUMPAD_MULTIPLY, "Num*"},
312
{NKCODE_NUMPAD_ADD, "Num+"},
313
{NKCODE_NUMPAD_SUBTRACT, "Num-"},
314
{NKCODE_NUMPAD_DOT, "Num."},
315
{NKCODE_NUMPAD_COMMA, "Num,"},
316
{NKCODE_NUMPAD_ENTER, "NumEnter"},
317
{NKCODE_NUMPAD_EQUALS, "Num="},
318
{NKCODE_NUMPAD_LEFT_PAREN, "Num("},
319
{NKCODE_NUMPAD_RIGHT_PAREN, "Num)"},
320
{NKCODE_NUMPAD_0, "Num0"},
321
{NKCODE_NUMPAD_1, "Num1"},
322
{NKCODE_NUMPAD_2, "Num2"},
323
{NKCODE_NUMPAD_3, "Num3"},
324
{NKCODE_NUMPAD_4, "Num4"},
325
{NKCODE_NUMPAD_5, "Num5"},
326
{NKCODE_NUMPAD_6, "Num6"},
327
{NKCODE_NUMPAD_7, "Num7"},
328
{NKCODE_NUMPAD_8, "Num8"},
329
{NKCODE_NUMPAD_9, "Num9"},
330
331
{NKCODE_LANGUAGE_SWITCH, "Language"},
332
{NKCODE_MANNER_MODE, "Manner"},
333
{NKCODE_3D_MODE, "3D Mode"},
334
{NKCODE_CONTACTS, "Contacts"},
335
{NKCODE_CALENDAR, "Calendar"},
336
{NKCODE_MUSIC, "Music"},
337
{NKCODE_CALCULATOR, "Calc"},
338
{NKCODE_ZENKAKU_HANKAKU, "Zenkaku"},
339
{NKCODE_EISU, "Eisu"},
340
{NKCODE_MUHENKAN, "Muhenkan"},
341
{NKCODE_HENKAN, "Henkan"},
342
{NKCODE_KATAKANA_HIRAGANA, "Katakana"},
343
{NKCODE_YEN, "Yen"},
344
{NKCODE_RO, "Ro"},
345
{NKCODE_KANA, "Kana"},
346
{NKCODE_ASSIST, "Assist"},
347
348
{NKCODE_EXT_MOUSEBUTTON_1, "MB1"},
349
{NKCODE_EXT_MOUSEBUTTON_2, "MB2"},
350
{NKCODE_EXT_MOUSEBUTTON_3, "MB3"},
351
{NKCODE_EXT_MOUSEBUTTON_4, "MB4"},
352
{NKCODE_EXT_MOUSEBUTTON_5, "MB5"},
353
{NKCODE_EXT_MOUSEWHEEL_UP, "MWheelU"},
354
{NKCODE_EXT_MOUSEWHEEL_DOWN, "MWheelD"},
355
356
{NKCODE_START_QUESTION, "¿"},
357
{NKCODE_LEFTBRACE, "{"},
358
{NKCODE_RIGHTBRACE, "}"},
359
360
{NKCODE_GUIDE, "Guide"},
361
{NKCODE_INFO, "Info"},
362
};
363
364
static const KeyMap_IntStrPair axis_names[] = {
365
{JOYSTICK_AXIS_X, "X Axis"},
366
{JOYSTICK_AXIS_Y, "Y Axis"},
367
{JOYSTICK_AXIS_PRESSURE, "Pressure"},
368
{JOYSTICK_AXIS_SIZE, "Size"},
369
{JOYSTICK_AXIS_TOUCH_MAJOR, "Touch Major"},
370
{JOYSTICK_AXIS_TOUCH_MINOR, "Touch Minor"},
371
{JOYSTICK_AXIS_TOOL_MAJOR, "Tool Major"},
372
{JOYSTICK_AXIS_TOOL_MINOR, "Tool Minor"},
373
{JOYSTICK_AXIS_ORIENTATION, "Orient"},
374
{JOYSTICK_AXIS_VSCROLL, "Vert Scroll"},
375
{JOYSTICK_AXIS_HSCROLL, "Horiz Scroll"},
376
{JOYSTICK_AXIS_Z, "Z Axis"}, // Also used as second stick X on many controllers - rename?
377
{JOYSTICK_AXIS_RX, "X Rotation"},
378
{JOYSTICK_AXIS_RY, "Y Rotation"},
379
{JOYSTICK_AXIS_RZ, "Z Rotation"}, // Also used as second stick Y on many controllers - rename?
380
{JOYSTICK_AXIS_HAT_X, "X HAT"},
381
{JOYSTICK_AXIS_HAT_Y, "Y HAT"},
382
{JOYSTICK_AXIS_LTRIGGER, "TriggerL"},
383
{JOYSTICK_AXIS_RTRIGGER, "TriggerR"},
384
{JOYSTICK_AXIS_THROTTLE, "Throttle"},
385
{JOYSTICK_AXIS_RUDDER, "Rudder"},
386
{JOYSTICK_AXIS_WHEEL, "Wheel"},
387
{JOYSTICK_AXIS_GAS, "Gas"},
388
{JOYSTICK_AXIS_BRAKE, "Brake"},
389
{JOYSTICK_AXIS_DISTANCE, "Distance"},
390
{JOYSTICK_AXIS_TILT, "Tilt"},
391
{JOYSTICK_AXIS_MOUSE_REL_X, "MouseDX"},
392
{JOYSTICK_AXIS_MOUSE_REL_Y, "MouseDY"},
393
{JOYSTICK_AXIS_ACCELEROMETER_X, "AccelX"},
394
{JOYSTICK_AXIS_ACCELEROMETER_Y, "AccelY"},
395
{JOYSTICK_AXIS_ACCELEROMETER_Z, "AccelZ"},
396
};
397
398
const KeyMap_IntStrPair psp_button_names[] = {
399
{CTRL_UP, "Up"},
400
{CTRL_DOWN, "Down"},
401
{CTRL_LEFT, "Left"},
402
{CTRL_RIGHT, "Right"},
403
{CTRL_CIRCLE, "Circle"},
404
{CTRL_CROSS, "Cross"},
405
{CTRL_SQUARE, "Square"},
406
{CTRL_TRIANGLE, "Triangle"},
407
{CTRL_START, "Start"},
408
{CTRL_SELECT, "Select"},
409
{CTRL_LTRIGGER, "L"},
410
{CTRL_RTRIGGER, "R"},
411
412
{VIRTKEY_AXIS_Y_MAX, "An.Up"},
413
{VIRTKEY_AXIS_Y_MIN, "An.Down"},
414
{VIRTKEY_AXIS_X_MIN, "An.Left"},
415
{VIRTKEY_AXIS_X_MAX, "An.Right"},
416
417
{VIRTKEY_ANALOG_ROTATE_CW, "Rotate Analog (CW)"},
418
{VIRTKEY_ANALOG_ROTATE_CCW, "Rotate Analog (CCW)"},
419
{VIRTKEY_ANALOG_LIGHTLY, "Analog limiter"},
420
{VIRTKEY_RAPID_FIRE, "RapidFire"},
421
{VIRTKEY_AXIS_SWAP, "AxisSwap"},
422
423
{VIRTKEY_FASTFORWARD, "Fast-forward"},
424
{VIRTKEY_PAUSE, "Pause"},
425
{VIRTKEY_PAUSE_NO_MENU, "Pause (no menu)"},
426
427
{VIRTKEY_SPEED_TOGGLE, "SpeedToggle"},
428
{VIRTKEY_SPEED_CUSTOM1, "Alt speed 1"},
429
{VIRTKEY_SPEED_CUSTOM2, "Alt speed 2"},
430
{VIRTKEY_SPEED_ANALOG, "Analog speed"},
431
{VIRTKEY_RESET_EMULATION, "Reset"},
432
{VIRTKEY_FRAME_ADVANCE, "Frame Advance"},
433
#if !defined(MOBILE_DEVICE)
434
{VIRTKEY_RECORD, "Audio/Video Recording" },
435
#endif
436
{VIRTKEY_REWIND, "Rewind"},
437
{VIRTKEY_SAVE_STATE, "Save State"},
438
{VIRTKEY_LOAD_STATE, "Load State"},
439
{VIRTKEY_PREVIOUS_SLOT, "Previous Slot"},
440
{VIRTKEY_NEXT_SLOT, "Next Slot"},
441
#if !defined(MOBILE_DEVICE)
442
{VIRTKEY_TOGGLE_FULLSCREEN, "Toggle Fullscreen"},
443
#endif
444
{VIRTKEY_TOGGLE_DEBUGGER, "Toggle Debugger"},
445
{VIRTKEY_TOGGLE_TILT, "Toggle tilt control"},
446
447
{VIRTKEY_OPENCHAT, "OpenChat" },
448
449
{VIRTKEY_DEVMENU, "DevMenu"},
450
{VIRTKEY_TEXTURE_DUMP, "Texture Dumping"},
451
{VIRTKEY_TEXTURE_REPLACE, "Texture Replacement"},
452
{VIRTKEY_SCREENSHOT, "Screenshot"},
453
{VIRTKEY_MUTE_TOGGLE, "Mute toggle"},
454
455
#ifdef OPENXR
456
{VIRTKEY_VR_CAMERA_ADJUST, "VR camera adjust"},
457
{VIRTKEY_VR_CAMERA_RESET, "VR camera reset"},
458
#else
459
{VIRTKEY_SCREEN_ROTATION_VERTICAL, "Display Portrait"},
460
{VIRTKEY_SCREEN_ROTATION_VERTICAL180, "Display Portrait Reversed"},
461
{VIRTKEY_SCREEN_ROTATION_HORIZONTAL, "Display Landscape"},
462
{VIRTKEY_SCREEN_ROTATION_HORIZONTAL180, "Display Landscape Reversed"},
463
#endif
464
465
{VIRTKEY_TOGGLE_WLAN, "Toggle WLAN"},
466
{VIRTKEY_EXIT_APP, "Exit App"},
467
468
{VIRTKEY_TOGGLE_MOUSE, "Toggle mouse input"},
469
{VIRTKEY_TOGGLE_TOUCH_CONTROLS, "Toggle touch controls"},
470
471
{VIRTKEY_AXIS_RIGHT_Y_MAX, "RightAn.Up"},
472
{VIRTKEY_AXIS_RIGHT_Y_MIN, "RightAn.Down"},
473
{VIRTKEY_AXIS_RIGHT_X_MIN, "RightAn.Left"},
474
{VIRTKEY_AXIS_RIGHT_X_MAX, "RightAn.Right"},
475
476
{CTRL_HOME, "Home"},
477
{CTRL_HOLD, "Hold"},
478
{CTRL_WLAN, "Wlan"},
479
{CTRL_REMOTE_HOLD, "Remote hold"},
480
{CTRL_VOL_UP, "Vol +"},
481
{CTRL_VOL_DOWN, "Vol -"},
482
{CTRL_SCREEN, "Screen"},
483
{CTRL_NOTE, "Note"},
484
{CTRL_L2, "Dev-kit L2"},
485
{CTRL_L3, "Dev-kit L3"},
486
{CTRL_R2, "Dev-kit R2"},
487
{CTRL_R3, "Dev-kit R3"},
488
};
489
490
// key here can be other things than InputKeyCode.
491
static std::string FindName(int key, const KeyMap_IntStrPair list[], size_t size) {
492
for (size_t i = 0; i < size; i++) {
493
if (list[i].key == key)
494
return list[i].name;
495
}
496
return StringFromFormat("%02x?", key);
497
}
498
499
std::string GetKeyName(InputKeyCode keyCode) {
500
return FindName(keyCode, key_names, ARRAY_SIZE(key_names));
501
}
502
503
std::string GetKeyOrAxisName(const InputMapping &mapping) {
504
if (mapping.IsAxis()) {
505
int direction;
506
int axis = mapping.Axis(&direction);
507
std::string temp = GetAxisName(axis);
508
if (direction == 1)
509
temp += "+";
510
else if (direction == -1)
511
temp += "-";
512
return temp;
513
} else {
514
return FindName(mapping.keyCode, key_names, ARRAY_SIZE(key_names));
515
}
516
}
517
518
std::string GetAxisName(int axisId) {
519
return FindName(axisId, axis_names, ARRAY_SIZE(axis_names));
520
}
521
522
std::string GetPspButtonName(int btn) {
523
return FindName(btn, psp_button_names, ARRAY_SIZE(psp_button_names));
524
}
525
526
const char* GetPspButtonNameCharPointer(int btn) {
527
for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++)
528
if (psp_button_names[i].key == btn)
529
return psp_button_names[i].name;
530
return nullptr;
531
}
532
533
const KeyMap::KeyMap_IntStrPair *GetMappableKeys(size_t *count) {
534
*count = ARRAY_SIZE(psp_button_names);
535
return psp_button_names;
536
}
537
538
bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspButtons) {
539
bool found = false;
540
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
541
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
542
for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {
543
if (iter2->EqualsSingleMapping(mapping)) {
544
if (pspButtons)
545
pspButtons->push_back(iter->first);
546
found = true;
547
}
548
}
549
}
550
return found;
551
}
552
553
// This is the main workhorse of the ControlMapper.
554
bool InputMappingsFromPspButtonNoLock(int btn, std::vector<MultiInputMapping> *mappings, bool ignoreMouse) {
555
auto iter = g_controllerMap.find(btn);
556
if (iter == g_controllerMap.end()) {
557
return false;
558
}
559
bool mapped = false;
560
if (mappings) {
561
mappings->clear();
562
}
563
for (auto &iter2 : iter->second) {
564
bool ignore = ignoreMouse && iter2.HasMouse();
565
if (!ignore) {
566
mapped = true;
567
if (mappings) {
568
mappings->push_back(iter2);
569
}
570
}
571
}
572
return mapped;
573
}
574
575
bool InputMappingsFromPspButton(int btn, std::vector<MultiInputMapping> *mappings, bool ignoreMouse) {
576
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
577
return InputMappingsFromPspButtonNoLock(btn, mappings, ignoreMouse);
578
}
579
580
void LockMappings() {
581
g_controllerMapLock.lock();
582
}
583
584
void UnlockMappings() {
585
g_controllerMapLock.unlock();
586
}
587
588
bool PspButtonHasMappings(int btn) {
589
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
590
auto iter = g_controllerMap.find(btn);
591
if (iter == g_controllerMap.end()) {
592
return false;
593
}
594
return !iter->second.empty();
595
}
596
597
MappedAnalogAxes MappedAxesForDevice(InputDeviceID deviceId) {
598
// Find the axisId mapped for a specific virtual button.
599
auto findAxisId = [&](int btn) -> MappedAnalogAxis {
600
MappedAnalogAxis info{ -1 };
601
for (const auto &key : g_controllerMap[btn]) {
602
// Only consider single mappings, combos don't make much sense for these.
603
if (key.mappings.empty()) continue;
604
auto &mapping = key.mappings[0];
605
if (mapping.deviceId == deviceId) {
606
info.axisId = TranslateKeyCodeToAxis(mapping.keyCode, &info.direction);
607
return info;
608
}
609
}
610
return info;
611
};
612
613
// Find the axisId of a pair of opposing buttons.
614
auto findAxisIdPair = [&](int minBtn, int maxBtn) -> MappedAnalogAxis {
615
MappedAnalogAxis foundMin = findAxisId(minBtn);
616
MappedAnalogAxis foundMax = findAxisId(maxBtn);
617
if (foundMin.axisId == foundMax.axisId) {
618
return foundMax;
619
}
620
return MappedAnalogAxis{ -1 };
621
};
622
623
MappedAnalogAxes result;
624
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
625
result.leftX = findAxisIdPair(VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX);
626
result.leftY = findAxisIdPair(VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX);
627
result.rightX = findAxisIdPair(VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX);
628
result.rightY = findAxisIdPair(VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX);
629
return result;
630
}
631
632
void RemoveButtonMapping(int btn) {
633
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
634
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
635
if (iter->first == btn) {
636
g_controllerMap.erase(iter);
637
return;
638
}
639
}
640
}
641
642
bool IsKeyMapped(InputDeviceID device, int key) {
643
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
644
for (auto &iter : g_controllerMap) {
645
for (auto &mappedKey : iter.second) {
646
if (mappedKey.mappings.contains(InputMapping(device, key))) {
647
return true;
648
}
649
}
650
}
651
return false;
652
}
653
654
bool ReplaceSingleKeyMapping(int btn, int index, const MultiInputMapping &key) {
655
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
656
// Check for duplicate
657
for (int i = 0; i < (int)g_controllerMap[btn].size(); ++i) {
658
if (i != index && g_controllerMap[btn][i] == key) {
659
g_controllerMap[btn].erase(g_controllerMap[btn].begin()+index);
660
g_controllerMapGeneration++;
661
662
UpdateNativeMenuKeys();
663
return false;
664
}
665
}
666
667
if (key.empty()) {
668
return false;
669
}
670
671
KeyMap::g_controllerMap[btn][index] = key;
672
g_controllerMapGeneration++;
673
674
for (auto &mapping : key.mappings) {
675
g_seenDeviceIds.insert(mapping.deviceId);
676
}
677
UpdateNativeMenuKeys();
678
return true;
679
}
680
681
void DeleteNthMapping(int key, int number) {
682
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
683
auto iter = g_controllerMap.find(key);
684
if (iter != g_controllerMap.end()) {
685
if (number < iter->second.size()) {
686
iter->second.erase(iter->second.begin() + number);
687
g_controllerMapGeneration++;
688
}
689
}
690
}
691
692
void SetInputMapping(int btn, const MultiInputMapping &key, bool replace) {
693
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
694
if (key.empty()) {
695
g_controllerMap.erase(btn);
696
return;
697
}
698
if (replace) {
699
RemoveButtonMapping(btn);
700
g_controllerMap[btn].clear();
701
g_controllerMap[btn].push_back(key);
702
} else {
703
for (auto iter = g_controllerMap[btn].begin(); iter != g_controllerMap[btn].end(); ++iter) {
704
if (*iter == key)
705
return;
706
}
707
g_controllerMap[btn].push_back(key);
708
}
709
g_controllerMapGeneration++;
710
711
for (auto &mapping : key.mappings) {
712
g_seenDeviceIds.insert(mapping.deviceId);
713
}
714
}
715
716
void RestoreDefault() {
717
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
718
g_controllerMap.clear();
719
g_controllerMapGeneration++;
720
721
if (IsVREnabled()) {
722
SetDefaultKeyMap(DEFAULT_MAPPING_VR_HEADSET, false);
723
return;
724
}
725
726
#if PPSSPP_PLATFORM(WINDOWS)
727
SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true);
728
SetDefaultKeyMap(DEFAULT_MAPPING_XINPUT, false);
729
SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);
730
#elif PPSSPP_PLATFORM(ANDROID)
731
// Autodetect a few common (and less common) devices
732
// Note that here we check the device name, not the controller name. We don't get
733
// the controller name until a button has been pressed so can't use it to set defaults.
734
std::string name = System_GetProperty(SYSPROP_NAME);
735
if (IsNvidiaShield(name)) {
736
SetDefaultKeyMap(DEFAULT_MAPPING_SHIELD, false);
737
} else if (IsXperiaPlay(name)) {
738
SetDefaultKeyMap(DEFAULT_MAPPING_XPERIA_PLAY, false);
739
} else if (IsMOQII7S(name)) {
740
SetDefaultKeyMap(DEFAULT_MAPPING_MOQI_I7S, false);
741
} else if (IsRetroid(name)) {
742
SetDefaultKeyMap(DEFAULT_MAPPING_RETROID_CONTROLLER, false);
743
} else {
744
SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_PAD, false);
745
}
746
#elif PPSSPP_PLATFORM(IOS)
747
SetDefaultKeyMap(DEFAULT_MAPPING_IOS_PAD, false);
748
#else
749
SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true);
750
SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);
751
#endif
752
}
753
754
// TODO: Make the ini format nicer.
755
void LoadFromIni(IniFile &file) {
756
RestoreDefault();
757
if (!file.HasSection("ControlMapping")) {
758
return;
759
}
760
761
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
762
763
Section *controls = file.GetOrCreateSection("ControlMapping");
764
for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++) {
765
std::string value;
766
controls->Get(psp_button_names[i].name, &value);
767
768
// Erase default mapping
769
g_controllerMap.erase(psp_button_names[i].key);
770
if (value.empty())
771
continue;
772
773
std::vector<std::string> mappings;
774
SplitString(value, ',', mappings);
775
776
for (size_t j = 0; j < mappings.size(); j++) {
777
MultiInputMapping input = MultiInputMapping::FromConfigString(mappings[j]);
778
if (input.empty()) {
779
continue; // eat empty mappings, however they arose, so they can't keep haunting us.
780
}
781
SetInputMapping(psp_button_names[i].key, input, false);
782
for (auto mapping : input.mappings) {
783
g_seenDeviceIds.insert(mapping.deviceId);
784
}
785
}
786
}
787
788
UpdateNativeMenuKeys();
789
}
790
791
void SaveToIni(IniFile &file) {
792
Section *controls = file.GetOrCreateSection("ControlMapping");
793
794
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
795
796
for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++) {
797
std::vector<MultiInputMapping> keys;
798
InputMappingsFromPspButton(psp_button_names[i].key, &keys, false);
799
800
std::string value;
801
for (size_t j = 0; j < keys.size(); j++) {
802
value += keys[j].ToConfigString();
803
if (j != keys.size() - 1)
804
value += ",";
805
}
806
807
controls->Set(psp_button_names[i].name, value, "");
808
}
809
}
810
811
void ClearAllMappings() {
812
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
813
g_controllerMap.clear();
814
g_controllerMapGeneration++;
815
}
816
817
bool IsNvidiaShield(std::string_view name) {
818
return name == "NVIDIA:SHIELD";
819
}
820
821
bool IsRetroid(std::string_view name) {
822
// TODO: Not sure if there are differences between different Retroid devices.
823
// The one I have is a "Retroid Pocket 2+".
824
return startsWith(name, "Retroid:");
825
}
826
827
bool IsNvidiaShieldTV(std::string_view name) {
828
return name == "NVIDIA:SHIELD Android TV";
829
}
830
831
bool IsXperiaPlay(std::string_view name) {
832
return name == "Sony Ericsson:R800a" || name == "Sony Ericsson:R800i" || name == "Sony Ericsson:R800x" || name == "Sony Ericsson:R800at" || name == "Sony Ericsson:SO-01D" || name == "Sony Ericsson:zeus";
833
}
834
835
bool IsMOQII7S(std::string_view name) {
836
return name == "MOQI:I7S";
837
}
838
839
bool HasBuiltinController(std::string_view name) {
840
return IsXperiaPlay(name) || IsNvidiaShield(name) || IsMOQII7S(name) || IsRetroid(name);
841
}
842
843
void NotifyPadConnected(InputDeviceID deviceId, std::string_view name) {
844
{
845
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
846
g_seenPads.insert(std::string(name));
847
g_padNames[deviceId] = name;
848
849
// Don't notify within the first 5 seconds, to avoid notification spam on startup.
850
// Also for some reason we get some strange things on Android... "Virtual"?
851
if (time_now_d() >= 5.0 && name != "Virtual") {
852
auto co = GetI18NCategory(I18NCat::CONTROLS);
853
g_OSD.Show(OSDType::MESSAGE_SUCCESS, ApplySafeSubstitutions("%1: %2", co->T("Game controller connected"), name), "", "I_CONTROLLER", 2.0f, "controller_connected");
854
}
855
}
856
857
System_Notify(SystemNotification::PAD_STATE_CHANGED);
858
}
859
860
void NotifyPadDisconnected(InputDeviceID deviceId) {
861
{
862
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
863
auto iter = g_padNames.find(deviceId);
864
if (iter != g_padNames.end()) {
865
auto co = GetI18NCategory(I18NCat::CONTROLS);
866
g_OSD.Show(OSDType::MESSAGE_WARNING, ApplySafeSubstitutions("%1: %2", co->T("Game controller disconnected"), iter->second), "", "I_CONTROLLER", 2.0f, "controller_connected");
867
g_seenPads.erase(iter->second);
868
}
869
g_padNames.erase(deviceId);
870
}
871
System_Notify(SystemNotification::PAD_STATE_CHANGED);
872
}
873
874
void ClearControlsWithDeviceId(InputDeviceID deviceId) {
875
bool modified = false;
876
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
877
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
878
auto &mappings = iter->second;
879
for (auto mapIter = mappings.begin(); mapIter != mappings.end(); ) {
880
bool found = false;
881
for (auto &mapping : mapIter->mappings) {
882
if (mapping.deviceId == deviceId) {
883
found = true;
884
break;
885
}
886
}
887
if (found) {
888
mapIter = mappings.erase(mapIter);
889
modified = true;
890
} else {
891
++mapIter;
892
}
893
}
894
}
895
896
if (modified) {
897
g_controllerMapGeneration++;
898
}
899
}
900
901
void AutoConfForPad(std::string_view name) {
902
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
903
904
InputDeviceID deviceId = DEVICE_ID_PAD_0;
905
for (auto [padDeviceId, padName] : g_padNames) {
906
if (padName == name) {
907
// Already configured.
908
deviceId = padDeviceId;
909
}
910
}
911
ClearControlsWithDeviceId(deviceId);
912
913
#if PPSSPP_PLATFORM(ANDROID)
914
if (name.find("Xbox") != std::string::npos) {
915
SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_XBOX, false);
916
} else if (name == "Retro Station Controller") {
917
SetDefaultKeyMap(DEFAULT_MAPPING_RETROID_CONTROLLER, false);
918
} else {
919
SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_PAD, false);
920
}
921
#else
922
#if PPSSPP_PLATFORM(WINDOWS)
923
const bool platformSupportsXinput = true;
924
#else
925
const bool platformSupportsXinput = false;
926
#endif
927
if (platformSupportsXinput && name.find("Xbox") != std::string::npos) {
928
SetDefaultKeyMap(DEFAULT_MAPPING_XINPUT, false);
929
} else {
930
SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);
931
}
932
#endif
933
934
// Add a couple of convenient keyboard mappings by default, too.
935
#if !defined(MOBILE_DEVICE)
936
g_controllerMap[VIRTKEY_PAUSE].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE)));
937
g_controllerMap[VIRTKEY_FASTFORWARD].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_TAB)));
938
#endif
939
g_controllerMapGeneration++;
940
}
941
942
const std::set<std::string> &GetSeenPads() {
943
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
944
return g_seenPads;
945
}
946
947
std::string PadName(InputDeviceID deviceId) {
948
std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);
949
auto it = g_padNames.find(deviceId);
950
if (it != g_padNames.end())
951
return it->second;
952
return "";
953
}
954
955
bool HasChanged(int &prevGeneration) {
956
if (prevGeneration != g_controllerMapGeneration) {
957
prevGeneration = g_controllerMapGeneration;
958
return true;
959
}
960
return false;
961
}
962
963
MultiInputMapping MultiInputMapping::FromConfigString(std::string_view str) {
964
MultiInputMapping out;
965
std::vector<std::string_view> parts;
966
SplitString(str, ':', parts);
967
for (auto iter : parts) {
968
out.mappings.push_back(InputMapping::FromConfigString(iter));
969
}
970
return out;
971
}
972
973
std::string MultiInputMapping::ToConfigString() const {
974
std::string out;
975
for (auto iter : mappings) {
976
out += iter.ToConfigString() + ":";
977
}
978
out.pop_back(); // remove the last ':'
979
return out;
980
}
981
982
std::string MultiInputMapping::ToVisualString() const {
983
std::string out;
984
for (auto iter : mappings) {
985
out += std::string(GetDeviceName(iter.deviceId)) + "." + GetKeyOrAxisName(iter) + " + ";
986
}
987
if (!out.empty()) {
988
// remove the last ' + '
989
out.pop_back();
990
out.pop_back();
991
out.pop_back();
992
}
993
return out;
994
}
995
996
} // KeyMap
997
998