From 859de98c21715253f4571da493c040afe80cb7d5 Mon Sep 17 00:00:00 2001 From: iota97 Date: Tue, 4 Aug 2020 18:41:29 +0200 Subject: [PATCH] Basic mappable gesture --- Core/Config.cpp | 9 +++ Core/Config.h | 10 ++++ UI/GameSettingsScreen.cpp | 33 +++++++++++ UI/GameSettingsScreen.h | 6 ++ UI/GamepadEmu.cpp | 116 ++++++++++++++++++++++++++++++++++++++ UI/GamepadEmu.h | 23 ++++++++ 6 files changed, 197 insertions(+) diff --git a/Core/Config.cpp b/Core/Config.cpp index d28e9c24948d..1de0d493d85d 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -458,12 +458,21 @@ static ConfigSetting generalSettings[] = { ConfigSetting("GridView2", &g_Config.bGridView2, true), ConfigSetting("GridView3", &g_Config.bGridView3, false), ConfigSetting("ComboMode", &g_Config.iComboMode, 0), + ConfigSetting("RightAnalogUp", &g_Config.iRightAnalogUp, 0, true, true), ConfigSetting("RightAnalogDown", &g_Config.iRightAnalogDown, 0, true, true), ConfigSetting("RightAnalogLeft", &g_Config.iRightAnalogLeft, 0, true, true), ConfigSetting("RightAnalogRight", &g_Config.iRightAnalogRight, 0, true, true), ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0, true, true), ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false, true, true), + ConfigSetting("SwipeUp", &g_Config.iSwipeUp, 0, true, true), + ConfigSetting("SwipeDown", &g_Config.iSwipeDown, 0, true, true), + ConfigSetting("SwipeLeft", &g_Config.iSwipeLeft, 0, true, true), + ConfigSetting("SwipeRight", &g_Config.iSwipeRight, 0, true, true), + ConfigSetting("SwipeSensitivity", &g_Config.fSwipeSensitivity, 1.0f, true, true), + ConfigSetting("SwipeSmoothing", &g_Config.fSwipeSmoothing, 0.3f, true, true), + ConfigSetting("DoubleTapGesture", &g_Config.iDoubleTapGesture, 0, true, true), + ConfigSetting("GestureControlEnabled", &g_Config.bGestureControlEnabled, false), // "default" means let emulator decide, "" means disable. ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"), diff --git a/Core/Config.h b/Core/Config.h index 71fa93e3575d..6bea1bfe43a3 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -301,6 +301,16 @@ struct Config { int iRightAnalogPress; bool bRightAnalogCustom; + // Motion gesture controller + bool bGestureControlEnabled; + int iSwipeUp; + int iSwipeDown; + int iSwipeLeft; + int iSwipeRight; + float fSwipeSensitivity; + float fSwipeSmoothing; + int iDoubleTapGesture; + // Disable diagonals bool bDisableDpadDiagonals; bool bGamepadOnlyFocused; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 5594e2e0355a..192e64be2c73 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -663,6 +663,10 @@ void GameSettingsScreen::CreateViews() { static const char *touchControlStyles[] = {"Classic", "Thin borders", "Glowing borders"}; View *style = controlsSettings->Add(new PopupMultiChoice(&g_Config.iTouchButtonStyle, co->T("Button style"), touchControlStyles, 0, ARRAY_SIZE(touchControlStyles), co->GetName(), screenManager())); style->SetEnabledPtr(&g_Config.bShowTouchControls); + controlsSettings->Add(new Choice(co->T("Gesture mapping")))->OnClick.Add([=](EventParams &e) { + screenManager()->push(new GestureMappingScreen()); + return UI::EVENT_DONE; + }); } #ifdef _WIN32 @@ -1976,3 +1980,32 @@ void SettingInfoMessage::Draw(UIContext &dc) { text_->SetTextColor(whiteAlpha(alpha)); ViewGroup::Draw(dc); } + +void GestureMappingScreen::CreateViews() { + using namespace UI; + + auto di = GetI18NCategory("Dialog"); + auto co = GetI18NCategory("Controls"); + auto mc = GetI18NCategory("MappableControls"); + + root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); + Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(130, WRAP_CONTENT, 10, NONE, NONE, 10)); + root_->Add(back)->OnClick.Handle(this, &UIScreen::OnBack); + TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 140, new AnchorLayoutParams(10, 0, 10, 0, false)); + root_->Add(tabHolder); + ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL); + tabHolder->AddTab(co->T("Gesture"), rightPanel); + LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); + vert->SetSpacing(0); + + static const char *gestureButton[] = {"None", "L", "R", "Square", "Triangle", "Circle", "Cross", "D-pad up", "D-pad down", "D-pad left", "D-pad right", "Start", "Select"}; + vert->Add(new CheckBox(&g_Config.bGestureControlEnabled, co->T("Enable gesture control"))); + vert->Add(new PopupMultiChoice(&g_Config.iSwipeUp, mc->T("Swipe Up"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupMultiChoice(&g_Config.iSwipeDown, mc->T("Swipe Down"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupMultiChoice(&g_Config.iSwipeLeft, mc->T("Swipe Left"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupMultiChoice(&g_Config.iSwipeRight, mc->T("Swipe Right"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSensitivity, 0.01f, 1.0f, co->T("Swipe sensitivity"), 0.01f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupSliderChoiceFloat(&g_Config.fSwipeSmoothing, 0.0f, 0.95f, co->T("Swipe smoothing"), 0.05f, screenManager(), "x"))->SetEnabledPtr(&g_Config.bGestureControlEnabled); + vert->Add(new PopupMultiChoice(&g_Config.iDoubleTapGesture, mc->T("Double tap button"), gestureButton, 0, ARRAY_SIZE(gestureButton), mc->GetName(), screenManager()))->SetEnabledPtr(&g_Config.bGestureControlEnabled); +} + diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 3b0859e0d060..a5697ae4af07 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -238,3 +238,9 @@ class HostnameSelectScreen : public PopupScreen { std::string lastResolved_ = ""; bool lastResolvedResult_ = false; }; + + +class GestureMappingScreen : public UIDialogScreenWithBackground { +public: + void CreateViews() override; +}; diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index ef3a1ad2be5c..caedf80851d8 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -30,6 +30,8 @@ #include "math/math_util.h" #include "ui/ui_context.h" +static uint32_t usedPointerMask = 0; + static u32 GetButtonColor() { return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080; } @@ -84,6 +86,7 @@ void MultiTouchButton::Touch(const TouchInput &input) { GamepadView::Touch(input); if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) { pointerDownMask_ |= 1 << input.id; + usedPointerMask |= 1 << input.id; } if (input.flags & TOUCH_MOVE) { if (bounds_.Contains(input.x, input.y)) @@ -93,9 +96,11 @@ void MultiTouchButton::Touch(const TouchInput &input) { } if (input.flags & TOUCH_UP) { pointerDownMask_ &= ~(1 << input.id); + usedPointerMask &= ~(1 << input.id); } if (input.flags & TOUCH_RELEASE_ALL) { pointerDownMask_ = 0; + usedPointerMask = 0; } } @@ -273,6 +278,7 @@ void PSPDpad::Touch(const TouchInput &input) { if (input.flags & TOUCH_DOWN) { if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) { dragPointerId_ = input.id; + usedPointerMask |= 1 << input.id; ProcessTouch(input.x, input.y, true); } } @@ -284,6 +290,7 @@ void PSPDpad::Touch(const TouchInput &input) { if (input.flags & TOUCH_UP) { if (input.id == dragPointerId_) { dragPointerId_ = -1; + usedPointerMask &= ~(1 << input.id); ProcessTouch(input.x, input.y, false); } } @@ -433,6 +440,7 @@ void PSPStick::Touch(const TouchInput &input) { centerY_ = bounds_.centerY(); __CtrlSetAnalogX(0.0f, stick_); __CtrlSetAnalogY(0.0f, stick_); + usedPointerMask = 0; return; } if (input.flags & TOUCH_DOWN) { @@ -445,6 +453,7 @@ void PSPStick::Touch(const TouchInput &input) { centerY_ = bounds_.centerY(); } dragPointerId_ = input.id; + usedPointerMask |= 1 << input.id; ProcessTouch(input.x, input.y, true); } } @@ -458,6 +467,7 @@ void PSPStick::Touch(const TouchInput &input) { dragPointerId_ = -1; centerX_ = bounds_.centerX(); centerY_ = bounds_.centerY(); + usedPointerMask &= ~(1 << input.id); ProcessTouch(input.x, input.y, false); } } @@ -533,6 +543,7 @@ void PSPCustomStick::Touch(const TouchInput &input) { centerY_ = bounds_.centerY(); posX_ = 0.0f; posY_ = 0.0f; + usedPointerMask = 0; return; } if (input.flags & TOUCH_DOWN) { @@ -545,6 +556,7 @@ void PSPCustomStick::Touch(const TouchInput &input) { centerY_ = bounds_.centerY(); } dragPointerId_ = input.id; + usedPointerMask |= 1 << input.id; ProcessTouch(input.x, input.y, true); } } @@ -558,6 +570,7 @@ void PSPCustomStick::Touch(const TouchInput &input) { dragPointerId_ = -1; centerX_ = bounds_.centerX(); centerY_ = bounds_.centerY(); + usedPointerMask &= ~(1 << input.id); ProcessTouch(input.x, input.y, false); } } @@ -865,5 +878,108 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause) { addComboKey(g_Config.iCombokey3, g_Config.bComboToggle3, roundImage, ImageID("I_ROUND"), comboKeyImages[3], g_Config.touchCombo3); addComboKey(g_Config.iCombokey4, g_Config.bComboToggle4, roundImage, ImageID("I_ROUND"), comboKeyImages[4], g_Config.touchCombo4); + if (g_Config.bGestureControlEnabled) + root->Add(new GestureGamepad()); + return root; } + + +void GestureGamepad::Touch(const TouchInput &input) { + 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}; + + if (usedPointerMask & (1 << input.id)) { + if (input.id == dragPointerId_) + dragPointerId_ = -1; + return; + } + + if (input.flags & TOUCH_RELEASE_ALL) { + dragPointerId_ = -1; + return; + } + + if (input.flags & TOUCH_DOWN) { + if (dragPointerId_ == -1) { + dragPointerId_ = input.id; + lastX_ = input.x; + lastY_ = input.y; + + const float now = time_now(); + if (now - lastTapRelease_ < 0.3f && !haveDoubleTapped_) { + if (g_Config.iDoubleTapGesture != 0 ) + __CtrlButtonDown(button[g_Config.iDoubleTapGesture-1]); + haveDoubleTapped_ = true; + } + + lastTouchDown_ = now; + } + } + if (input.flags & TOUCH_MOVE) { + if (input.id == dragPointerId_) { + deltaX_ += input.x - lastX_; + deltaY_ += input.y - lastY_; + lastX_ = input.x; + lastY_ = input.y; + } + } + if (input.flags & TOUCH_UP) { + if (input.id == dragPointerId_) { + dragPointerId_ = -1; + if (time_now() - lastTouchDown_ < 0.3f) + lastTapRelease_ = time_now(); + + if (haveDoubleTapped_) { + if (g_Config.iDoubleTapGesture != 0) + __CtrlButtonUp(button[g_Config.iDoubleTapGesture-1]); + haveDoubleTapped_ = false; + } + } + } +} + +void GestureGamepad::Update() { + 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}; + + const float th = 1.0f; + float dx = deltaX_ * g_dpi_scale_x * g_Config.fSwipeSensitivity; + float dy = deltaY_ * g_dpi_scale_y * g_Config.fSwipeSensitivity; + if (g_Config.iSwipeRight != 0) { + if (dx > th) { + __CtrlButtonDown(button[g_Config.iSwipeRight-1]); + swipeRightReleased_ = false; + } else if (!swipeRightReleased_) { + __CtrlButtonUp(button[g_Config.iSwipeRight-1]); + swipeRightReleased_ = true; + } + } + if (g_Config.iSwipeLeft != 0) { + if (dx < -th) { + __CtrlButtonDown(button[g_Config.iSwipeLeft-1]); + swipeLeftReleased_ = false; + } else if (!swipeLeftReleased_) { + __CtrlButtonUp(button[g_Config.iSwipeLeft-1]); + swipeLeftReleased_ = true; + } + } + if (g_Config.iSwipeUp != 0) { + if (dy < -th) { + __CtrlButtonDown(button[g_Config.iSwipeUp-1]); + swipeUpReleased_ = false; + } else if (!swipeUpReleased_) { + __CtrlButtonUp(button[g_Config.iSwipeUp-1]); + swipeUpReleased_ = true; + } + } + if (g_Config.iSwipeDown != 0) { + if (dy > th) { + __CtrlButtonDown(button[g_Config.iSwipeDown-1]); + swipeDownReleased_ = false; + } else if (!swipeDownReleased_) { + __CtrlButtonUp(button[g_Config.iSwipeDown-1]); + swipeDownReleased_ = true; + } + } + deltaX_ *= g_Config.fSwipeSmoothing; + deltaY_ *= g_Config.fSwipeSmoothing; +} diff --git a/UI/GamepadEmu.h b/UI/GamepadEmu.h index 9f2fbc2a1f71..3adde71ee00f 100644 --- a/UI/GamepadEmu.h +++ b/UI/GamepadEmu.h @@ -209,3 +209,26 @@ class ComboKey : public MultiTouchButton { int pspButtonBit_; bool toggle_; }; + +class GestureGamepad : public UI::View { +public: + GestureGamepad() {}; + + void Touch(const TouchInput &input) override; + void Update() override; + +protected: + + float lastX_ = 0.0f; + float lastY_ = 0.0f; + float deltaX_ = 0.0f; + float deltaY_ = 0.0f; + float lastTapRelease_ = 0.0f; + float lastTouchDown_ = 0.0f; + int dragPointerId_ = -1; + bool swipeLeftReleased_ = true; + bool swipeRightReleased_ = true; + bool swipeUpReleased_ = true; + bool swipeDownReleased_ = true; + bool haveDoubleTapped_ = false; +};