From 95a1c9555d2870feedc58a205be83c980d23531d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 30 Jun 2023 16:52:19 +0200 Subject: [PATCH] Base for controller touch screen input * Supports analog stick, touchpad and gryo as input sources * Relative and absolute input for sticks and touchpad Still needs work before ready to merge, UI is not hooked up yet, cursor drawing is a hack and doesn't work with OpenGL and the input code needs some refining. --- src/frontend/qt_sdl/Input.cpp | 204 ++++++++- src/frontend/qt_sdl/Input.h | 24 ++ .../qt_sdl/InputConfig/InputConfigDialog.ui | 408 +++++++++++++++--- src/frontend/qt_sdl/main.cpp | 15 + 4 files changed, 599 insertions(+), 52 deletions(-) diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index c1ef87c9..d02b87b5 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -28,6 +28,7 @@ namespace Input int JoystickID; SDL_Joystick* Joystick = nullptr; +SDL_GameController* GameController = nullptr; u32 KeyInputMask, JoyInputMask; u32 KeyHotkeyMask, JoyHotkeyMask; @@ -36,6 +37,16 @@ u32 HotkeyPress, HotkeyRelease; u32 InputMask; +u8 JoyTouchX, JoyTouchY; +bool JoyTouching; +bool JoyTouchReleased; +JoystickTouchMode TouchMode; +AnalogStick TouchAnalogStick; +JoystickTouchMovementStyle MovementStyle; + +bool touchpadTouching; +float touchpadLastX; +float touchpadLastY; void Init() { @@ -47,6 +58,18 @@ void Init() JoyHotkeyMask = 0; HotkeyMask = 0; LastHotkeyMask = 0; + + JoyTouchX = 0; + JoyTouchY = 0; + JoyTouching = false; + + TouchMode = ANALOG; + JoyTouchReleased = false; + MovementStyle = ABSOLUTE; + + touchpadLastX = 0; + touchpadLastY = 0; + touchpadTouching = false; } @@ -65,6 +88,14 @@ void OpenJoystick() JoystickID = 0; Joystick = SDL_JoystickOpen(JoystickID); + + if (Joystick != nullptr) + { + if (!SDL_IsGameController(JoystickID)) + return; + + GameController = SDL_GameControllerOpen(JoystickID); + } } void CloseJoystick() @@ -184,6 +215,168 @@ bool JoystickButtonDown(int val) return false; } +bool JoystickTouchModeAvailable(JoystickTouchMode mode, AnalogStick stick) +{ + if (GameController == nullptr) + return false; + + switch (mode) { + case ANALOG: + { + SDL_GameControllerAxis xAxis = stick == LEFT + ? SDL_CONTROLLER_AXIS_LEFTX + : SDL_CONTROLLER_AXIS_RIGHTX; + SDL_GameControllerAxis yAxis = stick == LEFT + ? SDL_CONTROLLER_AXIS_LEFTY + : SDL_CONTROLLER_AXIS_RIGHTY; + + return SDL_GameControllerHasAxis(GameController, xAxis) + && SDL_GameControllerHasAxis(GameController, yAxis); + } + case TOUCHPAD: + return SDL_GameControllerGetNumTouchpads(GameController) != 0; + case GYROSCOPE: + return SDL_GameControllerHasSensor(GameController, SDL_SENSOR_GYRO); + case NONE: + return true; + } +} + +constexpr float psTouchpadAspectMul = ((52.f / 23.f) / (4.f / 3.f)); + +// The touchpad is about 52x23 mm on the PS4 controller, and the DualSense looks similar +// so correct it to be more like the DS's aspect ratio +float TouchPadCorrectAspect(float x) +{ + SDL_GameControllerType type = SDL_GameControllerGetType(GameController); + + if (type != SDL_CONTROLLER_TYPE_PS4 && type != SDL_CONTROLLER_TYPE_PS5) + return x; + + + float pos = (x - 0.5f) * psTouchpadAspectMul; + pos = std::clamp(pos, -.5f, .5f); + return pos + 0.5f; +} + +float JoyTouchXFloat, JoyTouchYFloat; + +void HandleRelativeInput(float dx, float dy, float sensitivity) +{ + JoyTouchXFloat = std::clamp((float) JoyTouchXFloat + (dx * sensitivity), 0.f, 255.f); + JoyTouchYFloat = std::clamp((float) JoyTouchYFloat + (dy * sensitivity), 0.f, 192.f); + + JoyTouchX = (u8) std::round(JoyTouchXFloat); + JoyTouchY = (u8) std::round(JoyTouchYFloat); +} + +void UpdateJoystickTouch() +{ + bool newTouching = false; + + JoyTouchReleased = false; + + if (!JoystickTouchModeAvailable(TouchMode, TouchAnalogStick)) + { + if (JoyTouching) + { + JoyTouchReleased = true; + JoyTouching = false; + } + + return; + } + + if (TouchMode == TOUCHPAD) + { + u8 state; + float x, y, pressure; + SDL_GameControllerGetTouchpadFinger(GameController, 0, 0, &state, &x, &y, &pressure); + printf("Touchpad: state: %u, x: %f, y: %f, pressure: %f\n", state, x, y, pressure); + + bool haveTouchpadButton = SDL_GameControllerHasButton(GameController, SDL_CONTROLLER_BUTTON_TOUCHPAD); + + if (MovementStyle == RELATIVE && haveTouchpadButton) + { + if (state == 1) + { + float dx, dy; + + if (!touchpadTouching) + { + touchpadTouching = true; + dx = 0; + dy = 0; + } + else + { + dx = (x - touchpadLastX) * psTouchpadAspectMul; + dy = y - touchpadLastY; + } + + HandleRelativeInput(dx, dy, 256.f); + newTouching = SDL_GameControllerGetButton(GameController, SDL_CONTROLLER_BUTTON_TOUCHPAD); + + touchpadLastX = x; + touchpadLastY = y; + } + else + { + touchpadLastX = 0; + touchpadLastY = 0; + touchpadTouching = false; + } + } + else + { + if (SDL_GameControllerHasButton(GameController, SDL_CONTROLLER_BUTTON_TOUCHPAD)) + newTouching = SDL_GameControllerGetButton(GameController, SDL_CONTROLLER_BUTTON_TOUCHPAD); + else + newTouching = state == 1; + + JoyTouchX = (u8) round(TouchPadCorrectAspect(x) * 256.f); + JoyTouchY = (u8) round(y * 192.f); + } + } + + if (TouchMode == ANALOG) + { + s16 x = SDL_GameControllerGetAxis(GameController, SDL_CONTROLLER_AXIS_LEFTX); + s16 y = SDL_GameControllerGetAxis(GameController, SDL_CONTROLLER_AXIS_LEFTY); + float fx = ((float) x) / 32768.f; + float fy = ((float) y) / 32768.f; + + if (MovementStyle == RELATIVE) + { + HandleRelativeInput(fx, fy, 5.f); + } + else + { + JoyTouchX = (u8) std::round(((fx + 1.0f) / 2) * 256.f); + JoyTouchY = (u8) std::round(((fy + 1.0f) / 2) * 192.f); + } + + newTouching = SDL_GameControllerGetAxis(GameController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) > 0.5; + } + + if (TouchMode == GYROSCOPE) + { + float gyroPos[3] = {0}; + + SDL_GameControllerSetSensorEnabled(GameController, SDL_SENSOR_GYRO, SDL_TRUE); + + SDL_GameControllerGetSensorData(GameController, SDL_SENSOR_GYRO, (float*) &gyroPos, 3); + HandleRelativeInput(-gyroPos[1], -gyroPos[0], 5.f); + + newTouching = SDL_GameControllerGetAxis(GameController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) > 0.5; + } + + if (!newTouching && JoyTouching) + JoyTouchReleased = true; + + JoyTouching = newTouching; +} + void Process() { SDL_JoystickUpdate(); @@ -192,8 +385,14 @@ void Process() { if (!SDL_JoystickGetAttached(Joystick)) { + if (GameController != nullptr) + { + SDL_GameControllerClose(GameController); + GameController = nullptr; + } + SDL_JoystickClose(Joystick); - Joystick = NULL; + Joystick = nullptr; } } if (!Joystick && (SDL_NumJoysticks() > 0)) @@ -218,6 +417,9 @@ void Process() HotkeyPress = HotkeyMask & ~LastHotkeyMask; HotkeyRelease = LastHotkeyMask & ~HotkeyMask; LastHotkeyMask = HotkeyMask; + + if (TouchMode != NONE) + UpdateJoystickTouch(); } diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h index 0d2292d0..56a4e131 100644 --- a/src/frontend/qt_sdl/Input.h +++ b/src/frontend/qt_sdl/Input.h @@ -29,6 +29,30 @@ extern SDL_Joystick* Joystick; extern u32 InputMask; + +extern u8 JoyTouchX, JoyTouchY; +extern bool JoyTouching; +extern bool JoyTouchReleased; + +enum JoystickTouchMode +{ + NONE = 0, ANALOG, TOUCHPAD, GYROSCOPE +}; + +enum AnalogStick +{ + LEFT = 0, RIGHT +}; + +enum JoystickTouchMovementStyle +{ + ABSOLUTE = 0, RELATIVE +}; + +extern JoystickTouchMode TouchMode; +extern AnalogStick TouchAnalogStick; +extern JoystickTouchMovementStyle MovementStyle; + void Init(); // set joystickID before calling openJoystick() diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui index 61d7e454..7e30401e 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui @@ -7,7 +7,7 @@ 0 0 770 - 678 + 768 @@ -30,10 +30,59 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Joystick: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html> + + + + + + + + + Configuring mappings for instance X + + + - 0 + 3 @@ -2277,55 +2326,312 @@ General hotkeys - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Joystick: - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html> - - - - - - - - - Configuring mappings for instance X - + + + Touch screen + + + + + + + 12 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Touch screen input + + + + + + Controller input + + + + + + + + 6 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + None (mouse only) + + + + + + + Analog stick + + + + + + + Touchpad + + + + + + + Gyroscope + + + + + + + + + + Movement style + + + + + + + + 0 + 0 + + + + + 6 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + Relative + + + + + + + Absolute + + + + + + + + + + Recenter + + + + + + + [PLACEHOLDER] + + + + + + + Analog stick + + + + + + + + 0 + 0 + + + + + 6 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + Left + + + + + + + Right + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Cursor + + + + + + Color + + + + + + + + + + + + + + Hide after + + + + + + + + 0 + 0 + + + + + 6 + + + 0 + + + 6 + + + 0 + + + 6 + + + + + + + + seconds of inactivity + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a7a1b969..df71d767 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -469,6 +469,12 @@ void EmuThread::run() OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); } + if (Input::JoyTouching) + NDS::TouchScreen(Input::JoyTouchX, Input::JoyTouchY); + + if (Input::JoyTouchReleased) + NDS::ReleaseScreen(); + // microphone input AudioInOut::MicProcess(); @@ -1063,6 +1069,15 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) { painter.setTransform(screenTrans[i]); painter.drawImage(screenrc, screen[screenKind[i]]); + + if (i == 1) + { + //QTransform cursorTrans = screenTrans[i].translate(Input::JoyTouchX, Input::JoyTouchY); + //painter.setTransform(cursorTrans); + QRect cursorRect = QRect(Input::JoyTouchX - 3, Input::JoyTouchY - 3, 5, 5); + painter.setPen(QColor::fromRgb(255, 0, 0)); + painter.drawRoundedRect(cursorRect, 5, 5); + } } }