Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic mappable motion gesture #13107

Merged
merged 1 commit into from
Aug 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,14 @@ static ConfigSetting generalSettings[] = {
ConfigSetting("RightAnalogPress", &g_Config.iRightAnalogPress, 0, true, true),
ConfigSetting("RightAnalogCustom", &g_Config.bRightAnalogCustom, false, true, true),
ConfigSetting("RightAnalogDisableDiagonal", &g_Config.bRightAnalogDisableDiagonal, 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, true, true),

// "default" means let emulator decide, "" means disable.
ConfigSetting("ReportingHost", &g_Config.sReportHost, "default"),
Expand Down
10 changes: 10 additions & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,16 @@ struct Config {
bool bRightAnalogCustom;
bool bRightAnalogDisableDiagonal;

// 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;
Expand Down
33 changes: 33 additions & 0 deletions UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,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;
});
}

controlsSettings->Add(new ItemHeader(co->T("Keyboard", "Keyboard Control Settings")));
Expand Down Expand Up @@ -1989,3 +1993,32 @@ void HostnameSelectScreen::OnCompleted(DialogResult result) {
if (result == DR_OK)
*value_ = StripSpaces(addrView_->GetText());
}

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<UIScreen>(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);
}

6 changes: 6 additions & 0 deletions UI/GameSettingsScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,9 @@ class HostnameSelectScreen : public PopupScreen {
std::string lastResolved_ = "";
bool lastResolvedResult_ = false;
};


class GestureMappingScreen : public UIDialogScreenWithBackground {
public:
void CreateViews() override;
};
116 changes: 116 additions & 0 deletions UI/GamepadEmu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include "Core/ControlMapper.h"
#include "UI/GamepadEmu.h"

static uint32_t usedPointerMask = 0;

static u32 GetButtonColor() {
return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
}
Expand Down Expand Up @@ -97,6 +99,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))
Expand All @@ -106,9 +109,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;
}
}

Expand Down Expand Up @@ -231,6 +236,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);
}
}
Expand All @@ -242,6 +248,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);
}
}
Expand Down Expand Up @@ -389,6 +396,7 @@ void PSPStick::Touch(const TouchInput &input) {
centerX_ = bounds_.centerX();
centerY_ = bounds_.centerY();
__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
usedPointerMask = 0;
return;
}
if (input.flags & TOUCH_DOWN) {
Expand All @@ -401,6 +409,7 @@ void PSPStick::Touch(const TouchInput &input) {
centerY_ = bounds_.centerY();
}
dragPointerId_ = input.id;
usedPointerMask |= 1 << input.id;
ProcessTouch(input.x, input.y, true);
}
}
Expand All @@ -414,6 +423,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);
}
}
Expand Down Expand Up @@ -486,6 +496,7 @@ void PSPCustomStick::Touch(const TouchInput &input) {
centerY_ = bounds_.centerY();
posX_ = 0.0f;
posY_ = 0.0f;
usedPointerMask = 0;
return;
}
if (input.flags & TOUCH_DOWN) {
Expand All @@ -498,6 +509,7 @@ void PSPCustomStick::Touch(const TouchInput &input) {
centerY_ = bounds_.centerY();
}
dragPointerId_ = input.id;
usedPointerMask |= 1 << input.id;
ProcessTouch(input.x, input.y, true);
}
}
Expand All @@ -511,6 +523,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);
}
}
Expand Down Expand Up @@ -813,5 +826,108 @@ UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, ControlMappe
addComboKey(g_Config.CustomKey8, "Custom 9 button", g_Config.touchCombo8);
addComboKey(g_Config.CustomKey9, "Custom 10 button", g_Config.touchCombo9);

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_d();
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_d() - lastTouchDown_ < 0.3f)
lastTapRelease_ = time_now_d();

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;
}
23 changes: 23 additions & 0 deletions UI/GamepadEmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,26 @@ namespace CustomKey {
};
static_assert(ARRAY_SIZE(comboKeyList) <= 64, "Too many key for a uint64_t bit mask");
};

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;
};