Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/GamepadEmu.cpp
3185 views
1
// Copyright (c) 2012- 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
20
#include "Common/Data/Color/RGBAUtil.h"
21
#include "Common/Data/Text/I18n.h"
22
#include "Common/System/Display.h"
23
#include "Common/System/System.h"
24
#include "Common/Render/TextureAtlas.h"
25
#include "Common/Math/math_util.h"
26
#include "Common/UI/Context.h"
27
28
#include "Common/Log.h"
29
#include "Common/TimeUtil.h"
30
#include "Core/Config.h"
31
#include "Core/Core.h"
32
#include "Core/System.h"
33
#include "Core/HLE/sceCtrl.h"
34
#include "Core/ControlMapper.h"
35
#include "UI/GamepadEmu.h"
36
37
const float TOUCH_SCALE_FACTOR = 1.5f;
38
39
static uint32_t usedPointerMask = 0;
40
static uint32_t analogPointerMask = 0;
41
42
static float g_gamepadOpacity;
43
static double g_lastTouch;
44
45
MultiTouchButton *primaryButton[TOUCH_MAX_POINTERS]{};
46
47
void GamepadUpdateOpacity(float force) {
48
if (force >= 0.0f) {
49
g_gamepadOpacity = force;
50
return;
51
}
52
if (coreState != CORE_RUNNING_CPU) {
53
g_gamepadOpacity = 0.0f;
54
return;
55
}
56
57
float fadeAfterSeconds = g_Config.iTouchButtonHideSeconds;
58
float fadeTransitionSeconds = std::min(fadeAfterSeconds, 0.5f);
59
float opacity = g_Config.iTouchButtonOpacity / 100.0f;
60
61
float multiplier = 1.0f;
62
float secondsWithoutTouch = time_now_d() - g_lastTouch;
63
if (secondsWithoutTouch >= fadeAfterSeconds && fadeAfterSeconds > 0.0f) {
64
if (secondsWithoutTouch >= fadeAfterSeconds + fadeTransitionSeconds) {
65
multiplier = 0.0f;
66
} else {
67
float secondsIntoFade = secondsWithoutTouch - fadeAfterSeconds;
68
multiplier = 1.0f - (secondsIntoFade / fadeTransitionSeconds);
69
}
70
}
71
72
g_gamepadOpacity = opacity * multiplier;
73
}
74
75
void GamepadTouch(bool reset) {
76
g_lastTouch = reset ? 0.0f : time_now_d();
77
}
78
79
float GamepadGetOpacity() {
80
return g_gamepadOpacity;
81
}
82
83
static u32 GetButtonColor() {
84
return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
85
}
86
87
GamepadView::GamepadView(const char *key, UI::LayoutParams *layoutParams) : UI::View(layoutParams), key_(key) {}
88
89
std::string GamepadView::DescribeText() const {
90
auto co = GetI18NCategory(I18NCat::CONTROLS);
91
return std::string(co->T(key_));
92
}
93
94
void MultiTouchButton::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
95
const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(bgImg_);
96
if (image) {
97
w = image->w * scale_;
98
h = image->h * scale_;
99
} else {
100
w = 0.0f;
101
h = 0.0f;
102
}
103
}
104
105
bool MultiTouchButton::CanGlide() const {
106
return g_Config.bTouchGliding;
107
}
108
109
bool MultiTouchButton::Touch(const TouchInput &input) {
110
_dbg_assert_(input.id >= 0 && input.id < TOUCH_MAX_POINTERS);
111
112
bool retval = GamepadView::Touch(input);
113
if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) {
114
pointerDownMask_ |= 1 << input.id;
115
usedPointerMask |= 1 << input.id;
116
if (CanGlide() && !primaryButton[input.id])
117
primaryButton[input.id] = this;
118
}
119
if (input.flags & TOUCH_MOVE) {
120
if (!(input.flags & TOUCH_MOUSE) || input.buttons) {
121
if (bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id))) {
122
if (CanGlide() && !primaryButton[input.id]) {
123
primaryButton[input.id] = this;
124
}
125
pointerDownMask_ |= 1 << input.id;
126
} else if (primaryButton[input.id] != this) {
127
pointerDownMask_ &= ~(1 << input.id);
128
}
129
}
130
}
131
if (input.flags & TOUCH_UP) {
132
pointerDownMask_ &= ~(1 << input.id);
133
usedPointerMask &= ~(1 << input.id);
134
primaryButton[input.id] = nullptr;
135
}
136
if (input.flags & TOUCH_RELEASE_ALL) {
137
pointerDownMask_ = 0;
138
usedPointerMask = 0;
139
}
140
return retval;
141
}
142
143
void MultiTouchButton::Draw(UIContext &dc) {
144
float opacity = g_gamepadOpacity;
145
if (opacity <= 0.0f)
146
return;
147
148
float scale = scale_;
149
if (IsDown()) {
150
if (g_Config.iTouchButtonStyle == 2) {
151
opacity *= 1.35f;
152
} else {
153
scale *= TOUCH_SCALE_FACTOR;
154
opacity *= 1.15f;
155
}
156
}
157
158
uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
159
uint32_t downBg = colorAlpha(0xFFFFFF, opacity * 0.5f);
160
uint32_t color = colorAlpha(0xFFFFFF, opacity);
161
162
if (IsDown() && g_Config.iTouchButtonStyle == 2) {
163
if (bgImg_ != bgDownImg_)
164
dc.Draw()->DrawImageRotated(bgDownImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), downBg, flipImageH_);
165
}
166
167
dc.Draw()->DrawImageRotated(bgImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), colorBg, flipImageH_);
168
169
int y = bounds_.centerY();
170
// Hack round the fact that the center of the rectangular picture the triangle is contained in
171
// is not at the "weight center" of the triangle.
172
if (img_ == ImageID("I_TRIANGLE"))
173
y -= 2.8f * scale;
174
dc.Draw()->DrawImageRotated(img_, bounds_.centerX(), y, scale, angle_ * (M_PI * 2 / 360.0f), color);
175
}
176
177
bool BoolButton::Touch(const TouchInput &input) {
178
bool lastDown = pointerDownMask_ != 0;
179
bool retval = MultiTouchButton::Touch(input);
180
bool down = pointerDownMask_ != 0;
181
182
if (down != lastDown) {
183
*value_ = down;
184
UI::EventParams params{ this };
185
params.a = down;
186
OnChange.Trigger(params);
187
}
188
return retval;
189
}
190
191
bool PSPButton::Touch(const TouchInput &input) {
192
bool lastDown = pointerDownMask_ != 0;
193
bool retval = MultiTouchButton::Touch(input);
194
bool down = pointerDownMask_ != 0;
195
if (down && !lastDown) {
196
if (g_Config.bHapticFeedback) {
197
System_Vibrate(HAPTIC_VIRTUAL_KEY);
198
}
199
__CtrlUpdateButtons(pspButtonBit_, 0);
200
} else if (lastDown && !down) {
201
__CtrlUpdateButtons(0, pspButtonBit_);
202
}
203
return retval;
204
}
205
206
bool CustomButton::IsDown() {
207
return (toggle_ && on_) || (!toggle_ && pointerDownMask_ != 0);
208
}
209
210
void CustomButton::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
211
MultiTouchButton::GetContentDimensions(dc, w, h);
212
if (invertedContextDimension_) {
213
float tmp = w;
214
w = h;
215
h = tmp;
216
}
217
}
218
219
bool CustomButton::Touch(const TouchInput &input) {
220
using namespace CustomKeyData;
221
bool lastDown = pointerDownMask_ != 0;
222
bool retval = MultiTouchButton::Touch(input);
223
bool down = pointerDownMask_ != 0;
224
225
if (down && !lastDown) {
226
if (g_Config.bHapticFeedback)
227
System_Vibrate(HAPTIC_VIRTUAL_KEY);
228
229
if (!repeat_) {
230
for (int i = 0; i < ARRAY_SIZE(customKeyList); i++) {
231
if (pspButtonBit_ & (1ULL << i)) {
232
controlMapper_->PSPKey(DEVICE_ID_TOUCH, customKeyList[i].c, (on_ && toggle_) ? KEY_UP : KEY_DOWN);
233
}
234
}
235
}
236
on_ = toggle_ ? !on_ : true;
237
} else if (!toggle_ && lastDown && !down) {
238
if (!repeat_) {
239
for (int i = 0; i < ARRAY_SIZE(customKeyList); i++) {
240
if (pspButtonBit_ & (1ULL << i)) {
241
controlMapper_->PSPKey(DEVICE_ID_TOUCH, customKeyList[i].c, KEY_UP);
242
}
243
}
244
}
245
on_ = false;
246
}
247
return retval;
248
}
249
250
void CustomButton::Update() {
251
MultiTouchButton::Update();
252
using namespace CustomKeyData;
253
254
if (repeat_) {
255
// Give the game some time to process the input, frame based so it's faster when fast-forwarding.
256
static constexpr int DOWN_FRAME = 5;
257
258
if (pressedFrames_ == 2*DOWN_FRAME) {
259
pressedFrames_ = 0;
260
} else if (pressedFrames_ == DOWN_FRAME) {
261
for (int i = 0; i < ARRAY_SIZE(customKeyList); i++) {
262
if (pspButtonBit_ & (1ULL << i)) {
263
controlMapper_->PSPKey(DEVICE_ID_TOUCH, customKeyList[i].c, KEY_UP);
264
}
265
}
266
} else if (on_ && pressedFrames_ == 0) {
267
for (int i = 0; i < ARRAY_SIZE(customKeyList); i++) {
268
if (pspButtonBit_ & (1ULL << i)) {
269
controlMapper_->PSPKey(DEVICE_ID_TOUCH, customKeyList[i].c, KEY_DOWN);
270
}
271
}
272
pressedFrames_ = 1;
273
}
274
275
if (pressedFrames_ > 0)
276
pressedFrames_++;
277
}
278
}
279
280
bool PSPButton::IsDown() {
281
return (__CtrlPeekButtonsVisual() & pspButtonBit_) != 0;
282
}
283
284
PSPDpad::PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams)
285
: GamepadView(key, layoutParams), arrowIndex_(arrowIndex), arrowDownIndex_(arrowDownIndex), overlayIndex_(overlayIndex),
286
scale_(scale), spacing_(spacing), dragPointerId_(-1), down_(0) {
287
}
288
289
void PSPDpad::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
290
const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(arrowIndex_);
291
if (image) {
292
w = 2.0f * D_pad_Radius * spacing_ + image->w * scale_;
293
h = w;
294
} else {
295
w = 0.0f;
296
h = 0.0f;
297
}
298
}
299
300
bool PSPDpad::Touch(const TouchInput &input) {
301
bool retval = GamepadView::Touch(input);
302
303
if (input.flags & TOUCH_DOWN) {
304
if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
305
dragPointerId_ = input.id;
306
usedPointerMask |= 1 << input.id;
307
ProcessTouch(input.x, input.y, true);
308
}
309
}
310
if (input.flags & TOUCH_MOVE) {
311
if (!(input.flags & TOUCH_MOUSE) || input.buttons) {
312
if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id))) {
313
dragPointerId_ = input.id;
314
}
315
if (input.id == dragPointerId_) {
316
ProcessTouch(input.x, input.y, true);
317
}
318
}
319
}
320
if (input.flags & TOUCH_UP) {
321
if (input.id == dragPointerId_) {
322
dragPointerId_ = -1;
323
usedPointerMask &= ~(1 << input.id);
324
ProcessTouch(input.x, input.y, false);
325
}
326
}
327
return retval;
328
}
329
330
void PSPDpad::ProcessTouch(float x, float y, bool down) {
331
float stick_size = bounds_.w;
332
float inv_stick_size = 1.0f / stick_size;
333
const float deadzone = 0.05f;
334
335
float dx = (x - bounds_.centerX()) * inv_stick_size;
336
float dy = (y - bounds_.centerY()) * inv_stick_size;
337
float rad = sqrtf(dx * dx + dy * dy);
338
if (!g_Config.bStickyTouchDPad && (rad < deadzone || fabs(dx) > 0.5f || fabs(dy) > 0.5))
339
down = false;
340
341
int ctrlMask = 0;
342
bool fourWay = g_Config.bDisableDpadDiagonals || rad < 0.2f;
343
if (down) {
344
if (fourWay) {
345
int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 4) + 0.5f)) & 3;
346
switch (direction) {
347
case 0: ctrlMask = CTRL_RIGHT; break;
348
case 1: ctrlMask = CTRL_DOWN; break;
349
case 2: ctrlMask = CTRL_LEFT; break;
350
case 3: ctrlMask = CTRL_UP; break;
351
}
352
// 4 way pad
353
} else {
354
// 8 way pad
355
int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 8) + 0.5f)) & 7;
356
switch (direction) {
357
case 0: ctrlMask = CTRL_RIGHT; break;
358
case 1: ctrlMask = CTRL_RIGHT | CTRL_DOWN; break;
359
case 2: ctrlMask = CTRL_DOWN; break;
360
case 3: ctrlMask = CTRL_DOWN | CTRL_LEFT; break;
361
case 4: ctrlMask = CTRL_LEFT; break;
362
case 5: ctrlMask = CTRL_UP | CTRL_LEFT; break;
363
case 6: ctrlMask = CTRL_UP; break;
364
case 7: ctrlMask = CTRL_UP | CTRL_RIGHT; break;
365
}
366
}
367
}
368
369
int lastDown = down_;
370
int pressed = ctrlMask & ~lastDown;
371
int released = (~ctrlMask) & lastDown;
372
down_ = ctrlMask;
373
bool vibrate = false;
374
static const int dir[4] = { CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP };
375
for (int i = 0; i < 4; i++) {
376
if (pressed & dir[i]) {
377
vibrate = true;
378
__CtrlUpdateButtons(dir[i], 0);
379
}
380
if (released & dir[i]) {
381
__CtrlUpdateButtons(0, dir[i]);
382
}
383
}
384
if (vibrate && g_Config.bHapticFeedback) {
385
System_Vibrate(HAPTIC_VIRTUAL_KEY);
386
}
387
}
388
389
void PSPDpad::Draw(UIContext &dc) {
390
float opacity = g_gamepadOpacity;
391
if (opacity <= 0.0f)
392
return;
393
394
static const float xoff[4] = {1, 0, -1, 0};
395
static const float yoff[4] = {0, 1, 0, -1};
396
static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
397
int buttons = __CtrlPeekButtons();
398
float r = D_pad_Radius * spacing_;
399
for (int i = 0; i < 4; i++) {
400
bool isDown = (buttons & dir[i]) != 0;
401
402
float x = bounds_.centerX() + xoff[i] * r;
403
float y = bounds_.centerY() + yoff[i] * r;
404
float x2 = bounds_.centerX() + xoff[i] * (r + 10.f * scale_);
405
float y2 = bounds_.centerY() + yoff[i] * (r + 10.f * scale_);
406
float angle = i * (M_PI / 2.0f);
407
float imgScale = isDown ? scale_ * TOUCH_SCALE_FACTOR : scale_;
408
float imgOpacity = opacity;
409
410
if (isDown && g_Config.iTouchButtonStyle == 2) {
411
imgScale = scale_;
412
imgOpacity *= 1.35f;
413
414
uint32_t downBg = colorAlpha(0x00FFFFFF, imgOpacity * 0.5f);
415
if (arrowIndex_ != arrowDownIndex_)
416
dc.Draw()->DrawImageRotated(arrowDownIndex_, x, y, imgScale, angle + PI, downBg, false);
417
}
418
419
uint32_t colorBg = colorAlpha(GetButtonColor(), imgOpacity);
420
uint32_t color = colorAlpha(0xFFFFFF, imgOpacity);
421
422
dc.Draw()->DrawImageRotated(arrowIndex_, x, y, imgScale, angle + PI, colorBg, false);
423
if (overlayIndex_.isValid())
424
dc.Draw()->DrawImageRotated(overlayIndex_, x2, y2, imgScale, angle + PI, color);
425
}
426
}
427
428
PSPStick::PSPStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams)
429
: GamepadView(key, layoutParams), dragPointerId_(-1), bgImg_(bgImg), stickImageIndex_(stickImg), stickDownImg_(stickDownImg), stick_(stick), scale_(scale), centerX_(-1), centerY_(-1) {
430
stick_size_ = 50;
431
}
432
433
void PSPStick::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
434
dc.Draw()->GetAtlas()->measureImage(bgImg_, &w, &h);
435
w *= scale_;
436
h *= scale_;
437
}
438
439
void PSPStick::Draw(UIContext &dc) {
440
float opacity = g_gamepadOpacity;
441
if (opacity <= 0.0f)
442
return;
443
444
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
445
opacity *= 1.35f;
446
}
447
448
uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
449
uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
450
451
if (centerX_ < 0.0f) {
452
centerX_ = bounds_.centerX();
453
centerY_ = bounds_.centerY();
454
}
455
456
float stickX = centerX_;
457
float stickY = centerY_;
458
459
float dx, dy;
460
__CtrlPeekAnalog(stick_, &dx, &dy);
461
462
if (!g_Config.bHideStickBackground)
463
dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
464
float headScale = stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale;
465
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
466
dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, downBg, ALIGN_CENTER);
467
dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, colorBg, ALIGN_CENTER);
468
}
469
470
bool PSPStick::Touch(const TouchInput &input) {
471
bool retval = GamepadView::Touch(input);
472
if (input.flags & TOUCH_RELEASE_ALL) {
473
dragPointerId_ = -1;
474
centerX_ = bounds_.centerX();
475
centerY_ = bounds_.centerY();
476
__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
477
usedPointerMask = 0;
478
analogPointerMask = 0;
479
return retval;
480
}
481
if (input.flags & TOUCH_DOWN) {
482
float fac = 0.5f*(stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale)-0.5f;
483
if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
484
if (g_Config.bAutoCenterTouchAnalog) {
485
centerX_ = input.x;
486
centerY_ = input.y;
487
} else {
488
centerX_ = bounds_.centerX();
489
centerY_ = bounds_.centerY();
490
}
491
dragPointerId_ = input.id;
492
usedPointerMask |= 1 << input.id;
493
analogPointerMask |= 1 << input.id;
494
ProcessTouch(input.x, input.y, true);
495
retval = true;
496
}
497
}
498
if (input.flags & TOUCH_MOVE) {
499
if (input.id == dragPointerId_) {
500
ProcessTouch(input.x, input.y, true);
501
retval = true;
502
}
503
}
504
if (input.flags & TOUCH_UP) {
505
if (input.id == dragPointerId_) {
506
dragPointerId_ = -1;
507
centerX_ = bounds_.centerX();
508
centerY_ = bounds_.centerY();
509
usedPointerMask &= ~(1 << input.id);
510
analogPointerMask &= ~(1 << input.id);
511
ProcessTouch(input.x, input.y, false);
512
retval = true;
513
}
514
}
515
return retval;
516
}
517
518
void PSPStick::ProcessTouch(float x, float y, bool down) {
519
if (down && centerX_ >= 0.0f) {
520
float inv_stick_size = 1.0f / (stick_size_ * scale_);
521
522
float dx = (x - centerX_) * inv_stick_size;
523
float dy = (y - centerY_) * inv_stick_size;
524
// Do not clamp to a circle! The PSP has nearly square range!
525
526
// Old code to clamp to a circle
527
// float len = sqrtf(dx * dx + dy * dy);
528
// if (len > 1.0f) {
529
// dx /= len;
530
// dy /= len;
531
//}
532
533
// Still need to clamp to a square
534
dx = std::min(1.0f, std::max(-1.0f, dx));
535
dy = std::min(1.0f, std::max(-1.0f, dy));
536
537
__CtrlSetAnalogXY(stick_, dx, -dy);
538
} else {
539
__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
540
}
541
}
542
543
PSPCustomStick::PSPCustomStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams)
544
: PSPStick(bgImg, key, stickImg, stickDownImg, stick, scale, layoutParams) {
545
}
546
547
void PSPCustomStick::Draw(UIContext &dc) {
548
float opacity = g_gamepadOpacity;
549
if (opacity <= 0.0f)
550
return;
551
552
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
553
opacity *= 1.35f;
554
}
555
556
uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
557
uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
558
559
if (centerX_ < 0.0f) {
560
centerX_ = bounds_.centerX();
561
centerY_ = bounds_.centerY();
562
}
563
564
float stickX = centerX_;
565
float stickY = centerY_;
566
567
float dx, dy;
568
dx = posX_;
569
dy = -posY_;
570
571
if (!g_Config.bHideStickBackground)
572
dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
573
if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
574
dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, downBg, ALIGN_CENTER);
575
dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, colorBg, ALIGN_CENTER);
576
}
577
578
bool PSPCustomStick::Touch(const TouchInput &input) {
579
bool retval = GamepadView::Touch(input);
580
if (input.flags & TOUCH_RELEASE_ALL) {
581
dragPointerId_ = -1;
582
centerX_ = bounds_.centerX();
583
centerY_ = bounds_.centerY();
584
posX_ = 0.0f;
585
posY_ = 0.0f;
586
usedPointerMask = 0;
587
analogPointerMask = 0;
588
return false;
589
}
590
if (input.flags & TOUCH_DOWN) {
591
float fac = 0.5f*g_Config.fRightStickHeadScale-0.5f;
592
if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
593
if (g_Config.bAutoCenterTouchAnalog) {
594
centerX_ = input.x;
595
centerY_ = input.y;
596
} else {
597
centerX_ = bounds_.centerX();
598
centerY_ = bounds_.centerY();
599
}
600
dragPointerId_ = input.id;
601
usedPointerMask |= 1 << input.id;
602
analogPointerMask |= 1 << input.id;
603
ProcessTouch(input.x, input.y, true);
604
retval = true;
605
}
606
}
607
if (input.flags & TOUCH_MOVE) {
608
if (input.id == dragPointerId_) {
609
ProcessTouch(input.x, input.y, true);
610
retval = true;
611
}
612
}
613
if (input.flags & TOUCH_UP) {
614
if (input.id == dragPointerId_) {
615
dragPointerId_ = -1;
616
centerX_ = bounds_.centerX();
617
centerY_ = bounds_.centerY();
618
usedPointerMask &= ~(1 << input.id);
619
analogPointerMask &= ~(1 << input.id);
620
ProcessTouch(input.x, input.y, false);
621
retval = true;
622
}
623
}
624
return retval;
625
}
626
627
void PSPCustomStick::ProcessTouch(float x, float y, bool down) {
628
static const int buttons[] = {0, CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
629
static const int analogs[] = {VIRTKEY_AXIS_RIGHT_Y_MAX, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, VIRTKEY_AXIS_Y_MAX, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX};
630
u32 press = 0;
631
u32 release = 0;
632
633
auto toggle = [&](int config, bool simpleCheck, bool diagCheck = true) {
634
if (config <= 0 || (size_t)config >= ARRAY_SIZE(buttons))
635
return;
636
637
if (simpleCheck && (!g_Config.bRightAnalogDisableDiagonal || diagCheck))
638
press |= buttons[config];
639
else
640
release |= buttons[config];
641
};
642
643
auto analog = [&](float dx, float dy) {
644
if (g_Config.bRightAnalogDisableDiagonal) {
645
if (fabs(dx) > fabs(dy)) {
646
dy = 0.0f;
647
} else {
648
dx = 0.0f;
649
}
650
}
651
652
auto assign = [&](int config, float value) {
653
if (config < ARRAY_SIZE(buttons) || config >= ARRAY_SIZE(buttons) + ARRAY_SIZE(analogs)) {
654
return;
655
}
656
switch(analogs[config - ARRAY_SIZE(buttons)]) {
657
case VIRTKEY_AXIS_Y_MAX:
658
__CtrlSetAnalogY(0, value);
659
break;
660
case VIRTKEY_AXIS_Y_MIN:
661
__CtrlSetAnalogY(0, value * -1.0f);
662
break;
663
case VIRTKEY_AXIS_X_MIN:
664
__CtrlSetAnalogX(0, value * -1.0f);
665
break;
666
case VIRTKEY_AXIS_X_MAX:
667
__CtrlSetAnalogX(0, value);
668
break;
669
case VIRTKEY_AXIS_RIGHT_Y_MAX:
670
__CtrlSetAnalogY(1, value);
671
break;
672
case VIRTKEY_AXIS_RIGHT_Y_MIN:
673
__CtrlSetAnalogY(1, value * -1.0f);
674
break;
675
case VIRTKEY_AXIS_RIGHT_X_MIN:
676
__CtrlSetAnalogX(1, value * -1.0f);
677
break;
678
case VIRTKEY_AXIS_RIGHT_X_MAX:
679
__CtrlSetAnalogX(1, value);
680
break;
681
}
682
};
683
684
// if/when we ever get iLeftAnalog settings, check stick_ for the config to use
685
// let 0.0f through during centering
686
if (dy >= 0.0f) {
687
// down
688
assign(g_Config.iRightAnalogUp, 0.0f);
689
assign(g_Config.iRightAnalogDown, dy);
690
}
691
if (dy <= 0.0f) {
692
// up
693
assign(g_Config.iRightAnalogDown, 0.0f);
694
assign(g_Config.iRightAnalogUp, dy * -1.0f);
695
}
696
if (dx <= 0.0f) {
697
// left
698
assign(g_Config.iRightAnalogRight, 0.0f);
699
assign(g_Config.iRightAnalogLeft, dx * -1.0f);
700
}
701
if (dx >= 0.0f) {
702
// right
703
assign(g_Config.iRightAnalogLeft, 0.0f);
704
assign(g_Config.iRightAnalogRight, dx);
705
}
706
};
707
708
if (down && centerX_ >= 0.0f) {
709
float inv_stick_size = 1.0f / (stick_size_ * scale_);
710
711
float dx = (x - centerX_) * inv_stick_size;
712
float dy = (y - centerY_) * inv_stick_size;
713
714
dx = std::min(1.0f, std::max(-1.0f, dx));
715
dy = std::min(1.0f, std::max(-1.0f, dy));
716
717
toggle(g_Config.iRightAnalogRight, dx > 0.5f, fabs(dx) > fabs(dy));
718
toggle(g_Config.iRightAnalogLeft, dx < -0.5f, fabs(dx) > fabs(dy));
719
toggle(g_Config.iRightAnalogUp, dy < -0.5f, fabs(dx) <= fabs(dy));
720
toggle(g_Config.iRightAnalogDown, dy > 0.5f, fabs(dx) <= fabs(dy));
721
toggle(g_Config.iRightAnalogPress, true);
722
723
analog(dx, dy);
724
725
posX_ = dx;
726
posY_ = dy;
727
728
} else {
729
toggle(g_Config.iRightAnalogRight, false);
730
toggle(g_Config.iRightAnalogLeft, false);
731
toggle(g_Config.iRightAnalogUp, false);
732
toggle(g_Config.iRightAnalogDown, false);
733
toggle(g_Config.iRightAnalogPress, false);
734
735
analog(0.0f, 0.0f);
736
737
posX_ = 0.0f;
738
posY_ = 0.0f;
739
}
740
741
if (release || press) {
742
__CtrlUpdateButtons(press, release);
743
}
744
}
745
746
void InitPadLayout(float xres, float yres, float globalScale) {
747
const float scale = globalScale;
748
const int halfW = xres / 2;
749
750
auto initTouchPos = [=](ConfigTouchPos &touch, float x, float y) {
751
if (touch.x == -1.0f || touch.y == -1.0f) {
752
touch.x = x / xres;
753
touch.y = std::max(y, 20.0f * globalScale) / yres;
754
touch.scale = scale;
755
}
756
};
757
758
// PSP buttons (triangle, circle, square, cross)---------------------
759
// space between the PSP buttons (triangle, circle, square and cross)
760
if (g_Config.fActionButtonSpacing < 0) {
761
g_Config.fActionButtonSpacing = 1.0f;
762
}
763
764
// Position of the circle button (the PSP circle button). It is the farthest to the left
765
float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
766
int Action_button_center_X = xres - Action_button_spacing * 2;
767
int Action_button_center_Y = yres - Action_button_spacing * 2;
768
if (g_Config.touchRightAnalogStick.show) {
769
Action_button_center_Y -= 150 * scale;
770
}
771
initTouchPos(g_Config.touchActionButtonCenter, Action_button_center_X, Action_button_center_Y);
772
773
//D-PAD (up down left right) (aka PSP cross)----------------------------
774
//radius to the D-pad
775
// TODO: Make configurable
776
777
int D_pad_X = 2.5 * D_pad_Radius * scale;
778
int D_pad_Y = yres - D_pad_Radius * scale;
779
if (g_Config.touchAnalogStick.show) {
780
D_pad_Y -= 200 * scale;
781
}
782
initTouchPos(g_Config.touchDpad, D_pad_X, D_pad_Y);
783
784
//analog stick-------------------------------------------------------
785
//keep the analog stick right below the D pad
786
int analog_stick_X = D_pad_X;
787
int analog_stick_Y = yres - 80 * scale;
788
initTouchPos(g_Config.touchAnalogStick, analog_stick_X, analog_stick_Y);
789
790
//right analog stick-------------------------------------------------
791
//keep the right analog stick right below the face buttons
792
int right_analog_stick_X = Action_button_center_X;
793
int right_analog_stick_Y = yres - 80 * scale;
794
initTouchPos(g_Config.touchRightAnalogStick, right_analog_stick_X, right_analog_stick_Y);
795
796
//select, start, throttle--------------------------------------------
797
//space between the bottom keys (space between select, start and un-throttle)
798
float bottom_key_spacing = 100;
799
if (g_display.dp_xres < 750) {
800
bottom_key_spacing *= 0.8f;
801
}
802
803
// On IOS, nudge the bottom button up a little to avoid the task switcher.
804
#if PPSSPP_PLATFORM(IOS)
805
const float bottom_button_Y = 80.0f;
806
#else
807
const float bottom_button_Y = 60.0f;
808
#endif
809
810
int start_key_X = halfW + bottom_key_spacing * scale;
811
int start_key_Y = yres - bottom_button_Y * scale;
812
initTouchPos(g_Config.touchStartKey, start_key_X, start_key_Y);
813
814
int select_key_X = halfW;
815
int select_key_Y = yres - bottom_button_Y * scale;
816
initTouchPos(g_Config.touchSelectKey, select_key_X, select_key_Y);
817
818
int fast_forward_key_X = halfW - bottom_key_spacing * scale;
819
int fast_forward_key_Y = yres - bottom_button_Y * scale;
820
initTouchPos(g_Config.touchFastForwardKey, fast_forward_key_X, fast_forward_key_Y);
821
822
// L and R------------------------------------------------------------
823
// Put them above the analog stick / above the buttons to the right.
824
// The corners were very hard to reach..
825
826
int l_key_X = 60 * scale;
827
int l_key_Y = yres - 380 * scale;
828
initTouchPos(g_Config.touchLKey, l_key_X, l_key_Y);
829
830
int r_key_X = xres - 60 * scale;
831
int r_key_Y = l_key_Y;
832
initTouchPos(g_Config.touchRKey, r_key_X, r_key_Y);
833
834
struct { float x; float y; } customButtonPositions[10] = {
835
{ 1.2f, 0.5f },
836
{ 2.2f, 0.5f },
837
{ 3.2f, 0.5f },
838
{ 1.2f, 0.333f },
839
{ 2.2f, 0.333f },
840
{ -1.2f, 0.5f },
841
{ -2.2f, 0.5f },
842
{ -3.2f, 0.5f },
843
{ -1.2f, 0.333f },
844
{ -2.2f, 0.333f },
845
};
846
847
for (int i = 0; i < Config::CUSTOM_BUTTON_COUNT; i++) {
848
float y_offset = (float)(i / 10) * 0.08333f;
849
850
int combo_key_X = halfW + bottom_key_spacing * scale * customButtonPositions[i % 10].x;
851
int combo_key_Y = yres * (y_offset + customButtonPositions[i % 10].y);
852
853
initTouchPos(g_Config.touchCustom[i], combo_key_X, combo_key_Y);
854
}
855
}
856
857
UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, bool showPauseButton, ControlMapper* controllMapper) {
858
using namespace UI;
859
860
AnchorLayout *root = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
861
if (!g_Config.bShowTouchControls) {
862
return root;
863
}
864
865
struct ButtonOffset {
866
float x;
867
float y;
868
};
869
auto buttonLayoutParams = [=](const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) {
870
return new AnchorLayoutParams(touch.x * xres + off.x, touch.y * yres + off.y, NONE, NONE, true);
871
};
872
873
// Space between the PSP buttons (traingle, circle, square and cross)
874
const float actionButtonSpacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
875
// Position of the circle button (the PSP circle button). It is the farthest to the right.
876
ButtonOffset circleOffset{ actionButtonSpacing, 0.0f };
877
ButtonOffset crossOffset{ 0.0f, actionButtonSpacing };
878
ButtonOffset triangleOffset{ 0.0f, -actionButtonSpacing };
879
ButtonOffset squareOffset{ -actionButtonSpacing, 0.0f };
880
881
const int halfW = xres / 2;
882
883
const ImageID roundImage = g_Config.iTouchButtonStyle ? ImageID("I_ROUND_LINE") : ImageID("I_ROUND");
884
const ImageID rectImage = g_Config.iTouchButtonStyle ? ImageID("I_RECT_LINE") : ImageID("I_RECT");
885
const ImageID shoulderImage = g_Config.iTouchButtonStyle ? ImageID("I_SHOULDER_LINE") : ImageID("I_SHOULDER");
886
const ImageID stickImage = g_Config.iTouchButtonStyle ? ImageID("I_STICK_LINE") : ImageID("I_STICK");
887
const ImageID stickBg = g_Config.iTouchButtonStyle ? ImageID("I_STICK_BG_LINE") : ImageID("I_STICK_BG");
888
889
auto addPSPButton = [=](int buttonBit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * {
890
if (touch.show) {
891
return root->Add(new PSPButton(buttonBit, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch, off)));
892
}
893
return nullptr;
894
};
895
auto addBoolButton = [=](bool *value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> BoolButton * {
896
if (touch.show) {
897
return root->Add(new BoolButton(value, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch)));
898
}
899
return nullptr;
900
};
901
auto addCustomButton = [=](const ConfigCustomButton& cfg, const char *key, const ConfigTouchPos &touch) -> CustomButton * {
902
using namespace CustomKeyData;
903
if (touch.show) {
904
_dbg_assert_(cfg.shape < ARRAY_SIZE(customKeyShapes));
905
_dbg_assert_(cfg.image < ARRAY_SIZE(customKeyImages));
906
907
// Note: cfg.shape and cfg.image are bounds-checked elsewhere.
908
auto aux = root->Add(new CustomButton(cfg.key, key, cfg.toggle, cfg.repeat, controllMapper,
909
g_Config.iTouchButtonStyle == 0 ? customKeyShapes[cfg.shape].i : customKeyShapes[cfg.shape].l, customKeyShapes[cfg.shape].i,
910
customKeyImages[cfg.image].i, touch.scale, customKeyShapes[cfg.shape].d, buttonLayoutParams(touch)));
911
aux->SetAngle(customKeyImages[cfg.image].r, customKeyShapes[cfg.shape].r);
912
aux->FlipImageH(customKeyShapes[cfg.shape].f);
913
return aux;
914
}
915
return nullptr;
916
};
917
918
if (showPauseButton) {
919
root->Add(new BoolButton(pause, "Pause button", roundImage, ImageID("I_ROUND"), ImageID("I_ARROW"), 1.0f, new AnchorLayoutParams(halfW, 20, NONE, NONE, true)))->SetAngle(90);
920
}
921
922
// touchActionButtonCenter.show will always be true, since that's the default.
923
if (g_Config.bShowTouchCircle)
924
addPSPButton(CTRL_CIRCLE, "Circle button", roundImage, ImageID("I_ROUND"), ImageID("I_CIRCLE"), g_Config.touchActionButtonCenter, circleOffset);
925
if (g_Config.bShowTouchCross)
926
addPSPButton(CTRL_CROSS, "Cross button", roundImage, ImageID("I_ROUND"), ImageID("I_CROSS"), g_Config.touchActionButtonCenter, crossOffset);
927
if (g_Config.bShowTouchTriangle)
928
addPSPButton(CTRL_TRIANGLE, "Triangle button", roundImage, ImageID("I_ROUND"), ImageID("I_TRIANGLE"), g_Config.touchActionButtonCenter, triangleOffset);
929
if (g_Config.bShowTouchSquare)
930
addPSPButton(CTRL_SQUARE, "Square button", roundImage, ImageID("I_ROUND"), ImageID("I_SQUARE"), g_Config.touchActionButtonCenter, squareOffset);
931
932
addPSPButton(CTRL_START, "Start button", rectImage, ImageID("I_RECT"), ImageID("I_START"), g_Config.touchStartKey);
933
addPSPButton(CTRL_SELECT, "Select button", rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), g_Config.touchSelectKey);
934
935
BoolButton *fastForward = addBoolButton(&PSP_CoreParameter().fastForward, "Fast-forward button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchFastForwardKey);
936
if (fastForward) {
937
fastForward->SetAngle(180.0f);
938
fastForward->OnChange.Add([](UI::EventParams &e) {
939
if (e.a && coreState == CORE_STEPPING_CPU) {
940
Core_Resume();
941
}
942
return UI::EVENT_DONE;
943
});
944
}
945
946
addPSPButton(CTRL_LTRIGGER, "Left shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_L"), g_Config.touchLKey);
947
PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, "Right shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_R"), g_Config.touchRKey);
948
if (rTrigger)
949
rTrigger->FlipImageH(true);
950
951
if (g_Config.touchDpad.show) {
952
const ImageID dirImage = g_Config.iTouchButtonStyle ? ImageID("I_DIR_LINE") : ImageID("I_DIR");
953
root->Add(new PSPDpad(dirImage, "D-pad", ImageID("I_DIR"), ImageID("I_ARROW"), g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad)));
954
}
955
956
if (g_Config.touchAnalogStick.show)
957
root->Add(new PSPStick(stickBg, "Left analog stick", stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick)));
958
959
if (g_Config.touchRightAnalogStick.show) {
960
if (g_Config.bRightAnalogCustom)
961
root->Add(new PSPCustomStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
962
else
963
root->Add(new PSPStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
964
}
965
966
// Sanitize custom button images, while adding them.
967
for (int i = 0; i < Config::CUSTOM_BUTTON_COUNT; i++) {
968
if (g_Config.CustomButton[i].shape >= ARRAY_SIZE(CustomKeyData::customKeyShapes)) {
969
g_Config.CustomButton[i].shape = 0;
970
}
971
if (g_Config.CustomButton[i].image >= ARRAY_SIZE(CustomKeyData::customKeyImages)) {
972
g_Config.CustomButton[i].image = 0;
973
}
974
975
char temp[64];
976
snprintf(temp, sizeof(temp), "Custom %d button", i + 1);
977
addCustomButton(g_Config.CustomButton[i], temp, g_Config.touchCustom[i]);
978
}
979
980
if (g_Config.bGestureControlEnabled)
981
root->Add(new GestureGamepad(controllMapper));
982
983
return root;
984
}
985
986
bool GestureGamepad::Touch(const TouchInput &input) {
987
if (usedPointerMask & (1 << input.id)) {
988
if (input.id == dragPointerId_)
989
dragPointerId_ = -1;
990
return false;
991
}
992
993
if (input.flags & TOUCH_RELEASE_ALL) {
994
dragPointerId_ = -1;
995
return false;
996
}
997
998
if (input.flags & TOUCH_DOWN) {
999
if (dragPointerId_ == -1) {
1000
dragPointerId_ = input.id;
1001
lastX_ = input.x;
1002
lastY_ = input.y;
1003
downX_ = input.x;
1004
downY_ = input.y;
1005
const float now = time_now_d();
1006
if (now - lastTapRelease_ < 0.3f && !haveDoubleTapped_) {
1007
if (g_Config.iDoubleTapGesture != 0 )
1008
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_DOWN);
1009
haveDoubleTapped_ = true;
1010
}
1011
1012
lastTouchDown_ = now;
1013
}
1014
}
1015
if (input.flags & TOUCH_MOVE) {
1016
if (input.id == dragPointerId_) {
1017
deltaX_ += input.x - lastX_;
1018
deltaY_ += input.y - lastY_;
1019
lastX_ = input.x;
1020
lastY_ = input.y;
1021
1022
if (g_Config.bAnalogGesture) {
1023
const float k = g_Config.fAnalogGestureSensibility * 0.02;
1024
float dx = (input.x - downX_)*g_display.dpi_scale_x * k;
1025
float dy = (input.y - downY_)*g_display.dpi_scale_y * k;
1026
dx = std::min(1.0f, std::max(-1.0f, dx));
1027
dy = std::min(1.0f, std::max(-1.0f, dy));
1028
__CtrlSetAnalogXY(0, dx, -dy);
1029
}
1030
}
1031
}
1032
if (input.flags & TOUCH_UP) {
1033
if (input.id == dragPointerId_) {
1034
dragPointerId_ = -1;
1035
if (time_now_d() - lastTouchDown_ < 0.3f)
1036
lastTapRelease_ = time_now_d();
1037
1038
if (haveDoubleTapped_) {
1039
if (g_Config.iDoubleTapGesture != 0)
1040
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_UP);
1041
haveDoubleTapped_ = false;
1042
}
1043
1044
if (g_Config.bAnalogGesture)
1045
__CtrlSetAnalogXY(0, 0, 0);
1046
}
1047
}
1048
return true;
1049
}
1050
1051
void GestureGamepad::Draw(UIContext &dc) {
1052
float opacity = g_Config.iTouchButtonOpacity / 100.0;
1053
if (opacity <= 0.0f)
1054
return;
1055
1056
uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
1057
1058
if (g_Config.bAnalogGesture && dragPointerId_ != -1) {
1059
dc.Draw()->DrawImage(ImageID("I_CIRCLE"), downX_, downY_, 0.7f, colorBg, ALIGN_CENTER);
1060
}
1061
}
1062
1063
void GestureGamepad::Update() {
1064
const float th = 1.0f;
1065
float dx = deltaX_ * g_display.dpi_scale_x * g_Config.fSwipeSensitivity;
1066
float dy = deltaY_ * g_display.dpi_scale_y * g_Config.fSwipeSensitivity;
1067
if (g_Config.iSwipeRight != 0) {
1068
if (dx > th) {
1069
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeRight-1], KEY_DOWN);
1070
swipeRightReleased_ = false;
1071
} else if (!swipeRightReleased_) {
1072
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeRight-1], KEY_UP);
1073
swipeRightReleased_ = true;
1074
}
1075
}
1076
if (g_Config.iSwipeLeft != 0) {
1077
if (dx < -th) {
1078
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_DOWN);
1079
swipeLeftReleased_ = false;
1080
} else if (!swipeLeftReleased_) {
1081
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_UP);
1082
swipeLeftReleased_ = true;
1083
}
1084
}
1085
if (g_Config.iSwipeUp != 0) {
1086
if (dy < -th) {
1087
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeUp-1], KEY_DOWN);
1088
swipeUpReleased_ = false;
1089
} else if (!swipeUpReleased_) {
1090
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeUp-1], KEY_UP);
1091
swipeUpReleased_ = true;
1092
}
1093
}
1094
if (g_Config.iSwipeDown != 0) {
1095
if (dy > th) {
1096
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeDown-1], KEY_DOWN);
1097
swipeDownReleased_ = false;
1098
} else if (!swipeDownReleased_) {
1099
controlMapper_->PSPKey(DEVICE_ID_TOUCH, GestureKey::keyList[g_Config.iSwipeDown-1], KEY_UP);
1100
swipeDownReleased_ = true;
1101
}
1102
}
1103
deltaX_ *= g_Config.fSwipeSmoothing;
1104
deltaY_ *= g_Config.fSwipeSmoothing;
1105
}
1106
1107