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 static uint32_t usedPointerMask = 0;
38 static uint32_t analogPointerMask = 0;
39
GetButtonColor()40 static u32 GetButtonColor() {
41 return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
42 }
43
GamepadView(const char * key,UI::LayoutParams * layoutParams)44 GamepadView::GamepadView(const char *key, UI::LayoutParams *layoutParams) : UI::View(layoutParams), key_(key) {
45 lastFrameTime_ = time_now_d();
46 }
47
Touch(const TouchInput & input)48 void GamepadView::Touch(const TouchInput &input) {
49 secondsWithoutTouch_ = 0.0f;
50 }
51
Update()52 void GamepadView::Update() {
53 const double now = time_now_d();
54 float delta = now - lastFrameTime_;
55 if (delta > 0) {
56 secondsWithoutTouch_ += delta;
57 }
58 lastFrameTime_ = now;
59 }
60
DescribeText() const61 std::string GamepadView::DescribeText() const {
62 auto co = GetI18NCategory("Controls");
63 return co->T(key_);
64 }
65
GetButtonOpacity()66 float GamepadView::GetButtonOpacity() {
67 if (coreState != CORE_RUNNING) {
68 return 0.0f;
69 }
70
71 float fadeAfterSeconds = g_Config.iTouchButtonHideSeconds;
72 float fadeTransitionSeconds = std::min(fadeAfterSeconds, 0.5f);
73 float opacity = g_Config.iTouchButtonOpacity / 100.0f;
74
75 float multiplier = 1.0f;
76 if (secondsWithoutTouch_ >= fadeAfterSeconds && fadeAfterSeconds > 0.0f) {
77 if (secondsWithoutTouch_ >= fadeAfterSeconds + fadeTransitionSeconds) {
78 multiplier = 0.0f;
79 } else {
80 float secondsIntoFade = secondsWithoutTouch_ - fadeAfterSeconds;
81 multiplier = 1.0f - (secondsIntoFade / fadeTransitionSeconds);
82 }
83 }
84
85 return opacity * multiplier;
86 }
87
GetContentDimensions(const UIContext & dc,float & w,float & h) const88 void MultiTouchButton::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
89 const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(bgImg_);
90 if (image) {
91 w = image->w * scale_;
92 h = image->h * scale_;
93 } else {
94 w = 0.0f;
95 h = 0.0f;
96 }
97 }
98
Touch(const TouchInput & input)99 void MultiTouchButton::Touch(const TouchInput &input) {
100 GamepadView::Touch(input);
101 if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) {
102 pointerDownMask_ |= 1 << input.id;
103 usedPointerMask |= 1 << input.id;
104 }
105 if (input.flags & TOUCH_MOVE) {
106 if (bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id)))
107 pointerDownMask_ |= 1 << input.id;
108 else
109 pointerDownMask_ &= ~(1 << input.id);
110 }
111 if (input.flags & TOUCH_UP) {
112 pointerDownMask_ &= ~(1 << input.id);
113 usedPointerMask &= ~(1 << input.id);
114 }
115 if (input.flags & TOUCH_RELEASE_ALL) {
116 pointerDownMask_ = 0;
117 usedPointerMask = 0;
118 }
119 }
120
Draw(UIContext & dc)121 void MultiTouchButton::Draw(UIContext &dc) {
122 float opacity = GetButtonOpacity();
123 if (opacity <= 0.0f)
124 return;
125
126 float scale = scale_;
127 if (IsDown()) {
128 if (g_Config.iTouchButtonStyle == 2) {
129 opacity *= 1.35f;
130 } else {
131 scale *= 2.0f;
132 opacity *= 1.15f;
133 }
134 }
135
136 uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
137 uint32_t downBg = colorAlpha(0xFFFFFF, opacity * 0.5f);
138 uint32_t color = colorAlpha(0xFFFFFF, opacity);
139
140 if (IsDown() && g_Config.iTouchButtonStyle == 2) {
141 if (bgImg_ != bgDownImg_)
142 dc.Draw()->DrawImageRotated(bgDownImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), downBg, flipImageH_);
143 }
144
145 dc.Draw()->DrawImageRotated(bgImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), colorBg, flipImageH_);
146
147 int y = bounds_.centerY();
148 // Hack round the fact that the center of the rectangular picture the triangle is contained in
149 // is not at the "weight center" of the triangle.
150 if (img_ == ImageID("I_TRIANGLE"))
151 y -= 2.8f * scale;
152 dc.Draw()->DrawImageRotated(img_, bounds_.centerX(), y, scale, angle_ * (M_PI * 2 / 360.0f), color);
153 }
154
Touch(const TouchInput & input)155 void BoolButton::Touch(const TouchInput &input) {
156 bool lastDown = pointerDownMask_ != 0;
157 MultiTouchButton::Touch(input);
158 bool down = pointerDownMask_ != 0;
159
160 if (down != lastDown) {
161 *value_ = down;
162 UI::EventParams params{ this };
163 params.a = down;
164 OnChange.Trigger(params);
165 }
166 }
167
Touch(const TouchInput & input)168 void PSPButton::Touch(const TouchInput &input) {
169 bool lastDown = pointerDownMask_ != 0;
170 MultiTouchButton::Touch(input);
171 bool down = pointerDownMask_ != 0;
172 if (down && !lastDown) {
173 if (g_Config.bHapticFeedback) {
174 Vibrate(HAPTIC_VIRTUAL_KEY);
175 }
176 __CtrlButtonDown(pspButtonBit_);
177 } else if (lastDown && !down) {
178 __CtrlButtonUp(pspButtonBit_);
179 }
180 }
181
IsDown()182 bool ComboKey::IsDown() {
183 return (toggle_ && on_) || (!toggle_ && pointerDownMask_ != 0);
184 }
185
GetContentDimensions(const UIContext & dc,float & w,float & h) const186 void ComboKey::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
187 MultiTouchButton::GetContentDimensions(dc, w, h);
188 if (invertedContextDimension_) {
189 float tmp = w;
190 w = h;
191 h = tmp;
192 }
193 }
194
Touch(const TouchInput & input)195 void ComboKey::Touch(const TouchInput &input) {
196 using namespace CustomKey;
197 bool lastDown = pointerDownMask_ != 0;
198 MultiTouchButton::Touch(input);
199 bool down = pointerDownMask_ != 0;
200
201 if (down && !lastDown) {
202 if (g_Config.bHapticFeedback)
203 Vibrate(HAPTIC_VIRTUAL_KEY);
204 for (int i = 0; i < ARRAY_SIZE(comboKeyList); i++) {
205 if (pspButtonBit_ & (1ULL << i)) {
206 controllMapper_->pspKey(comboKeyList[i].c, (on_ && toggle_) ? KEY_UP : KEY_DOWN);
207 }
208 }
209 if (toggle_)
210 on_ = !on_;
211 } else if (!toggle_ && lastDown && !down) {
212 for (int i = 0; i < ARRAY_SIZE(comboKeyList); i++) {
213 if (pspButtonBit_ & (1ULL << i)) {
214 controllMapper_->pspKey(comboKeyList[i].c, KEY_UP);
215 }
216 }
217 }
218 }
219
IsDown()220 bool PSPButton::IsDown() {
221 return (__CtrlPeekButtons() & pspButtonBit_) != 0;
222 }
223
PSPDpad(ImageID arrowIndex,const char * key,ImageID arrowDownIndex,ImageID overlayIndex,float scale,float spacing,UI::LayoutParams * layoutParams)224 PSPDpad::PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams)
225 : GamepadView(key, layoutParams), arrowIndex_(arrowIndex), arrowDownIndex_(arrowDownIndex), overlayIndex_(overlayIndex),
226 scale_(scale), spacing_(spacing), dragPointerId_(-1), down_(0) {
227 }
228
GetContentDimensions(const UIContext & dc,float & w,float & h) const229 void PSPDpad::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
230 w = D_pad_Radius * spacing_ * 4;
231 h = D_pad_Radius * spacing_ * 4;
232 }
233
Touch(const TouchInput & input)234 void PSPDpad::Touch(const TouchInput &input) {
235 GamepadView::Touch(input);
236
237 if (input.flags & TOUCH_DOWN) {
238 if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
239 dragPointerId_ = input.id;
240 usedPointerMask |= 1 << input.id;
241 ProcessTouch(input.x, input.y, true);
242 }
243 }
244 if (input.flags & TOUCH_MOVE) {
245 if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id))) {
246 dragPointerId_ = input.id;
247 }
248 if (input.id == dragPointerId_) {
249 ProcessTouch(input.x, input.y, true);
250 }
251 }
252 if (input.flags & TOUCH_UP) {
253 if (input.id == dragPointerId_) {
254 dragPointerId_ = -1;
255 usedPointerMask &= ~(1 << input.id);
256 ProcessTouch(input.x, input.y, false);
257 }
258 }
259 }
260
ProcessTouch(float x,float y,bool down)261 void PSPDpad::ProcessTouch(float x, float y, bool down) {
262 float stick_size = spacing_ * D_pad_Radius * scale_;
263 float inv_stick_size = 1.0f / stick_size;
264 const float deadzone = 0.17f;
265
266 float dx = (x - bounds_.centerX()) * inv_stick_size;
267 float dy = (y - bounds_.centerY()) * inv_stick_size;
268 float rad = sqrtf(dx*dx + dy*dy);
269 if (rad < deadzone || rad > 2.0f)
270 down = false;
271
272 int ctrlMask = 0;
273 int lastDown = down_;
274
275 bool fourWay = g_Config.bDisableDpadDiagonals || rad < 0.7f;
276 if (down) {
277 if (fourWay) {
278 int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 4) + 0.5f)) & 3;
279 switch (direction) {
280 case 0: ctrlMask |= CTRL_RIGHT; break;
281 case 1: ctrlMask |= CTRL_DOWN; break;
282 case 2: ctrlMask |= CTRL_LEFT; break;
283 case 3: ctrlMask |= CTRL_UP; break;
284 }
285 // 4 way pad
286 } else {
287 // 8 way pad
288 int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 8) + 0.5f)) & 7;
289 switch (direction) {
290 case 0: ctrlMask |= CTRL_RIGHT; break;
291 case 1: ctrlMask |= CTRL_RIGHT | CTRL_DOWN; break;
292 case 2: ctrlMask |= CTRL_DOWN; break;
293 case 3: ctrlMask |= CTRL_DOWN | CTRL_LEFT; break;
294 case 4: ctrlMask |= CTRL_LEFT; break;
295 case 5: ctrlMask |= CTRL_UP | CTRL_LEFT; break;
296 case 6: ctrlMask |= CTRL_UP; break;
297 case 7: ctrlMask |= CTRL_UP | CTRL_RIGHT; break;
298 }
299 }
300 }
301
302 down_ = ctrlMask;
303 int pressed = down_ & ~lastDown;
304 int released = (~down_) & lastDown;
305 static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
306 for (int i = 0; i < 4; i++) {
307 if (pressed & dir[i]) {
308 if (g_Config.bHapticFeedback) {
309 Vibrate(HAPTIC_VIRTUAL_KEY);
310 }
311 __CtrlButtonDown(dir[i]);
312 }
313 if (released & dir[i]) {
314 __CtrlButtonUp(dir[i]);
315 }
316 }
317 }
318
Draw(UIContext & dc)319 void PSPDpad::Draw(UIContext &dc) {
320 float opacity = GetButtonOpacity();
321 if (opacity <= 0.0f)
322 return;
323
324 static const float xoff[4] = {1, 0, -1, 0};
325 static const float yoff[4] = {0, 1, 0, -1};
326 static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
327 int buttons = __CtrlPeekButtons();
328 float r = D_pad_Radius * spacing_;
329 for (int i = 0; i < 4; i++) {
330 bool isDown = (buttons & dir[i]) != 0;
331
332 float x = bounds_.centerX() + xoff[i] * r;
333 float y = bounds_.centerY() + yoff[i] * r;
334 float x2 = bounds_.centerX() + xoff[i] * (r + 10.f * scale_);
335 float y2 = bounds_.centerY() + yoff[i] * (r + 10.f * scale_);
336 float angle = i * M_PI / 2;
337 float imgScale = isDown ? scale_ * 2 : scale_;
338 float imgOpacity = opacity;
339
340 if (isDown && g_Config.iTouchButtonStyle == 2) {
341 imgScale = scale_;
342 imgOpacity *= 1.35f;
343
344 uint32_t downBg = colorAlpha(0x00FFFFFF, imgOpacity * 0.5f);
345 if (arrowIndex_ != arrowDownIndex_)
346 dc.Draw()->DrawImageRotated(arrowDownIndex_, x, y, imgScale, angle + PI, downBg, false);
347 }
348
349 uint32_t colorBg = colorAlpha(GetButtonColor(), imgOpacity);
350 uint32_t color = colorAlpha(0xFFFFFF, imgOpacity);
351
352 dc.Draw()->DrawImageRotated(arrowIndex_, x, y, imgScale, angle + PI, colorBg, false);
353 if (overlayIndex_.isValid())
354 dc.Draw()->DrawImageRotated(overlayIndex_, x2, y2, imgScale, angle + PI, color);
355 }
356 }
357
PSPStick(ImageID bgImg,const char * key,ImageID stickImg,ImageID stickDownImg,int stick,float scale,UI::LayoutParams * layoutParams)358 PSPStick::PSPStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams)
359 : GamepadView(key, layoutParams), dragPointerId_(-1), bgImg_(bgImg), stickImageIndex_(stickImg), stickDownImg_(stickDownImg), stick_(stick), scale_(scale), centerX_(-1), centerY_(-1) {
360 stick_size_ = 50;
361 }
362
GetContentDimensions(const UIContext & dc,float & w,float & h) const363 void PSPStick::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
364 dc.Draw()->GetAtlas()->measureImage(bgImg_, &w, &h);
365 w *= scale_;
366 h *= scale_;
367 }
368
Draw(UIContext & dc)369 void PSPStick::Draw(UIContext &dc) {
370 float opacity = GetButtonOpacity();
371 if (opacity <= 0.0f)
372 return;
373
374 if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
375 opacity *= 1.35f;
376 }
377
378 uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
379 uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
380
381 if (centerX_ < 0.0f) {
382 centerX_ = bounds_.centerX();
383 centerY_ = bounds_.centerY();
384 }
385
386 float stickX = centerX_;
387 float stickY = centerY_;
388
389 float dx, dy;
390 __CtrlPeekAnalog(stick_, &dx, &dy);
391
392 if (!g_Config.bHideStickBackground)
393 dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
394 float headScale = stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale;
395 if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
396 dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, downBg, ALIGN_CENTER);
397 dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, colorBg, ALIGN_CENTER);
398 }
399
Touch(const TouchInput & input)400 void PSPStick::Touch(const TouchInput &input) {
401 GamepadView::Touch(input);
402 if (input.flags & TOUCH_RELEASE_ALL) {
403 dragPointerId_ = -1;
404 centerX_ = bounds_.centerX();
405 centerY_ = bounds_.centerY();
406 __CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
407 usedPointerMask = 0;
408 analogPointerMask = 0;
409 return;
410 }
411 if (input.flags & TOUCH_DOWN) {
412 float fac = 0.5f*(stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale)-0.5f;
413 if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
414 if (g_Config.bAutoCenterTouchAnalog) {
415 centerX_ = input.x;
416 centerY_ = input.y;
417 } else {
418 centerX_ = bounds_.centerX();
419 centerY_ = bounds_.centerY();
420 }
421 dragPointerId_ = input.id;
422 usedPointerMask |= 1 << input.id;
423 analogPointerMask |= 1 << input.id;
424 ProcessTouch(input.x, input.y, true);
425 }
426 }
427 if (input.flags & TOUCH_MOVE) {
428 if (input.id == dragPointerId_) {
429 ProcessTouch(input.x, input.y, true);
430 }
431 }
432 if (input.flags & TOUCH_UP) {
433 if (input.id == dragPointerId_) {
434 dragPointerId_ = -1;
435 centerX_ = bounds_.centerX();
436 centerY_ = bounds_.centerY();
437 usedPointerMask &= ~(1 << input.id);
438 analogPointerMask &= ~(1 << input.id);
439 ProcessTouch(input.x, input.y, false);
440 }
441 }
442 }
443
ProcessTouch(float x,float y,bool down)444 void PSPStick::ProcessTouch(float x, float y, bool down) {
445 if (down && centerX_ >= 0.0f) {
446 float inv_stick_size = 1.0f / (stick_size_ * scale_);
447
448 float dx = (x - centerX_) * inv_stick_size;
449 float dy = (y - centerY_) * inv_stick_size;
450 // Do not clamp to a circle! The PSP has nearly square range!
451
452 // Old code to clamp to a circle
453 // float len = sqrtf(dx * dx + dy * dy);
454 // if (len > 1.0f) {
455 // dx /= len;
456 // dy /= len;
457 //}
458
459 // Still need to clamp to a square
460 dx = std::min(1.0f, std::max(-1.0f, dx));
461 dy = std::min(1.0f, std::max(-1.0f, dy));
462
463 __CtrlSetAnalogXY(stick_, dx, -dy);
464 } else {
465 __CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
466 }
467 }
468
PSPCustomStick(ImageID bgImg,const char * key,ImageID stickImg,ImageID stickDownImg,float scale,UI::LayoutParams * layoutParams)469 PSPCustomStick::PSPCustomStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams)
470 : PSPStick(bgImg, key, stickImg, stickDownImg, -1, scale, layoutParams) {
471 }
472
Draw(UIContext & dc)473 void PSPCustomStick::Draw(UIContext &dc) {
474 float opacity = GetButtonOpacity();
475 if (opacity <= 0.0f)
476 return;
477
478 if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
479 opacity *= 1.35f;
480 }
481
482 uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
483 uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
484
485 if (centerX_ < 0.0f) {
486 centerX_ = bounds_.centerX();
487 centerY_ = bounds_.centerY();
488 }
489
490 float stickX = centerX_;
491 float stickY = centerY_;
492
493 float dx, dy;
494 dx = posX_;
495 dy = -posY_;
496
497 if (!g_Config.bHideStickBackground)
498 dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
499 if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
500 dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, downBg, ALIGN_CENTER);
501 dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, colorBg, ALIGN_CENTER);
502 }
503
Touch(const TouchInput & input)504 void PSPCustomStick::Touch(const TouchInput &input) {
505 GamepadView::Touch(input);
506 if (input.flags & TOUCH_RELEASE_ALL) {
507 dragPointerId_ = -1;
508 centerX_ = bounds_.centerX();
509 centerY_ = bounds_.centerY();
510 posX_ = 0.0f;
511 posY_ = 0.0f;
512 usedPointerMask = 0;
513 analogPointerMask = 0;
514 return;
515 }
516 if (input.flags & TOUCH_DOWN) {
517 float fac = 0.5f*g_Config.fRightStickHeadScale-0.5f;
518 if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
519 if (g_Config.bAutoCenterTouchAnalog) {
520 centerX_ = input.x;
521 centerY_ = input.y;
522 } else {
523 centerX_ = bounds_.centerX();
524 centerY_ = bounds_.centerY();
525 }
526 dragPointerId_ = input.id;
527 usedPointerMask |= 1 << input.id;
528 analogPointerMask |= 1 << input.id;
529 ProcessTouch(input.x, input.y, true);
530 }
531 }
532 if (input.flags & TOUCH_MOVE) {
533 if (input.id == dragPointerId_) {
534 ProcessTouch(input.x, input.y, true);
535 }
536 }
537 if (input.flags & TOUCH_UP) {
538 if (input.id == dragPointerId_) {
539 dragPointerId_ = -1;
540 centerX_ = bounds_.centerX();
541 centerY_ = bounds_.centerY();
542 usedPointerMask &= ~(1 << input.id);
543 analogPointerMask &= ~(1 << input.id);
544 ProcessTouch(input.x, input.y, false);
545 }
546 }
547 }
548
ProcessTouch(float x,float y,bool down)549 void PSPCustomStick::ProcessTouch(float x, float y, bool down) {
550 static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
551
552 if (down && centerX_ >= 0.0f) {
553 float inv_stick_size = 1.0f / (stick_size_ * scale_);
554
555 float dx = (x - centerX_) * inv_stick_size;
556 float dy = (y - centerY_) * inv_stick_size;
557
558 dx = std::min(1.0f, std::max(-1.0f, dx));
559 dy = std::min(1.0f, std::max(-1.0f, dy));
560
561 if (g_Config.iRightAnalogRight != 0) {
562 if (dx > 0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) > fabs(dy)))
563 __CtrlButtonDown(button[g_Config.iRightAnalogRight-1]);
564 else
565 __CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
566 }
567 if (g_Config.iRightAnalogLeft != 0) {
568 if (dx < -0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) > fabs(dy)))
569 __CtrlButtonDown(button[g_Config.iRightAnalogLeft-1]);
570 else
571 __CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
572 }
573 if (g_Config.iRightAnalogUp != 0) {
574 if (dy < -0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) <= fabs(dy)))
575 __CtrlButtonDown(button[g_Config.iRightAnalogUp-1]);
576 else
577 __CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
578 }
579 if (g_Config.iRightAnalogDown != 0) {
580 if (dy > 0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) <= fabs(dy)))
581 __CtrlButtonDown(button[g_Config.iRightAnalogDown-1]);
582 else
583 __CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
584 }
585 if (g_Config.iRightAnalogPress != 0)
586 __CtrlButtonDown(button[g_Config.iRightAnalogPress-1]);
587
588 posX_ = dx;
589 posY_ = dy;
590
591 } else {
592 if (g_Config.iRightAnalogUp != 0)
593 __CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
594 if (g_Config.iRightAnalogDown != 0)
595 __CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
596 if (g_Config.iRightAnalogLeft != 0)
597 __CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
598 if (g_Config.iRightAnalogRight != 0)
599 __CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
600 if (g_Config.iRightAnalogPress != 0)
601 __CtrlButtonUp(button[g_Config.iRightAnalogPress-1]);
602
603 posX_ = 0.0f;
604 posY_ = 0.0f;
605 }
606 }
607
InitPadLayout(float xres,float yres,float globalScale)608 void InitPadLayout(float xres, float yres, float globalScale) {
609 const float scale = globalScale;
610 const int halfW = xres / 2;
611
612 auto initTouchPos = [=](ConfigTouchPos &touch, float x, float y) {
613 if (touch.x == -1.0f || touch.y == -1.0f) {
614 touch.x = x / xres;
615 touch.y = y / yres;
616 touch.scale = scale;
617 }
618 };
619
620 // PSP buttons (triangle, circle, square, cross)---------------------
621 // space between the PSP buttons (triangle, circle, square and cross)
622 if (g_Config.fActionButtonSpacing < 0) {
623 g_Config.fActionButtonSpacing = 1.0f;
624 }
625
626 // Position of the circle button (the PSP circle button). It is the farthest to the left
627 float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
628 int Action_button_center_X = xres - Action_button_spacing * 2;
629 int Action_button_center_Y = yres - Action_button_spacing * 2;
630 if (g_Config.touchRightAnalogStick.show) {
631 Action_button_center_Y -= 150 * scale;
632 }
633 initTouchPos(g_Config.touchActionButtonCenter, Action_button_center_X, Action_button_center_Y);
634
635 //D-PAD (up down left right) (aka PSP cross)----------------------------
636 //radius to the D-pad
637 // TODO: Make configurable
638
639 int D_pad_X = 2.5 * D_pad_Radius * scale;
640 int D_pad_Y = yres - D_pad_Radius * scale;
641 if (g_Config.touchAnalogStick.show) {
642 D_pad_Y -= 200 * scale;
643 }
644 initTouchPos(g_Config.touchDpad, D_pad_X, D_pad_Y);
645
646 //analog stick-------------------------------------------------------
647 //keep the analog stick right below the D pad
648 int analog_stick_X = D_pad_X;
649 int analog_stick_Y = yres - 80 * scale;
650 initTouchPos(g_Config.touchAnalogStick, analog_stick_X, analog_stick_Y);
651
652 //right analog stick-------------------------------------------------
653 //keep the right analog stick right below the face buttons
654 int right_analog_stick_X = Action_button_center_X;
655 int right_analog_stick_Y = yres - 80 * scale;
656 initTouchPos(g_Config.touchRightAnalogStick, right_analog_stick_X, right_analog_stick_Y);
657
658 //select, start, throttle--------------------------------------------
659 //space between the bottom keys (space between select, start and un-throttle)
660 float bottom_key_spacing = 100;
661 if (dp_xres < 750) {
662 bottom_key_spacing *= 0.8f;
663 }
664
665 int start_key_X = halfW + bottom_key_spacing * scale;
666 int start_key_Y = yres - 60 * scale;
667 initTouchPos(g_Config.touchStartKey, start_key_X, start_key_Y);
668
669 int select_key_X = halfW;
670 int select_key_Y = yres - 60 * scale;
671 initTouchPos(g_Config.touchSelectKey, select_key_X, select_key_Y);
672
673 int fast_forward_key_X = halfW - bottom_key_spacing * scale;
674 int fast_forward_key_Y = yres - 60 * scale;
675 initTouchPos(g_Config.touchFastForwardKey, fast_forward_key_X, fast_forward_key_Y);
676
677 // L and R------------------------------------------------------------
678 // Put them above the analog stick / above the buttons to the right.
679 // The corners were very hard to reach..
680
681 int l_key_X = 60 * scale;
682 int l_key_Y = yres - 380 * scale;
683 initTouchPos(g_Config.touchLKey, l_key_X, l_key_Y);
684
685 int r_key_X = xres - 60 * scale;
686 int r_key_Y = l_key_Y;
687 initTouchPos(g_Config.touchRKey, r_key_X, r_key_Y);
688
689 //Combo key
690 int combo_key_X = halfW + bottom_key_spacing * scale * 1.2f;
691 int combo_key_Y = yres / 2;
692 initTouchPos(g_Config.touchCombo0, combo_key_X, combo_key_Y);
693
694 int combo1_key_X = halfW + bottom_key_spacing * scale * 2.2f;
695 int combo1_key_Y = yres / 2;
696 initTouchPos(g_Config.touchCombo1, combo1_key_X, combo1_key_Y);
697
698 int combo2_key_X = halfW + bottom_key_spacing * scale * 3.2f;
699 int combo2_key_Y = yres / 2;
700 initTouchPos(g_Config.touchCombo2, combo2_key_X, combo2_key_Y);
701
702 int combo3_key_X = halfW + bottom_key_spacing * scale * 1.2f;
703 int combo3_key_Y = yres / 3;
704 initTouchPos(g_Config.touchCombo3, combo3_key_X, combo3_key_Y);
705
706 int combo4_key_X = halfW + bottom_key_spacing * scale * 2.2f;
707 int combo4_key_Y = yres / 3;
708 initTouchPos(g_Config.touchCombo4, combo4_key_X, combo4_key_Y);
709
710 int combo5_key_X = halfW - bottom_key_spacing * scale * 1.2f;
711 int combo5_key_Y = yres / 2;
712 initTouchPos(g_Config.touchCombo5, combo5_key_X, combo5_key_Y);
713
714 int combo6_key_X = halfW - bottom_key_spacing * scale * 2.2f;
715 int combo6_key_Y = yres / 2;
716 initTouchPos(g_Config.touchCombo6, combo6_key_X, combo6_key_Y);
717
718 int combo7_key_X = halfW - bottom_key_spacing * scale * 3.2f;
719 int combo7_key_Y = yres / 2;
720 initTouchPos(g_Config.touchCombo7, combo7_key_X, combo7_key_Y);
721
722 int combo8_key_X = halfW - bottom_key_spacing * scale * 1.2f;
723 int combo8_key_Y = yres / 3;
724 initTouchPos(g_Config.touchCombo8, combo8_key_X, combo8_key_Y);
725
726 int combo9_key_X = halfW - bottom_key_spacing * scale * 2.2f;
727 int combo9_key_Y = yres / 3;
728 initTouchPos(g_Config.touchCombo9, combo9_key_X, combo9_key_Y);
729 }
730
CreatePadLayout(float xres,float yres,bool * pause,bool showPauseButton,ControlMapper * controllMapper)731 UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, bool showPauseButton, ControlMapper* controllMapper) {
732 using namespace UI;
733
734 AnchorLayout *root = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
735 if (!g_Config.bShowTouchControls) {
736 return root;
737 }
738
739 struct ButtonOffset {
740 float x;
741 float y;
742 };
743 auto buttonLayoutParams = [=](const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) {
744 return new AnchorLayoutParams(touch.x * xres + off.x, touch.y * yres + off.y, NONE, NONE, true);
745 };
746
747 // Space between the PSP buttons (traingle, circle, square and cross)
748 const float actionButtonSpacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
749 // Position of the circle button (the PSP circle button). It is the farthest to the right.
750 ButtonOffset circleOffset{ actionButtonSpacing, 0.0f };
751 ButtonOffset crossOffset{ 0.0f, actionButtonSpacing };
752 ButtonOffset triangleOffset{ 0.0f, -actionButtonSpacing };
753 ButtonOffset squareOffset{ -actionButtonSpacing, 0.0f };
754
755 const int halfW = xres / 2;
756
757 const ImageID roundImage = g_Config.iTouchButtonStyle ? ImageID("I_ROUND_LINE") : ImageID("I_ROUND");
758 const ImageID rectImage = g_Config.iTouchButtonStyle ? ImageID("I_RECT_LINE") : ImageID("I_RECT");
759 const ImageID shoulderImage = g_Config.iTouchButtonStyle ? ImageID("I_SHOULDER_LINE") : ImageID("I_SHOULDER");
760 const ImageID dirImage = g_Config.iTouchButtonStyle ? ImageID("I_DIR_LINE") : ImageID("I_DIR");
761 const ImageID stickImage = g_Config.iTouchButtonStyle ? ImageID("I_STICK_LINE") : ImageID("I_STICK");
762 const ImageID stickBg = g_Config.iTouchButtonStyle ? ImageID("I_STICK_BG_LINE") : ImageID("I_STICK_BG");
763
764 auto addPSPButton = [=](int buttonBit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * {
765 if (touch.show) {
766 return root->Add(new PSPButton(buttonBit, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch, off)));
767 }
768 return nullptr;
769 };
770 auto addBoolButton = [=](bool *value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> BoolButton * {
771 if (touch.show) {
772 return root->Add(new BoolButton(value, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch)));
773 }
774 return nullptr;
775 };
776 auto addComboKey = [=](const ConfigCustomButton& cfg, const char *key, const ConfigTouchPos &touch) -> ComboKey * {
777 using namespace CustomKey;
778 if (touch.show) {
779 auto aux = root->Add(new ComboKey(cfg.key, key, cfg.toggle, controllMapper,
780 g_Config.iTouchButtonStyle == 0 ? comboKeyShapes[cfg.shape].i : comboKeyShapes[cfg.shape].l, comboKeyShapes[cfg.shape].i,
781 comboKeyImages[cfg.image].i, touch.scale, comboKeyShapes[cfg.shape].d, buttonLayoutParams(touch)));
782 aux->SetAngle(comboKeyImages[cfg.image].r, comboKeyShapes[cfg.shape].r);
783 aux->FlipImageH(comboKeyShapes[cfg.shape].f);
784 return aux;
785 }
786 return nullptr;
787 };
788
789 if (showPauseButton) {
790 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);
791 }
792
793 // touchActionButtonCenter.show will always be true, since that's the default.
794 if (g_Config.bShowTouchCircle)
795 addPSPButton(CTRL_CIRCLE, "Circle button", roundImage, ImageID("I_ROUND"), ImageID("I_CIRCLE"), g_Config.touchActionButtonCenter, circleOffset);
796 if (g_Config.bShowTouchCross)
797 addPSPButton(CTRL_CROSS, "Cross button", roundImage, ImageID("I_ROUND"), ImageID("I_CROSS"), g_Config.touchActionButtonCenter, crossOffset);
798 if (g_Config.bShowTouchTriangle)
799 addPSPButton(CTRL_TRIANGLE, "Triangle button", roundImage, ImageID("I_ROUND"), ImageID("I_TRIANGLE"), g_Config.touchActionButtonCenter, triangleOffset);
800 if (g_Config.bShowTouchSquare)
801 addPSPButton(CTRL_SQUARE, "Square button", roundImage, ImageID("I_ROUND"), ImageID("I_SQUARE"), g_Config.touchActionButtonCenter, squareOffset);
802
803 addPSPButton(CTRL_START, "Start button", rectImage, ImageID("I_RECT"), ImageID("I_START"), g_Config.touchStartKey);
804 addPSPButton(CTRL_SELECT, "Select button", rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), g_Config.touchSelectKey);
805
806 BoolButton *fastForward = addBoolButton(&PSP_CoreParameter().fastForward, "Fast-forward button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchFastForwardKey);
807 if (fastForward) {
808 fastForward->SetAngle(180.0f);
809 fastForward->OnChange.Add([](UI::EventParams &e) {
810 if (e.a && coreState == CORE_STEPPING) {
811 Core_EnableStepping(false);
812 }
813 return UI::EVENT_DONE;
814 });
815 }
816
817 addPSPButton(CTRL_LTRIGGER, "Left shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_L"), g_Config.touchLKey);
818 PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, "Right shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_R"), g_Config.touchRKey);
819 if (rTrigger)
820 rTrigger->FlipImageH(true);
821
822 if (g_Config.touchDpad.show)
823 root->Add(new PSPDpad(dirImage, "D-pad", ImageID("I_DIR"), ImageID("I_ARROW"), g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad)));
824
825 if (g_Config.touchAnalogStick.show)
826 root->Add(new PSPStick(stickBg, "Left analog stick", stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick)));
827
828 if (g_Config.touchRightAnalogStick.show) {
829 if (g_Config.bRightAnalogCustom)
830 root->Add(new PSPCustomStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
831 else
832 root->Add(new PSPStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
833 }
834
835 addComboKey(g_Config.CustomKey0, "Custom 1 button", g_Config.touchCombo0);
836 addComboKey(g_Config.CustomKey1, "Custom 2 button", g_Config.touchCombo1);
837 addComboKey(g_Config.CustomKey2, "Custom 3 button", g_Config.touchCombo2);
838 addComboKey(g_Config.CustomKey3, "Custom 4 button", g_Config.touchCombo3);
839 addComboKey(g_Config.CustomKey4, "Custom 5 button", g_Config.touchCombo4);
840 addComboKey(g_Config.CustomKey5, "Custom 6 button", g_Config.touchCombo5);
841 addComboKey(g_Config.CustomKey6, "Custom 7 button", g_Config.touchCombo6);
842 addComboKey(g_Config.CustomKey7, "Custom 8 button", g_Config.touchCombo7);
843 addComboKey(g_Config.CustomKey8, "Custom 9 button", g_Config.touchCombo8);
844 addComboKey(g_Config.CustomKey9, "Custom 10 button", g_Config.touchCombo9);
845
846 if (g_Config.bGestureControlEnabled)
847 root->Add(new GestureGamepad(controllMapper));
848
849 return root;
850 }
851
852
Touch(const TouchInput & input)853 void GestureGamepad::Touch(const TouchInput &input) {
854 if (usedPointerMask & (1 << input.id)) {
855 if (input.id == dragPointerId_)
856 dragPointerId_ = -1;
857 return;
858 }
859
860 if (input.flags & TOUCH_RELEASE_ALL) {
861 dragPointerId_ = -1;
862 return;
863 }
864
865 if (input.flags & TOUCH_DOWN) {
866 if (dragPointerId_ == -1) {
867 dragPointerId_ = input.id;
868 lastX_ = input.x;
869 lastY_ = input.y;
870
871 const float now = time_now_d();
872 if (now - lastTapRelease_ < 0.3f && !haveDoubleTapped_) {
873 if (g_Config.iDoubleTapGesture != 0 )
874 controllMapper_->pspKey(GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_DOWN);
875 haveDoubleTapped_ = true;
876 }
877
878 lastTouchDown_ = now;
879 }
880 }
881 if (input.flags & TOUCH_MOVE) {
882 if (input.id == dragPointerId_) {
883 deltaX_ += input.x - lastX_;
884 deltaY_ += input.y - lastY_;
885 lastX_ = input.x;
886 lastY_ = input.y;
887 }
888 }
889 if (input.flags & TOUCH_UP) {
890 if (input.id == dragPointerId_) {
891 dragPointerId_ = -1;
892 if (time_now_d() - lastTouchDown_ < 0.3f)
893 lastTapRelease_ = time_now_d();
894
895 if (haveDoubleTapped_) {
896 if (g_Config.iDoubleTapGesture != 0)
897 controllMapper_->pspKey(GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_UP);
898 haveDoubleTapped_ = false;
899 }
900 }
901 }
902 }
903
Update()904 void GestureGamepad::Update() {
905 const float th = 1.0f;
906 float dx = deltaX_ * g_dpi_scale_x * g_Config.fSwipeSensitivity;
907 float dy = deltaY_ * g_dpi_scale_y * g_Config.fSwipeSensitivity;
908 if (g_Config.iSwipeRight != 0) {
909 if (dx > th) {
910 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeRight-1], KEY_DOWN);
911 swipeRightReleased_ = false;
912 } else if (!swipeRightReleased_) {
913 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeRight-1], KEY_UP);
914 swipeRightReleased_ = true;
915 }
916 }
917 if (g_Config.iSwipeLeft != 0) {
918 if (dx < -th) {
919 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_DOWN);
920 swipeLeftReleased_ = false;
921 } else if (!swipeLeftReleased_) {
922 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_UP);
923 swipeLeftReleased_ = true;
924 }
925 }
926 if (g_Config.iSwipeUp != 0) {
927 if (dy < -th) {
928 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeUp-1], KEY_DOWN);
929 swipeUpReleased_ = false;
930 } else if (!swipeUpReleased_) {
931 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeUp-1], KEY_UP);
932 swipeUpReleased_ = true;
933 }
934 }
935 if (g_Config.iSwipeDown != 0) {
936 if (dy > th) {
937 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeDown-1], KEY_DOWN);
938 swipeDownReleased_ = false;
939 } else if (!swipeDownReleased_) {
940 controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeDown-1], KEY_UP);
941 swipeDownReleased_ = true;
942 }
943 }
944 deltaX_ *= g_Config.fSwipeSmoothing;
945 deltaY_ *= g_Config.fSwipeSmoothing;
946 }
947