From 42bac60ec58163705dfffda876600d739f6488cc Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 12 Jul 2025 19:13:37 -0700 Subject: [PATCH] Add new touch control scheme ("gamepad") (#587) * Add new touch control scheme * Add export * Fix enum naming --- ISLE/isleapp.cpp | 12 +- ISLE/isleapp.h | 3 + .../include/legoeventnotificationparam.h | 1 + .../lego/legoomni/include/legoinputmanager.h | 13 ++ .../src/entity/legocameracontroller.cpp | 4 + .../legoomni/src/input/legoinputmanager.cpp | 115 +++++++++++++----- 6 files changed, 114 insertions(+), 34 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index d898e48b..fb511569 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -172,6 +172,7 @@ IsleApp::IsleApp() m_maxAllowedExtras = m_islandQuality <= 1 ? 10 : 20; m_transitionType = MxTransitionManager::e_mosaic; m_cursorSensitivity = 4; + m_touchScheme = LegoInputManager::e_gamepad; } // FUNCTION: ISLE 0x4011a0 @@ -650,7 +651,12 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) float y = SDL_clamp(event->tfinger.y, 0, 1) * g_targetHeight; if (InputManager()) { - InputManager()->QueueEvent(c_notificationMouseMove, LegoEventNotificationParam::c_lButtonState, x, y, 0); + MxU8 modifier = LegoEventNotificationParam::c_lButtonState; + if (InputManager()->HandleTouchEvent(event, g_isle->GetTouchScheme())) { + modifier |= LegoEventNotificationParam::c_motionHandled; + } + + InputManager()->QueueEvent(c_notificationMouseMove, modifier, x, y, 0); } g_lastMouseX = x; @@ -691,6 +697,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) float y = SDL_clamp(event->tfinger.y, 0, 1) * g_targetHeight; if (InputManager()) { + InputManager()->HandleTouchEvent(event, g_isle->GetTouchScheme()); InputManager()->QueueEvent(c_notificationButtonDown, LegoEventNotificationParam::c_lButtonState, x, y, 0); } @@ -738,6 +745,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) float y = SDL_clamp(event->tfinger.y, 0, 1) * g_targetHeight; if (InputManager()) { + InputManager()->HandleTouchEvent(event, g_isle->GetTouchScheme()); InputManager()->QueueEvent(c_notificationButtonUp, 0, x, y, 0); } break; @@ -1033,6 +1041,7 @@ bool IsleApp::LoadConfig() iniparser_set(dict, "isle:Max LOD", buf); iniparser_set(dict, "isle:Max Allowed Extras", SDL_itoa(m_maxAllowedExtras, buf, 10)); iniparser_set(dict, "isle:Transition Type", SDL_itoa(m_transitionType, buf, 10)); + iniparser_set(dict, "isle:Touch Scheme", SDL_itoa(m_touchScheme, buf, 10)); #ifdef EXTENSIONS iniparser_set(dict, "extensions", NULL); @@ -1103,6 +1112,7 @@ bool IsleApp::LoadConfig() m_maxAllowedExtras = iniparser_getint(dict, "isle:Max Allowed Extras", m_maxAllowedExtras); m_transitionType = (MxTransitionManager::TransitionType) iniparser_getint(dict, "isle:Transition Type", m_transitionType); + m_touchScheme = (LegoInputManager::TouchScheme) iniparser_getint(dict, "isle:Touch Scheme", m_touchScheme); const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL); if (deviceId != NULL) { diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index 7054f6d1..3322427e 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -3,6 +3,7 @@ #include "cursor.h" #include "lego1_export.h" +#include "legoinputmanager.h" #include "legoutils.h" #include "mxtransitionmanager.h" #include "mxtypes.h" @@ -53,6 +54,7 @@ class IsleApp { MxS32 GetDrawCursor() { return m_drawCursor; } MxS32 GetGameStarted() { return m_gameStarted; } MxFloat GetCursorSensitivity() { return m_cursorSensitivity; } + LegoInputManager::TouchScheme GetTouchScheme() { return m_touchScheme; } void SetWindowActive(MxS32 p_windowActive) { m_windowActive = p_windowActive; } void SetGameStarted(MxS32 p_gameStarted) { m_gameStarted = p_gameStarted; } @@ -102,6 +104,7 @@ class IsleApp { MxFloat m_maxLod; MxU32 m_maxAllowedExtras; MxTransitionManager::TransitionType m_transitionType; + LegoInputManager::TouchScheme m_touchScheme; }; extern IsleApp* g_isle; diff --git a/LEGO1/lego/legoomni/include/legoeventnotificationparam.h b/LEGO1/lego/legoomni/include/legoeventnotificationparam.h index a88c62f0..5569280c 100644 --- a/LEGO1/lego/legoomni/include/legoeventnotificationparam.h +++ b/LEGO1/lego/legoomni/include/legoeventnotificationparam.h @@ -18,6 +18,7 @@ class LegoEventNotificationParam : public MxNotificationParam { c_rButtonState = 2, c_modKey1 = 4, c_modKey2 = 8, + c_motionHandled = 16, }; // FUNCTION: LEGO1 0x10028690 diff --git a/LEGO1/lego/legoomni/include/legoinputmanager.h b/LEGO1/lego/legoomni/include/legoinputmanager.h index 5f3052c1..30170822 100644 --- a/LEGO1/lego/legoomni/include/legoinputmanager.h +++ b/LEGO1/lego/legoomni/include/legoinputmanager.h @@ -18,6 +18,8 @@ #include #endif +#include + class LegoCameraController; class LegoControlManager; class LegoWorld; @@ -89,6 +91,12 @@ class LegoInputManager : public MxPresenter { c_upOrDown = c_up | c_down }; + enum TouchScheme { + e_mouse = 0, + e_arrowKeys, + e_gamepad, + }; + LegoInputManager(); ~LegoInputManager() override; @@ -144,6 +152,7 @@ class LegoInputManager : public MxPresenter { void GetKeyboardState(); MxResult GetNavigationKeyStates(MxU32& p_keyFlags); MxResult GetNavigationTouchStates(MxU32& p_keyFlags); + LEGO1_EXPORT MxBool HandleTouchEvent(SDL_Event* p_event, TouchScheme p_touchScheme); // SYNTHETIC: LEGO1 0x1005b8d0 // LegoInputManager::`scalar deleting destructor' @@ -171,6 +180,10 @@ class LegoInputManager : public MxPresenter { MxBool m_useJoystick; // 0x334 MxBool m_unk0x335; // 0x335 MxBool m_unk0x336; // 0x336 + + std::map m_touchOrigins; + std::map m_touchFlags; + std::map m_touchLastMotion; }; // TEMPLATE: LEGO1 0x10028850 diff --git a/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp b/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp index 563ba615..3e977d5b 100644 --- a/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp +++ b/LEGO1/lego/legoomni/src/entity/legocameracontroller.cpp @@ -41,6 +41,10 @@ MxResult LegoCameraController::Create() // FUNCTION: BETA10 0x10067852 MxLong LegoCameraController::Notify(MxParam& p_param) { + if (((LegoEventNotificationParam&) p_param).GetModifier() & LegoEventNotificationParam::c_motionHandled) { + return SUCCESS; + } + switch (((MxNotificationParam&) p_param).GetNotification()) { case c_notificationDragEnd: { if (((((LegoEventNotificationParam&) p_param).GetModifier()) & LegoEventNotificationParam::c_lButtonState) == diff --git a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp index 27b0f814..ddcadaae 100644 --- a/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp +++ b/LEGO1/lego/legoomni/src/input/legoinputmanager.cpp @@ -528,42 +528,91 @@ void LegoInputManager::EnableInputProcessing() MxResult LegoInputManager::GetNavigationTouchStates(MxU32& p_keyStates) { - int count; - SDL_TouchID* touchDevices = SDL_GetTouchDevices(&count); + for (auto& [fingerID, touchFlags] : m_touchFlags) { + p_keyStates |= touchFlags; - if (touchDevices) { - auto applyFingerNavigation = [&p_keyStates](SDL_TouchID p_touchId) { - int count; - SDL_Finger** fingers = SDL_GetTouchFingers(p_touchId, &count); - - if (fingers) { - for (int i = 0; i < count; i++) { - if (fingers[i]->y > 3.0 / 4.0) { - if (fingers[i]->x < 1.0 / 3.0) { - p_keyStates |= c_left; - } - else if (fingers[i]->x > 2.0 / 3.0) { - p_keyStates |= c_right; - } - else { - p_keyStates |= c_down; - } - } - else { - p_keyStates |= c_up; - } - } - - SDL_free(fingers); - } - }; - - for (int i = 0; i < count; i++) { - applyFingerNavigation(touchDevices[i]); + // We need to clear these as they are not meant to be persistent in e_gamepad mode. + if (m_touchOrigins.count(fingerID) && SDL_GetTicks() - m_touchLastMotion[fingerID] > 5) { + touchFlags &= ~c_left; + touchFlags &= ~c_right; } - - SDL_free(touchDevices); } return SUCCESS; } + +MxBool LegoInputManager::HandleTouchEvent(SDL_Event* p_event, TouchScheme p_touchScheme) +{ + const SDL_TouchFingerEvent& event = p_event->tfinger; + + switch (p_touchScheme) { + case e_mouse: + // Handled in LegoCameraController + return FALSE; + case e_arrowKeys: + switch (p_event->type) { + case SDL_EVENT_FINGER_UP: + m_touchFlags.erase(event.fingerID); + break; + case SDL_EVENT_FINGER_DOWN: + case SDL_EVENT_FINGER_MOTION: + m_touchFlags[event.fingerID] = 0; + + if (event.y > 3.0 / 4.0) { + if (event.x < 1.0 / 3.0) { + m_touchFlags[event.fingerID] |= c_left; + } + else if (event.x > 2.0 / 3.0) { + m_touchFlags[event.fingerID] |= c_right; + } + else { + m_touchFlags[event.fingerID] |= c_down; + } + } + else { + m_touchFlags[event.fingerID] |= c_up; + } + break; + } + break; + case e_gamepad: + switch (p_event->type) { + case SDL_EVENT_FINGER_DOWN: + m_touchOrigins[event.fingerID] = {event.x, event.y}; + break; + case SDL_EVENT_FINGER_UP: + m_touchOrigins.erase(event.fingerID); + m_touchFlags.erase(event.fingerID); + break; + case SDL_EVENT_FINGER_MOTION: + if (m_touchOrigins.count(event.fingerID)) { + m_touchFlags[event.fingerID] = 0; + m_touchLastMotion[event.fingerID] = SDL_GetTicks(); + + const float deltaY = event.y - m_touchOrigins[event.fingerID].y; + const float activationThreshold = 0.05f; + + if (SDL_fabsf(deltaY) > activationThreshold) { + if (deltaY > 0) { + m_touchFlags[event.fingerID] |= c_down; + } + else if (deltaY < 0) { + m_touchFlags[event.fingerID] |= c_up; + } + } + + const float deltaX = event.dx; + if (deltaX > 0) { + m_touchFlags[event.fingerID] |= c_right; + } + else if (deltaX < 0) { + m_touchFlags[event.fingerID] |= c_left; + } + } + break; + } + break; + } + + return TRUE; +}