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