Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/ControlMapper.cpp
3185 views
1
#include <algorithm>
2
#include <sstream>
3
4
#include "Common/Math/math_util.h"
5
#include "Common/TimeUtil.h"
6
#include "Common/StringUtils.h"
7
#include "Common/Log.h"
8
9
#include "Core/HLE/sceCtrl.h"
10
#include "Core/KeyMap.h"
11
#include "Core/ControlMapper.h"
12
#include "Core/Config.h"
13
#include "Core/CoreParameter.h"
14
#include "Core/System.h"
15
16
using KeyMap::MultiInputMapping;
17
18
const float AXIS_BIND_THRESHOLD = 0.75f;
19
const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f;
20
21
22
// We reduce the threshold of some axes when another axis on the same stick is active.
23
// This makes it easier to hit diagonals if you bind an analog stick to four face buttons or D-Pad.
24
static InputAxis GetCoAxis(InputAxis axis) {
25
switch (axis) {
26
case JOYSTICK_AXIS_X: return JOYSTICK_AXIS_Y;
27
case JOYSTICK_AXIS_Y: return JOYSTICK_AXIS_X;
28
29
// This looks weird, but it's simply how XInput axes are mapped.
30
case JOYSTICK_AXIS_Z: return JOYSTICK_AXIS_RZ;
31
case JOYSTICK_AXIS_RZ: return JOYSTICK_AXIS_Z;
32
33
// Not sure if these two are used.
34
case JOYSTICK_AXIS_RX: return JOYSTICK_AXIS_RY;
35
case JOYSTICK_AXIS_RY: return JOYSTICK_AXIS_RX;
36
37
default:
38
return JOYSTICK_AXIS_MAX; // invalid
39
}
40
}
41
42
float ControlMapper::GetDeviceAxisThreshold(int device, const InputMapping &mapping) {
43
if (device == DEVICE_ID_MOUSE) {
44
return AXIS_BIND_THRESHOLD_MOUSE;
45
}
46
if (mapping.IsAxis()) {
47
switch (KeyMap::GetAxisType((InputAxis)mapping.Axis(nullptr))) {
48
case KeyMap::AxisType::TRIGGER:
49
return g_Config.fAnalogTriggerThreshold;
50
case KeyMap::AxisType::STICK:
51
{
52
// Co-axis processing, see GetCoAxes comment.
53
InputAxis axis = (InputAxis)mapping.Axis(nullptr);
54
InputAxis coAxis = GetCoAxis(axis);
55
if (coAxis != JOYSTICK_AXIS_MAX) {
56
float absCoValue = fabsf(rawAxisValue_[(int)coAxis]);
57
if (absCoValue > 0.0f) {
58
// Bias down the threshold if the other axis is active.
59
float biasedThreshold = AXIS_BIND_THRESHOLD * (1.0f - absCoValue * 0.35f);
60
// INFO_LOG(Log::System, "coValue: %f threshold: %f", absCoValue, biasedThreshold);
61
return biasedThreshold;
62
}
63
}
64
break;
65
}
66
default:
67
break;
68
}
69
}
70
return AXIS_BIND_THRESHOLD;
71
}
72
73
static int GetOppositeVKey(int vkey) {
74
switch (vkey) {
75
case VIRTKEY_AXIS_X_MIN: return VIRTKEY_AXIS_X_MAX; break;
76
case VIRTKEY_AXIS_X_MAX: return VIRTKEY_AXIS_X_MIN; break;
77
case VIRTKEY_AXIS_Y_MIN: return VIRTKEY_AXIS_Y_MAX; break;
78
case VIRTKEY_AXIS_Y_MAX: return VIRTKEY_AXIS_Y_MIN; break;
79
case VIRTKEY_AXIS_RIGHT_X_MIN: return VIRTKEY_AXIS_RIGHT_X_MAX; break;
80
case VIRTKEY_AXIS_RIGHT_X_MAX: return VIRTKEY_AXIS_RIGHT_X_MIN; break;
81
case VIRTKEY_AXIS_RIGHT_Y_MIN: return VIRTKEY_AXIS_RIGHT_Y_MAX; break;
82
case VIRTKEY_AXIS_RIGHT_Y_MAX: return VIRTKEY_AXIS_RIGHT_Y_MIN; break;
83
default:
84
return 0;
85
}
86
}
87
88
static bool IsUnsignedMapping(int vkey) {
89
return vkey == VIRTKEY_SPEED_ANALOG;
90
}
91
92
static bool IsSignedAxis(int axis) {
93
switch (axis) {
94
case JOYSTICK_AXIS_X:
95
case JOYSTICK_AXIS_Y:
96
case JOYSTICK_AXIS_Z:
97
case JOYSTICK_AXIS_RX:
98
case JOYSTICK_AXIS_RY:
99
case JOYSTICK_AXIS_RZ:
100
return true;
101
default:
102
return false;
103
}
104
}
105
106
// This is applied on the circular radius, not directly on the axes.
107
// TODO: Share logic with tilt?
108
109
static float MapAxisValue(float v) {
110
const float deadzone = g_Config.fAnalogDeadzone;
111
const float invDeadzone = g_Config.fAnalogInverseDeadzone;
112
const float sensitivity = g_Config.fAnalogSensitivity;
113
const float sign = v >= 0.0f ? 1.0f : -1.0f;
114
115
// Apply deadzone.
116
v = Clamp((fabsf(v) - deadzone) / (1.0f - deadzone), 0.0f, 1.0f);
117
118
// Apply sensitivity and inverse deadzone.
119
if (v != 0.0f) {
120
v = Clamp(invDeadzone + v * (sensitivity - invDeadzone), 0.0f, 1.0f);
121
}
122
123
return sign * v;
124
}
125
126
void ConvertAnalogStick(float x, float y, float *outX, float *outY) {
127
const bool isCircular = g_Config.bAnalogIsCircular;
128
129
float norm = std::max(fabsf(x), fabsf(y));
130
if (norm == 0.0f) {
131
*outX = x;
132
*outY = y;
133
return;
134
}
135
136
if (isCircular) {
137
float newNorm = sqrtf(x * x + y * y);
138
float factor = newNorm / norm;
139
x *= factor;
140
y *= factor;
141
norm = newNorm;
142
}
143
144
float mappedNorm = MapAxisValue(norm);
145
*outX = Clamp(x / norm * mappedNorm, -1.0f, 1.0f);
146
*outY = Clamp(y / norm * mappedNorm, -1.0f, 1.0f);
147
}
148
149
void ControlMapper::SetCallbacks(
150
std::function<void(VirtKey, bool)> onVKey,
151
std::function<void(VirtKey, float)> onVKeyAnalog,
152
std::function<void(uint32_t, uint32_t)> updatePSPButtons,
153
std::function<void(int, float, float)> setPSPAnalog,
154
std::function<void(int, float, float)> setRawAnalog) {
155
onVKey_ = onVKey;
156
onVKeyAnalog_ = onVKeyAnalog;
157
updatePSPButtons_ = updatePSPButtons;
158
setPSPAnalog_ = setPSPAnalog;
159
setRawAnalog_ = setRawAnalog;
160
}
161
162
void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) {
163
const int axisId = axis == 'X' ? 0 : 1;
164
if (stick != 0 && stick != 1) {
165
return;
166
}
167
168
float position[2];
169
position[0] = history_[stick][0];
170
position[1] = history_[stick][1];
171
172
position[axisId] = value;
173
174
float x = position[0];
175
float y = position[1];
176
177
if (setRawAnalog_) {
178
setRawAnalog_(stick, x, y);
179
}
180
181
// NOTE: We need to use single-axis checks, since the other axis might be from another device,
182
// so we'll add a little leeway.
183
bool inDeadZone = fabsf(value) < g_Config.fAnalogDeadzone * 0.7f;
184
185
bool ignore = false;
186
if (inDeadZone && lastNonDeadzoneDeviceID_[stick] != device) {
187
// Ignore this event! See issue #15465
188
ignore = true;
189
}
190
191
if (!inDeadZone) {
192
lastNonDeadzoneDeviceID_[stick] = device;
193
}
194
195
if (!ignore) {
196
history_[stick][axisId] = value;
197
198
UpdateAnalogOutput(stick);
199
}
200
}
201
202
void ControlMapper::UpdateAnalogOutput(int stick) {
203
float x, y;
204
ConvertAnalogStick(history_[stick][0], history_[stick][1], &x, &y);
205
if (virtKeyOn_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST]) {
206
x *= g_Config.fAnalogLimiterDeadzone;
207
y *= g_Config.fAnalogLimiterDeadzone;
208
}
209
converted_[stick][0] = x;
210
converted_[stick][1] = y;
211
setPSPAnalog_(stick, x, y);
212
}
213
214
void ControlMapper::ForceReleaseVKey(int vkey) {
215
// Note: This one is called from an onVKey_ handler, which already holds mutex_.
216
217
KeyMap::LockMappings();
218
std::vector<KeyMap::MultiInputMapping> multiMappings;
219
if (KeyMap::InputMappingsFromPspButtonNoLock(vkey, &multiMappings, true)) {
220
double now = time_now_d();
221
for (const auto &entry : multiMappings) {
222
for (const auto &mapping : entry.mappings) {
223
curInput_[mapping] = { 0.0f, now };
224
// Different logic for signed axes?
225
UpdatePSPState(mapping, now);
226
}
227
}
228
}
229
KeyMap::UnlockMappings();
230
}
231
232
void ControlMapper::ReleaseAll() {
233
std::vector<AxisInput> axes;
234
std::vector<KeyInput> keys;
235
236
{
237
std::lock_guard<std::mutex> guard(mutex_);
238
239
for (const auto &input : curInput_) {
240
if (input.first.IsAxis()) {
241
if (input.second.value != 0.0f) {
242
AxisInput axis;
243
axis.deviceId = input.first.deviceId;
244
int dir;
245
axis.axisId = (InputAxis)input.first.Axis(&dir);
246
axis.value = 0.0;
247
axes.push_back(axis);
248
}
249
} else {
250
if (input.second.value != 0.0) {
251
KeyInput key;
252
key.deviceId = input.first.deviceId;
253
key.flags = KEY_UP;
254
key.keyCode = (InputKeyCode)input.first.keyCode;
255
keys.push_back(key);
256
}
257
}
258
}
259
}
260
261
Axis(axes.data(), axes.size());;
262
for (const auto &key : keys) {
263
Key(key, nullptr);
264
}
265
}
266
267
268
static int RotatePSPKeyCode(int x) {
269
switch (x) {
270
case CTRL_UP: return CTRL_RIGHT;
271
case CTRL_RIGHT: return CTRL_DOWN;
272
case CTRL_DOWN: return CTRL_LEFT;
273
case CTRL_LEFT: return CTRL_UP;
274
default:
275
return x;
276
}
277
}
278
279
// Used to decay analog values when clashing with digital ones.
280
static ControlMapper::InputSample ReduceMagnitude(ControlMapper::InputSample sample, double now) {
281
float reduction = std::min(std::max(0.0f, (float)(now - sample.timestamp) - 2.0f), 1.0f);
282
if (reduction > 0.0f) {
283
sample.value *= (1.0f - reduction);
284
}
285
if ((sample.value > 0.0f && sample.value < 0.05f) || (sample.value < 0.0f && sample.value > -0.05f)) {
286
sample.value = 0.0f;
287
}
288
return sample;
289
}
290
291
float ControlMapper::MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched) {
292
if (IsUnsignedMapping(vkId)) {
293
// If a signed axis is mapped to an unsigned mapping,
294
// convert it. This happens when mapping DirectInput triggers to analog speed,
295
// for example.
296
int direction;
297
if (IsSignedAxis(mapping.Axis(&direction))) {
298
// The value has been split up into two curInput values, so we need to go fetch the other
299
// and put them back together again. Kind of awkward, but at least makes the regular case simple...
300
InputMapping other = mapping.FlipDirection();
301
if (other == changedMapping) {
302
*oppositeTouched = true;
303
}
304
float valueOther = curInput_[other].value;
305
float signedValue = value - valueOther;
306
float ranged = (signedValue + 1.0f) * 0.5f;
307
if (direction == -1) {
308
ranged = 1.0f - ranged;
309
}
310
// NOTICE_LOG(Log::System, "rawValue: %f other: %f signed: %f ranged: %f", iter->second, valueOther, signedValue, ranged);
311
return ranged;
312
} else {
313
return value;
314
}
315
} else {
316
return value;
317
}
318
}
319
320
static bool IsSwappableVKey(uint32_t vkey) {
321
switch (vkey) {
322
case CTRL_UP:
323
case CTRL_LEFT:
324
case CTRL_DOWN:
325
case CTRL_RIGHT:
326
case VIRTKEY_AXIS_X_MIN:
327
case VIRTKEY_AXIS_X_MAX:
328
case VIRTKEY_AXIS_Y_MIN:
329
case VIRTKEY_AXIS_Y_MAX:
330
return true;
331
default:
332
return false;
333
}
334
}
335
336
void ControlMapper::SwapMappingIfEnabled(uint32_t *vkey) {
337
if (swapAxes_) {
338
switch (*vkey) {
339
case CTRL_UP: *vkey = VIRTKEY_AXIS_Y_MAX; break;
340
case VIRTKEY_AXIS_Y_MAX: *vkey = CTRL_UP; break;
341
case CTRL_DOWN: *vkey = VIRTKEY_AXIS_Y_MIN; break;
342
case VIRTKEY_AXIS_Y_MIN: *vkey = CTRL_DOWN; break;
343
case CTRL_LEFT: *vkey = VIRTKEY_AXIS_X_MIN; break;
344
case VIRTKEY_AXIS_X_MIN: *vkey = CTRL_LEFT; break;
345
case CTRL_RIGHT: *vkey = VIRTKEY_AXIS_X_MAX; break;
346
case VIRTKEY_AXIS_X_MAX: *vkey = CTRL_RIGHT; break;
347
}
348
}
349
}
350
351
// Can only be called from Key or Axis.
352
// mutex_ should be locked, and also KeyMap::LockMappings().
353
// TODO: We should probably make a batched version of this.
354
bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping, double now) {
355
// Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and
356
// see if the input that corresponds to it has a value. That way we can easily implement all sorts
357
// of crazy input combos if needed.
358
359
int rotations = 0;
360
switch (g_Config.iInternalScreenRotation) {
361
case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break;
362
case ROTATION_LOCKED_VERTICAL: rotations = 1; break;
363
case ROTATION_LOCKED_VERTICAL180: rotations = 3; break;
364
}
365
366
// For the PSP's digital button inputs, we just go through and put the flags together.
367
uint32_t buttonMask = 0;
368
uint32_t changedButtonMask = 0;
369
std::vector<MultiInputMapping> inputMappings;
370
for (int i = 0; i < 32; i++) {
371
uint32_t mask = 1 << i;
372
if (!(mask & CTRL_MASK_USER)) {
373
// Not a mappable button bit
374
continue;
375
}
376
377
uint32_t mappingBit = mask;
378
for (int i = 0; i < rotations; i++) {
379
mappingBit = RotatePSPKeyCode(mappingBit);
380
}
381
382
SwapMappingIfEnabled(&mappingBit);
383
if (!KeyMap::InputMappingsFromPspButtonNoLock(mappingBit, &inputMappings, false))
384
continue;
385
386
// If a mapping could consist of a combo, we could trivially check it here.
387
for (auto &multiMapping : inputMappings) {
388
// Check if the changed mapping was involved in this PSP key.
389
if (multiMapping.mappings.contains(changedMapping)) {
390
changedButtonMask |= mask;
391
}
392
// Check if all inputs are "on".
393
bool all = true;
394
double curTime = 0.0;
395
for (auto mapping : multiMapping.mappings) {
396
auto iter = curInput_.find(mapping);
397
if (iter == curInput_.end()) {
398
all = false;
399
continue;
400
}
401
// Stop reverse ordering from triggering.
402
if (g_Config.bStrictComboOrder && iter->second.timestamp < curTime) {
403
all = false;
404
break;
405
} else {
406
curTime = iter->second.timestamp;
407
}
408
bool down = iter->second.value > 0.0f && iter->second.value > GetDeviceAxisThreshold(iter->first.deviceId, mapping);
409
if (!down)
410
all = false;
411
}
412
if (all) {
413
buttonMask |= mask;
414
}
415
}
416
}
417
418
// We only request changing the buttons where the mapped input was involved.
419
updatePSPButtons_(buttonMask & changedButtonMask, (~buttonMask) & changedButtonMask);
420
421
bool keyInputUsed = changedButtonMask != 0;
422
bool updateAnalogSticks = false;
423
424
// OK, handle all the virtual keys next. For these we need to do deltas here and send events.
425
// Note that virtual keys include the analog directions, as they are driven by them.
426
for (int i = 0; i < VIRTKEY_COUNT; i++) {
427
VirtKey vkId = (VirtKey)(i + VIRTKEY_FIRST);
428
429
uint32_t idForMapping = vkId;
430
SwapMappingIfEnabled(&idForMapping);
431
432
if (!KeyMap::InputMappingsFromPspButtonNoLock(idForMapping, &inputMappings, false))
433
continue;
434
435
// If a mapping could consist of a combo, we could trivially check it here.
436
// Save the first device ID so we can pass it into onVKeyDown, which in turn needs it for the analog
437
// mapping which gets a little hacky.
438
float threshold = 1.0f;
439
bool touchedByMapping = false;
440
float value = 0.0f;
441
for (auto &multiMapping : inputMappings) {
442
if (multiMapping.mappings.contains(changedMapping)) {
443
touchedByMapping = true;
444
}
445
446
float product = 1.0f; // We multiply the various inputs in a combo mapping with each other.
447
double curTime = 0.0;
448
for (auto mapping : multiMapping.mappings) {
449
auto iter = curInput_.find(mapping);
450
451
if (iter != curInput_.end()) {
452
// Stop reverse ordering from triggering.
453
if (g_Config.bStrictComboOrder && iter->second.timestamp < curTime) {
454
product = 0.0f;
455
break;
456
} else {
457
curTime = iter->second.timestamp;
458
}
459
460
if (mapping.IsAxis()) {
461
threshold = GetDeviceAxisThreshold(iter->first.deviceId, mapping);
462
float value = MapAxisValue(iter->second.value, idForMapping, mapping, changedMapping, &touchedByMapping);
463
product *= value;
464
} else {
465
product *= iter->second.value;
466
}
467
} else {
468
product = 0.0f;
469
}
470
}
471
472
value += product;
473
}
474
475
if (!touchedByMapping) {
476
continue;
477
}
478
479
keyInputUsed = true;
480
481
// Small values from analog inputs like gamepad sticks can linger around, which is bad here because we sum
482
// up before applying deadzone etc. This means that it can be impossible to reach the min/max values with digital input!
483
// So if non-analog events clash with analog ones mapped to the same input, decay the analog input,
484
// which will quickly get things back to normal, while if it's intentional to use both at the same time for some reason,
485
// that still works, though a bit weaker. We could also zero here, but you never know who relies on such strange tricks..
486
// Note: This is an old problem, it didn't appear with the refactoring.
487
if (!changedMapping.IsAxis()) {
488
for (auto &multiMapping : inputMappings) {
489
for (auto &mapping : multiMapping.mappings) {
490
if (mapping != changedMapping && curInput_[mapping].value > 0.0f) {
491
// Note that this takes the time into account now - values will
492
// decay after a while, not immediately.
493
curInput_[mapping] = ReduceMagnitude(curInput_[mapping], now);
494
}
495
}
496
}
497
}
498
499
value = clamp_value(value, 0.0f, 1.0f);
500
501
// Derive bools from the floats using the device's threshold.
502
// NOTE: This must be before the equality check below.
503
bool bPrevValue = virtKeys_[i] >= threshold;
504
bool bValue = value >= threshold;
505
506
if (virtKeys_[i] != value) {
507
// INFO_LOG(Log::G3D, "vkeyanalog %s : %f", KeyMap::GetVirtKeyName(vkId), value);
508
onVKeyAnalog(changedMapping.deviceId, vkId, value);
509
virtKeys_[i] = value;
510
}
511
512
if (!bPrevValue && bValue) {
513
// INFO_LOG(Log::G3D, "vkeyon %s", KeyMap::GetVirtKeyName(vkId));
514
onVKey(vkId, true);
515
virtKeyOn_[vkId - VIRTKEY_FIRST] = true;
516
517
if (vkId == VIRTKEY_ANALOG_LIGHTLY) {
518
updateAnalogSticks = true;
519
}
520
} else if (bPrevValue && !bValue) {
521
// INFO_LOG(Log::G3D, "vkeyoff %s", KeyMap::GetVirtKeyName(vkId));
522
onVKey(vkId, false);
523
virtKeyOn_[vkId - VIRTKEY_FIRST] = false;
524
525
if (vkId == VIRTKEY_ANALOG_LIGHTLY) {
526
updateAnalogSticks = true;
527
}
528
}
529
}
530
531
if (updateAnalogSticks) {
532
// If "lightly" (analog limiter) was toggled, we need to update both computed stick outputs.
533
UpdateAnalogOutput(0);
534
UpdateAnalogOutput(1);
535
}
536
537
return keyInputUsed;
538
}
539
540
bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) {
541
if (key.flags & KEY_IS_REPEAT) {
542
// Claim that we handled this. Prevents volume key repeats from popping up the volume control on Android.
543
return true;
544
}
545
546
double now = time_now_d();
547
InputMapping mapping(key.deviceId, key.keyCode);
548
549
std::lock_guard<std::mutex> guard(mutex_);
550
551
if (key.deviceId < DEVICE_ID_COUNT) {
552
deviceTimestamps_[(int)key.deviceId] = now;
553
}
554
555
if (key.flags & KEY_DOWN) {
556
curInput_[mapping] = { 1.0f, now };
557
} else if (key.flags & KEY_UP) {
558
curInput_[mapping] = { 0.0f, now};
559
}
560
561
// TODO: See if this can be simplified further somehow.
562
if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) {
563
bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr);
564
DEBUG_LOG(Log::System, "Key: %d DeviceId: %d", key.keyCode, key.deviceId);
565
if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) {
566
*pauseTrigger = true;
567
return true;
568
}
569
}
570
571
KeyMap::LockMappings();
572
bool retval = UpdatePSPState(mapping, now);
573
KeyMap::UnlockMappings();
574
return retval;
575
}
576
577
void ControlMapper::ToggleSwapAxes() {
578
// Note: The lock is already locked here.
579
580
swapAxes_ = !swapAxes_;
581
582
updatePSPButtons_(0, CTRL_LEFT | CTRL_RIGHT | CTRL_UP | CTRL_DOWN);
583
584
for (VirtKey vkey = VIRTKEY_FIRST; vkey < VIRTKEY_LAST; vkey = (VirtKey)(vkey + 1)) {
585
if (IsSwappableVKey(vkey)) {
586
if (virtKeyOn_[vkey - VIRTKEY_FIRST]) {
587
onVKey_(vkey, false);
588
virtKeyOn_[vkey - VIRTKEY_FIRST] = false;
589
}
590
if (virtKeys_[vkey - VIRTKEY_FIRST] > 0.0f) {
591
onVKeyAnalog_(vkey, 0.0f);
592
virtKeys_[vkey - VIRTKEY_FIRST] = 0.0f;
593
}
594
}
595
}
596
597
history_[0][0] = 0.0f;
598
history_[0][1] = 0.0f;
599
600
UpdateAnalogOutput(0);
601
UpdateAnalogOutput(1);
602
}
603
604
void ControlMapper::UpdateCurInputAxis(const InputMapping &mapping, float value, double timestamp) {
605
InputSample &input = curInput_[mapping];
606
input.value = value;
607
if (value >= GetDeviceAxisThreshold(mapping.deviceId, mapping)) {
608
if (input.timestamp == 0.0) {
609
input.timestamp = time_now_d();
610
}
611
} else {
612
input.timestamp = 0.0;
613
}
614
}
615
616
void ControlMapper::Axis(const AxisInput *axes, size_t count) {
617
double now = time_now_d();
618
619
std::lock_guard<std::mutex> guard(mutex_);
620
621
KeyMap::LockMappings();
622
for (size_t i = 0; i < count; i++) {
623
const AxisInput &axis = axes[i];
624
625
if (axis.deviceId == DEVICE_ID_MOUSE && !g_Config.bMouseControl) {
626
continue;
627
}
628
629
size_t deviceIndex = (size_t)axis.deviceId; // this wraps -1 up high, so will get rejected on the next line.
630
if (deviceIndex < (size_t)DEVICE_ID_COUNT) {
631
deviceTimestamps_[deviceIndex] = now;
632
}
633
rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping
634
if (axis.value >= 0.0f) {
635
InputMapping mapping(axis.deviceId, axis.axisId, 1);
636
InputMapping opposite(axis.deviceId, axis.axisId, -1);
637
UpdateCurInputAxis(mapping, axis.value, now);
638
UpdateCurInputAxis(opposite, 0.0f, now);
639
UpdatePSPState(mapping, now);
640
UpdatePSPState(opposite, now);
641
} else if (axis.value < 0.0f) {
642
InputMapping mapping(axis.deviceId, axis.axisId, -1);
643
InputMapping opposite(axis.deviceId, axis.axisId, 1);
644
UpdateCurInputAxis(mapping, -axis.value, now);
645
UpdateCurInputAxis(opposite, 0.0f, now);
646
UpdatePSPState(mapping, now);
647
UpdatePSPState(opposite, now);
648
}
649
}
650
KeyMap::UnlockMappings();
651
}
652
653
void ControlMapper::Update(double now) {
654
if (autoRotatingAnalogCW_) {
655
// Clamp to a square
656
float x = std::min(1.0f, std::max(-1.0f, 1.42f * (float)cos(now * -g_Config.fAnalogAutoRotSpeed)));
657
float y = std::min(1.0f, std::max(-1.0f, 1.42f * (float)sin(now * -g_Config.fAnalogAutoRotSpeed)));
658
659
setPSPAnalog_(0, x, y);
660
} else if (autoRotatingAnalogCCW_) {
661
float x = std::min(1.0f, std::max(-1.0f, 1.42f * (float)cos(now * g_Config.fAnalogAutoRotSpeed)));
662
float y = std::min(1.0f, std::max(-1.0f, 1.42f * (float)sin(now * g_Config.fAnalogAutoRotSpeed)));
663
664
setPSPAnalog_(0, x, y);
665
}
666
}
667
668
void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) {
669
std::lock_guard<std::mutex> guard(mutex_);
670
if (pspKeyCode >= VIRTKEY_FIRST) {
671
int vk = pspKeyCode - VIRTKEY_FIRST;
672
if (flags & KEY_DOWN) {
673
virtKeys_[vk] = 1.0f;
674
onVKey((VirtKey)pspKeyCode, true);
675
onVKeyAnalog(deviceId, (VirtKey)pspKeyCode, 1.0f);
676
}
677
if (flags & KEY_UP) {
678
virtKeys_[vk] = 0.0f;
679
onVKey((VirtKey)pspKeyCode, false);
680
onVKeyAnalog(deviceId, (VirtKey)pspKeyCode, 0.0f);
681
}
682
} else {
683
// INFO_LOG(Log::System, "pspKey %d %d", pspKeyCode, flags);
684
if (flags & KEY_DOWN)
685
updatePSPButtons_(pspKeyCode, 0);
686
if (flags & KEY_UP)
687
updatePSPButtons_(0, pspKeyCode);
688
}
689
}
690
691
void ControlMapper::onVKeyAnalog(int deviceId, VirtKey vkey, float value) {
692
// Unfortunately, for digital->analog inputs to work sanely, we need to sum up
693
// with the opposite value too.
694
int stick = 0;
695
int axis = 'X';
696
int oppositeVKey = GetOppositeVKey(vkey);
697
float sign = 1.0f;
698
switch (vkey) {
699
case VIRTKEY_AXIS_X_MIN: sign = -1.0f; break;
700
case VIRTKEY_AXIS_X_MAX: break;
701
case VIRTKEY_AXIS_Y_MIN: axis = 'Y'; sign = -1.0f; break;
702
case VIRTKEY_AXIS_Y_MAX: axis = 'Y'; break;
703
case VIRTKEY_AXIS_RIGHT_X_MIN: stick = CTRL_STICK_RIGHT; sign = -1.0f; break;
704
case VIRTKEY_AXIS_RIGHT_X_MAX: stick = CTRL_STICK_RIGHT; break;
705
case VIRTKEY_AXIS_RIGHT_Y_MIN: stick = CTRL_STICK_RIGHT; axis = 'Y'; sign = -1.0f; break;
706
case VIRTKEY_AXIS_RIGHT_Y_MAX: stick = CTRL_STICK_RIGHT; axis = 'Y'; break;
707
default:
708
if (onVKeyAnalog_)
709
onVKeyAnalog_(vkey, value);
710
return;
711
}
712
if (oppositeVKey != 0) {
713
float oppVal = virtKeys_[oppositeVKey - VIRTKEY_FIRST];
714
if (oppVal != 0.0f) {
715
value -= oppVal;
716
// NOTICE_LOG(Log::sceCtrl, "Reducing %f by %f (from %08x : %s)", value, oppVal, oppositeVKey, KeyMap::GetPspButtonName(oppositeVKey).c_str());
717
}
718
}
719
SetPSPAxis(deviceId, stick, axis, sign * value);
720
}
721
722
void ControlMapper::onVKey(VirtKey vkey, bool down) {
723
switch (vkey) {
724
case VIRTKEY_ANALOG_ROTATE_CW:
725
if (down) {
726
autoRotatingAnalogCW_ = true;
727
autoRotatingAnalogCCW_ = false;
728
} else {
729
autoRotatingAnalogCW_ = false;
730
setPSPAnalog_(0, 0.0f, 0.0f);
731
}
732
break;
733
case VIRTKEY_ANALOG_ROTATE_CCW:
734
if (down) {
735
autoRotatingAnalogCW_ = false;
736
autoRotatingAnalogCCW_ = true;
737
} else {
738
autoRotatingAnalogCCW_ = false;
739
setPSPAnalog_(0, 0.0f, 0.0f);
740
}
741
break;
742
default:
743
if (onVKey_)
744
onVKey_(vkey, down);
745
break;
746
}
747
}
748
749
void ControlMapper::GetDebugString(char *buffer, size_t bufSize) const {
750
std::stringstream str;
751
for (auto &iter : curInput_) {
752
char temp[256];
753
iter.first.FormatDebug(temp, sizeof(temp));
754
str << temp << ": " << iter.second.value << std::endl;
755
}
756
for (int i = 0; i < ARRAY_SIZE(virtKeys_); i++) {
757
int vkId = VIRTKEY_FIRST + i;
758
if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY || vkId == VIRTKEY_SPEED_ANALOG) {
759
str << KeyMap::GetPspButtonName(vkId) << ": " << virtKeys_[i] << std::endl;
760
}
761
}
762
str << "Lstick: " << converted_[0][0] << ", " << converted_[0][1] << std::endl;
763
truncate_cpy(buffer, bufSize, str.str().c_str());
764
}
765
766