More work on Prompt Widget

This commit is contained in:
2026-05-07 04:11:37 -04:00
parent b5e121f884
commit 236693fca6
7 changed files with 193 additions and 37 deletions

View File

@@ -84,3 +84,16 @@ DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.Defaul
-ConsoleKeys=Tilde -ConsoleKeys=Tilde
+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)

View File

@@ -134,3 +134,153 @@
}; };
typedef FUnixPlatformStackWalk FPlatformStackWalk; 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<FGamepadKeyNames::Type, float> 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);

View File

@@ -28,7 +28,8 @@
"files.watcherExclude": { "files.watcherExclude": {
"[UNREALENGINE]/Engine/**": true, "[UNREALENGINE]/Engine/**": true,
"[UNREALENGINE]/Samples/**": true, "[UNREALENGINE]/Samples/**": true,
"[UNREALENGINE]/Templates/**": true "[UNREALENGINE]/Templates/**": true,
"**/.*": true
}, },
"files.associations": { "files.associations": {
"**/include/**": "cpp", "**/include/**": "cpp",
@@ -130,7 +131,7 @@
"settings set target.inline-breakpoint-strategy always", "settings set target.inline-breakpoint-strategy always",
"settings set target.prefer-dynamic-value no-run-target", "settings set target.prefer-dynamic-value no-run-target",
"process handle SIGTRAP --notify false --pass false --stop false", "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()\""
] ]
} }
}, },

View File

@@ -1,8 +1,6 @@
#include "PromptWidget.h" #include "PromptWidget.h"
#include "UtilityLibrary.h" #include "UtilityLibrary.h"
#include "PlayerControllerBase.h" #include "PlayerControllerBase.h"
#include "Engine/Engine.h"
#include "GameFramework/InputDeviceSubsystem.h"
#include "InputAction.h" #include "InputAction.h"
#include "InputMappingContext.h" #include "InputMappingContext.h"
#include "Widgets/SOverlay.h" #include "Widgets/SOverlay.h"
@@ -22,6 +20,7 @@
// Row 7: Space Bar, unused, unused, unused // Row 7: Space Bar, unused, unused, unused
// Row 8: unused, unused, unused, unused. // Row 8: unused, unused, unused, unused.
int32 UlxPromptWidget::ChooseIcon(bool Playstation, FKey Key) const int32 UlxPromptWidget::ChooseIcon(bool Playstation, FKey Key) const
{ {
// Mouse buttons (row 1) // Mouse buttons (row 1)
@@ -68,15 +67,6 @@ void UlxPromptWidget::ChooseAppearance(int32 &OutIcon, FString &OutGlyph)
else OutGlyph = TEXT(""); 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) FBox2f UlxPromptWidget::GetIconUVs(int32 IconIndex)
{ {
@@ -202,15 +192,23 @@ void UlxPromptWidget::SetKeysFromBindings(const UInputMappingContext* InputMappi
SetKeys(NewGamepadKey, NewKeyboardKey); SetKeys(NewGamepadKey, NewKeyboardKey);
} }
bool UlxPromptWidget::OnTick(float DeltaTime)
{
ElxControllerType Type = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer());
if (ControllerType != Type)
{
ControllerType = Type;
SynchronizeProperties();
}
return true;
}
TSharedRef<SWidget> UlxPromptWidget::RebuildWidget() TSharedRef<SWidget> UlxPromptWidget::RebuildWidget()
{ {
if (!IsDesignTime()) if (!IsDesignTime())
{ {
ControllerType = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer()); ControllerType = UlxUtilityLibrary::DetectControllerType(GetOwningLocalPlayer());
if (UInputDeviceSubsystem* IDS = GEngine->GetEngineSubsystem<UInputDeviceSubsystem>()) TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &UlxPromptWidget::OnTick));
{
IDS->OnInputHardwareDeviceChanged.AddDynamic(this, &UlxPromptWidget::OnHardwareDeviceChanged);
}
} }
MyBrush.SetResourceObject(ButtonAtlas.Get()); MyBrush.SetResourceObject(ButtonAtlas.Get());
@@ -240,10 +238,7 @@ void UlxPromptWidget::ReleaseSlateResources(bool bReleaseChildren)
Super::ReleaseSlateResources(bReleaseChildren); Super::ReleaseSlateResources(bReleaseChildren);
if (!IsDesignTime()) if (!IsDesignTime())
{ {
if (UInputDeviceSubsystem* IDS = GEngine->GetEngineSubsystem<UInputDeviceSubsystem>()) FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
{
IDS->OnInputHardwareDeviceChanged.RemoveDynamic(this, &UlxPromptWidget::OnHardwareDeviceChanged);
}
} }
MyBox.Reset(); MyBox.Reset();
MyOverlay.Reset(); MyOverlay.Reset();

