From 236693fca6d04ab47ca9351d3f7045fb1a8e54d7 Mon Sep 17 00:00:00 2001 From: jyelon Date: Thu, 7 May 2026 04:11:37 -0400 Subject: [PATCH] More work on Prompt Widget --- Config/DefaultInput.ini | 13 +++ EnginePatches/EnginePatch | 150 ++++++++++++++++++++++++++ Integration.code-workspace.tpl.json | 5 +- Source/Integration/PromptWidget.cpp | 33 +++--- Source/Integration/PromptWidget.h | 6 +- Source/Integration/UtilityLibrary.cpp | 17 ++- luprex/lua/login.lua | 6 +- 7 files changed, 193 insertions(+), 37 deletions(-) diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini index 1ff89cc1..102b3683 100644 --- a/Config/DefaultInput.ini +++ b/Config/DefaultInput.ini @@ -84,3 +84,16 @@ DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.Defaul -ConsoleKeys=Tilde +ConsoleKeys=Tilde +[InputPlatformSettings_Linux InputPlatformSettings] +MaxPlatformUserCount=8 +input.DeviceMappingPolicy=1 +MaxTriggerFeedbackPosition=8 +MaxTriggerFeedbackStrength=8 +MaxTriggerVibrationTriggerPosition=9 +MaxTriggerVibrationFrequency=255 +MaxTriggerVibrationAmplitude=8 ++HardwareDevices=(InputClassName="",HardwareDeviceIdentifier="",PrimaryDeviceType=Unspecified,SupportedFeaturesMask=0) ++HardwareDevices=(InputClassName="DefaultKeyboardAndMouse",HardwareDeviceIdentifier="KBM",PrimaryDeviceType=KeyboardAndMouse,SupportedFeaturesMask=3) ++HardwareDevices=(InputClassName="DefaultGamepad",HardwareDeviceIdentifier="Gamepad",PrimaryDeviceType=Gamepad,SupportedFeaturesMask=4) ++HardwareDevices=(InputClassName="DefaultMobileTouch",HardwareDeviceIdentifier="MobileTouch",PrimaryDeviceType=Touch,SupportedFeaturesMask=8) + diff --git a/EnginePatches/EnginePatch b/EnginePatches/EnginePatch index e508eeb2..e4f025c3 100644 --- a/EnginePatches/EnginePatch +++ b/EnginePatches/EnginePatch @@ -134,3 +134,153 @@ }; typedef FUnixPlatformStackWalk FPlatformStackWalk; +--- Engine/Source/Runtime/ApplicationCore/Public/Linux/LinuxApplication.h.orig 2026-05-05 21:15:20.644087179 -0400 ++++ Engine/Source/Runtime/ApplicationCore/Public/Linux/LinuxApplication.h 2026-05-05 21:15:38.654613091 -0400 +@@ -265,6 +265,12 @@ + /** The input device Id of the controller that can be used to find the matching ULocalPlayer */ + FInputDeviceId DeviceId; + ++ /** SDL gamepad type string (e.g. "ps4", "xboxone"), used as HardwareDeviceIdentifier in FInputDeviceScope */ ++ FName GamepadType; ++ ++ /** SDL gamepad name (human-readable device name), used as InputDeviceName in FInputDeviceScope */ ++ FName GamepadName; ++ + /** Store axis values from events here to be handled once per frame. */ + TMap AxisEvents; + +--- Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxApplication.cpp.orig 2026-05-05 21:15:20.646317814 -0400 ++++ Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxApplication.cpp 2026-05-05 23:13:59.528028910 -0400 +@@ -13,6 +13,13 @@ + #include "IHapticDevice.h" + #include "GenericPlatform/GenericPlatformInputDeviceMapper.h" + ++namespace UE::LinuxInput ++{ ++ static const FName InputClassName = TEXT("LinuxApplication"); ++ static const FString KBMInputHardwareName = TEXT("KBM"); ++ static const FString TouchInputHardwareName = TEXT("MobileTouch"); ++} ++ + // + // GameController thresholds + // +@@ -320,6 +327,7 @@ + { + case SDL_EVENT_KEY_DOWN: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + const SDL_KeyboardEvent &KeyEvent = Event.key; + SDL_Keycode KeySym = KeyEvent.key; + const uint32 CharCode = CharCodeFromSDLKeySym(KeySym); +@@ -342,6 +350,7 @@ + break; + case SDL_EVENT_KEY_UP: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + const SDL_KeyboardEvent &KeyEvent = Event.key; + const SDL_Keycode KeySym = KeyEvent.key; + const uint32 CharCode = CharCodeFromSDLKeySym(KeySym); +@@ -352,6 +361,7 @@ + break; + case SDL_EVENT_TEXT_INPUT: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + // Slate now gets all its text from here, I hope. + const bool bIsRepeated = false; //Event.key.repeat != 0; + const FString TextStr(UTF8_TO_TCHAR(Event.text.text)); +@@ -363,6 +373,7 @@ + break; + case SDL_EVENT_MOUSE_MOTION: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + SDL_MouseMotionEvent motionEvent = Event.motion; + FLinuxCursor *LinuxCursor = (FLinuxCursor*)Cursor.Get(); + LinuxCursor->InvalidateCaches(); +@@ -406,6 +417,7 @@ + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + SDL_MouseButtonEvent buttonEvent = Event.button; + + EMouseButtons::Type button; +@@ -484,6 +496,7 @@ + break; + case SDL_EVENT_MOUSE_WHEEL: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::KBMInputHardwareName); + SDL_MouseWheelEvent *WheelEvent = &Event.wheel; + float Amount = (float)WheelEvent->y * fMouseWheelScrollAccel; + +@@ -515,6 +528,7 @@ + + SDLControllerState &ControllerState = ControllerStates[caxisEvent.which]; + FPlatformUserId UserId = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(ControllerState.DeviceId); ++ FInputDeviceScope InputScope(nullptr, ControllerState.GamepadName, ControllerState.DeviceId.GetId(), ControllerState.GamepadType.ToString()); + + switch (caxisEvent.axis) + { +@@ -740,15 +754,17 @@ + + if (Button != FGamepadKeyNames::Invalid) + { +- FPlatformUserId UserId = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(ControllerStates[cbuttonEvent.which].DeviceId); ++ SDLControllerState& ButtonControllerState = ControllerStates[cbuttonEvent.which]; ++ FPlatformUserId UserId = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(ButtonControllerState.DeviceId); ++ FInputDeviceScope InputScope(nullptr, ButtonControllerState.GamepadName, ButtonControllerState.DeviceId.GetId(), ButtonControllerState.GamepadType.ToString()); + + if(cbuttonEvent.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) + { +- MessageHandler->OnControllerButtonPressed(Button, UserId, ControllerStates[cbuttonEvent.which].DeviceId, false); ++ MessageHandler->OnControllerButtonPressed(Button, UserId, ButtonControllerState.DeviceId, false); + } + else + { +- MessageHandler->OnControllerButtonReleased(Button, UserId, ControllerStates[cbuttonEvent.which].DeviceId, false); ++ MessageHandler->OnControllerButtonReleased(Button, UserId, ButtonControllerState.DeviceId, false); + } + } + } +@@ -1018,6 +1034,7 @@ + + case SDL_EVENT_FINGER_DOWN: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::TouchInputHardwareName); + UE_LOG(LogLinuxWindow, Verbose, TEXT("Finger %llu is down at (%f, %f)"), Event.tfinger.fingerID, Event.tfinger.x, Event.tfinger.y); + + // touch events can have no window associated with them, in that case ignore (with a warning) +@@ -1053,6 +1070,7 @@ + break; + case SDL_EVENT_FINGER_UP: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::TouchInputHardwareName); + UE_LOG(LogLinuxWindow, Verbose, TEXT("Finger %llu is up at (%f, %f)"), Event.tfinger.fingerID, Event.tfinger.x, Event.tfinger.y); + + // touch events can have no window associated with them, in that case ignore (with a warning) +@@ -1087,6 +1105,7 @@ + break; + case SDL_EVENT_FINGER_MOTION: + { ++ FInputDeviceScope InputScope(nullptr, UE::LinuxInput::InputClassName, IPlatformInputDeviceMapper::Get().GetDefaultInputDevice().GetId(), UE::LinuxInput::TouchInputHardwareName); + // touch events can have no window associated with them, in that case ignore (with a warning) + if (LIKELY(!bWindowlessEvent)) + { +@@ -1196,6 +1215,7 @@ + IPlatformInputDeviceMapper& Mapper = IPlatformInputDeviceMapper::Get(); + for(auto ControllerIt = ControllerStates.CreateIterator(); ControllerIt; ++ControllerIt) + { ++ FInputDeviceScope InputScope(nullptr, ControllerIt.Value().GamepadName, ControllerIt.Value().DeviceId.GetId(), ControllerIt.Value().GamepadType.ToString()); + for(auto Event = ControllerIt.Value().AxisEvents.CreateConstIterator(); Event; ++Event) + { + FPlatformUserId UserId = Mapper.GetUserForInputDevice(ControllerIt.Value().DeviceId); +@@ -2075,6 +2095,9 @@ + UE_LOG(LogLinux, Verbose, TEXT("Adding controller %i '%s'"), FirstUnusedIndex, UTF8_TO_TCHAR(SDL_GetGamepadName(Controller))); + auto& ControllerState = ControllerStates.Add(Id); + ControllerState.Controller = Controller; ++ ControllerState.GamepadName = FName(UTF8_TO_TCHAR(SDL_GetGamepadName(Controller))); ++ const char* GamepadTypeStr = SDL_GetGamepadStringForType(SDL_GetGamepadType(Controller)); ++ ControllerState.GamepadType = FName(GamepadTypeStr ? UTF8_TO_TCHAR(GamepadTypeStr) : TEXT("unknown")); + + FPlatformUserId UserId = FPlatformUserId::CreateFromInternalId(FirstUnusedIndex); + DeviceMapper.RemapControllerIdToPlatformUserAndDevice(FirstUnusedIndex, UserId, ControllerState.DeviceId); diff --git a/Integration.code-workspace.tpl.json b/Integration.code-workspace.tpl.json index 473218d4..049ced9f 100644 --- a/Integration.code-workspace.tpl.json +++ b/Integration.code-workspace.tpl.json @@ -28,7 +28,8 @@ "files.watcherExclude": { "[UNREALENGINE]/Engine/**": true, "[UNREALENGINE]/Samples/**": true, - "[UNREALENGINE]/Templates/**": true + "[UNREALENGINE]/Templates/**": true, + "**/.*": true }, "files.associations": { "**/include/**": "cpp", @@ -130,7 +131,7 @@ "settings set target.inline-breakpoint-strategy always", "settings set target.prefer-dynamic-value no-run-target", "process handle SIGTRAP --notify false --pass false --stop false", - "target stop-hook add --one-liner \"p ::UngrabAllInputImpl()\"" + "target stop-hook add --one-liner \"p FUnixPlatformMisc::UngrabAllInput()\"" ] } }, diff --git a/Source/Integration/PromptWidget.cpp b/Source/Integration/PromptWidget.cpp index 3922f707..2dfbd9e6 100644 --- a/Source/Integration/PromptWidget.cpp +++ b/Source/Integration/PromptWidget.cpp @@ -1,8 +1,6 @@ #include "PromptWidget.h" #include "UtilityLibrary.h" #include "PlayerControllerBase.h" -#include "Engine/Engine.h" -#include "GameFramework/InputDeviceSubsystem.h" #include "InputAction.h" #include "InputMappingContext.h" #include "Widgets/SOverlay.h" @@ -22,6 +20,7 @@ // Row 7: Space Bar, unused, unused, unused // Row 8: unused, unused, unused, unused. + int32 UlxPromptWidget::ChooseIcon(bool Playstation, FKey Key) const { // Mouse buttons (row 1) @@ -68,15 +67,6 @@ void UlxPromptWidget::ChooseAppearance(int32 &OutIcon, FString &OutGlyph) else OutGlyph = TEXT(""); } -void UlxPromptWidget::OnHardwareDeviceChanged(const FPlatformUserId UserId, const FInputDeviceId DeviceId) -{ - ElxControllerType Type = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); - if (ControllerType != Type) - { - ControllerType = Type; - SynchronizeProperties(); - } -} FBox2f UlxPromptWidget::GetIconUVs(int32 IconIndex) { @@ -202,15 +192,23 @@ void UlxPromptWidget::SetKeysFromBindings(const UInputMappingContext* InputMappi SetKeys(NewGamepadKey, NewKeyboardKey); } +bool UlxPromptWidget::OnTick(float DeltaTime) +{ + ElxControllerType Type = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); + if (ControllerType != Type) + { + ControllerType = Type; + SynchronizeProperties(); + } + return true; +} + TSharedRef UlxPromptWidget::RebuildWidget() { if (!IsDesignTime()) { ControllerType = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); - if (UInputDeviceSubsystem* IDS = GEngine->GetEngineSubsystem()) - { - IDS->OnInputHardwareDeviceChanged.AddDynamic(this, &UlxPromptWidget::OnHardwareDeviceChanged); - } + TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &UlxPromptWidget::OnTick)); } MyBrush.SetResourceObject(ButtonAtlas.Get()); @@ -240,10 +238,7 @@ void UlxPromptWidget::ReleaseSlateResources(bool bReleaseChildren) Super::ReleaseSlateResources(bReleaseChildren); if (!IsDesignTime()) { - if (UInputDeviceSubsystem* IDS = GEngine->GetEngineSubsystem()) - { - IDS->OnInputHardwareDeviceChanged.RemoveDynamic(this, &UlxPromptWidget::OnHardwareDeviceChanged); - } + FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); } MyBox.Reset(); MyOverlay.Reset(); diff --git a/Source/Integration/PromptWidget.h b/Source/Integration/PromptWidget.h index bf1c7aba..6806d964 100644 --- a/Source/Integration/PromptWidget.h +++ b/Source/Integration/PromptWidget.h @@ -7,6 +7,7 @@ #include "Widgets/SOverlay.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SScaleBox.h" +#include "Containers/Ticker.h" #include "PromptWidget.generated.h" class UInputAction; @@ -62,8 +63,6 @@ public: UFUNCTION(BlueprintCallable, Category="Prompt") void SetDepressed(bool InDepressed); - UFUNCTION() - void OnHardwareDeviceChanged(const FPlatformUserId UserId, const FInputDeviceId DeviceId); protected: virtual TSharedRef RebuildWidget() override; @@ -84,4 +83,7 @@ private: FMargin GetScaledMargins() const; int32 ChooseIcon(bool Playstation, FKey Key) const; void ChooseAppearance(int32 &OutIcon, FString &OutGlyph); + bool OnTick(float DeltaTime); + + FTSTicker::FDelegateHandle TickHandle; }; diff --git a/Source/Integration/UtilityLibrary.cpp b/Source/Integration/UtilityLibrary.cpp index 387fef74..1f5123a0 100644 --- a/Source/Integration/UtilityLibrary.cpp +++ b/Source/Integration/UtilityLibrary.cpp @@ -283,17 +283,12 @@ ElxControllerType UlxUtilityLibrary::DetectControllerType(ULocalPlayer *Player) UInputDeviceSubsystem *IDS = GEngine->GetEngineSubsystem(); if (!IDS) return ElxControllerType::KeyboardMouse; - FHardwareDeviceIdentifier Device = IDS->GetMostRecentlyUsedHardwareDevice(Player->GetPlatformUserId()); - if (Device.PrimaryDeviceType != EHardwareDevicePrimaryType::Gamepad) - { - return ElxControllerType::KeyboardMouse; - } + FName Id = IDS->GetMostRecentlyUsedHardwareDevice(Player->GetPlatformUserId()).HardwareDeviceIdentifier; - FString DeviceName = Device.HardwareDeviceIdentifier.ToString(); - if (DeviceName.Contains(TEXT("PS4")) || DeviceName.Contains(TEXT("PS5")) || DeviceName.Contains(TEXT("PlayStation"))) - { - return ElxControllerType::PlayStationGamepad; - } - return ElxControllerType::XboxGamepad; + if (Id == TEXT("ps3") || Id == TEXT("ps4") || Id == TEXT("ps5")) return ElxControllerType::PlayStationGamepad; + if (Id == TEXT("xbox360") || Id == TEXT("xboxone")) return ElxControllerType::XboxGamepad; + + // Unknown or unrecognized device — assume keyboard/mouse + return ElxControllerType::KeyboardMouse; } diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index f74f21e1..47dec86a 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -40,9 +40,9 @@ function cube.lookmenu(add) end function sphere.lookhotkeys(add) - add("DPadU", "Sphere Hi", function () dprint("Doing Sphere Hi") end) - add("DPadL", "Sphere Bye", function () dprint("Doing Sphere Bye") end) - add("DPadR", "Sphere Yo", function () dprint("Doing Sphere Yo") end) + add("FaceL", "Sphere Hi", function () dprint("Doing Sphere Hi") end) + add("FaceM", "Sphere Bye", function () dprint("Doing Sphere Bye") end) + add("FaceR", "Sphere Yo", function () dprint("Doing Sphere Yo") end) end