View File

@@ -7,6 +7,7 @@
#include "Widgets/SOverlay.h" #include "Widgets/SOverlay.h"
#include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SScaleBox.h" #include "Widgets/Layout/SScaleBox.h"
#include "Containers/Ticker.h"
#include "PromptWidget.generated.h" #include "PromptWidget.generated.h"
class UInputAction; class UInputAction;
@@ -62,8 +63,6 @@ public:
UFUNCTION(BlueprintCallable, Category="Prompt") UFUNCTION(BlueprintCallable, Category="Prompt")
void SetDepressed(bool InDepressed); void SetDepressed(bool InDepressed);
UFUNCTION()
void OnHardwareDeviceChanged(const FPlatformUserId UserId, const FInputDeviceId DeviceId);
protected: protected:
virtual TSharedRef<SWidget> RebuildWidget() override; virtual TSharedRef<SWidget> RebuildWidget() override;
@@ -84,4 +83,7 @@ private:
FMargin GetScaledMargins() const; FMargin GetScaledMargins() const;
int32 ChooseIcon(bool Playstation, FKey Key) const; int32 ChooseIcon(bool Playstation, FKey Key) const;
void ChooseAppearance(int32 &OutIcon, FString &OutGlyph); void ChooseAppearance(int32 &OutIcon, FString &OutGlyph);
bool OnTick(float DeltaTime);
FTSTicker::FDelegateHandle TickHandle;
}; };

View File

@@ -283,17 +283,12 @@ ElxControllerType UlxUtilityLibrary::DetectControllerType(ULocalPlayer *Player)
UInputDeviceSubsystem *IDS = GEngine->GetEngineSubsystem<UInputDeviceSubsystem>(); UInputDeviceSubsystem *IDS = GEngine->GetEngineSubsystem<UInputDeviceSubsystem>();
if (!IDS) return ElxControllerType::KeyboardMouse; if (!IDS) return ElxControllerType::KeyboardMouse;
FHardwareDeviceIdentifier Device = IDS->GetMostRecentlyUsedHardwareDevice(Player->GetPlatformUserId()); FName Id = IDS->GetMostRecentlyUsedHardwareDevice(Player->GetPlatformUserId()).HardwareDeviceIdentifier;
if (Device.PrimaryDeviceType != EHardwareDevicePrimaryType::Gamepad)
{
return ElxControllerType::KeyboardMouse;
}
FString DeviceName = Device.HardwareDeviceIdentifier.ToString(); if (Id == TEXT("ps3") || Id == TEXT("ps4") || Id == TEXT("ps5")) return ElxControllerType::PlayStationGamepad;
if (DeviceName.Contains(TEXT("PS4")) || DeviceName.Contains(TEXT("PS5")) || DeviceName.Contains(TEXT("PlayStation"))) if (Id == TEXT("xbox360") || Id == TEXT("xboxone")) return ElxControllerType::XboxGamepad;
{
return ElxControllerType::PlayStationGamepad; // Unknown or unrecognized device — assume keyboard/mouse
} return ElxControllerType::KeyboardMouse;
return ElxControllerType::XboxGamepad;
} }

View File

@@ -40,9 +40,9 @@ function cube.lookmenu(add)
end end
function sphere.lookhotkeys(add) function sphere.lookhotkeys(add)
add("DPadU", "Sphere Hi", function () dprint("Doing Sphere Hi") end) add("FaceL", "Sphere Hi", function () dprint("Doing Sphere Hi") end)
add("DPadL", "Sphere Bye", function () dprint("Doing Sphere Bye") end) add("FaceM", "Sphere Bye", function () dprint("Doing Sphere Bye") end)
add("DPadR", "Sphere Yo", function () dprint("Doing Sphere Yo") end) add("FaceR", "Sphere Yo", function () dprint("Doing Sphere Yo") end)
end end