Skip to content

Commit d3b8517

Browse files
committed
Initial plugin commit
1 parent fe952d7 commit d3b8517

16 files changed

+458
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Binaries/
2+
Intermediate/

OnDeck.uplugin

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"FileVersion": 3,
3+
"Version": 1,
4+
"VersionName": "1.0",
5+
"FriendlyName": "OnDeck",
6+
"Description": "Provides detection for the presence of a Steam Deck and applies relevant device profiles.",
7+
"Category": "Other",
8+
"CreatedBy": "Stephen Swires",
9+
"CreatedByURL": "https://swires.me",
10+
"DocsURL": "",
11+
"MarketplaceURL": "",
12+
"CanContainContent": true,
13+
"IsBetaVersion": false,
14+
"IsExperimentalVersion": false,
15+
"Installed": false,
16+
"Modules": [
17+
{
18+
"Name": "OnDeck",
19+
"Type": "Runtime",
20+
"LoadingPhase": "PreDefault"
21+
}
22+
]
23+
}

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
1-
# UE-OnDeck
1+
# OnDeck
22
An Unreal Engine plugin that provides detection utilites for the Steam Deck and Big Picture mode.
3+
4+
**Note:** This is WIP and untested.
5+
6+
## Features
7+
* Provides C++ and Blueprint API to query Big Picture status and whether the game in running on a Steam Deck.
8+
* Support the on-screen keyboard if running in Big Picture mode.
9+
* Optionally apply a device profile if the Steam Deck is detected.

Resources/Icon128.png

12.4 KB
Loading

Source/OnDeck/OnDeck.Build.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
using UnrealBuildTool;
4+
5+
public class OnDeck : ModuleRules
6+
{
7+
public bool IsPlatformSupported => Target.Platform == UnrealTargetPlatform.Linux || Target.Platform == UnrealTargetPlatform.Win64;
8+
public string IsPlatformSupportedString => IsPlatformSupported ? "1" : "0";
9+
10+
public OnDeck(ReadOnlyTargetRules Target) : base(Target)
11+
{
12+
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
13+
14+
PublicDependencyModuleNames.AddRange(new string[]
15+
{
16+
"Core",
17+
});
18+
19+
PrivateDependencyModuleNames.AddRange(new string[]
20+
{
21+
"CoreUObject",
22+
"Engine",
23+
"DeveloperSettings",
24+
});
25+
26+
PublicDefinitions.Add($"ONDECK_STEAM_API_AVAILABLE={IsPlatformSupportedString}");
27+
28+
if (IsPlatformSupported)
29+
{
30+
PrivateDependencyModuleNames.AddRange(new string[]
31+
{
32+
"Slate",
33+
"SteamShared",
34+
});
35+
36+
AddEngineThirdPartyPrivateStaticDependencies(Target, "Steamworks");
37+
}
38+
}
39+
}

Source/OnDeck/Private/OnDeck.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#include "OnDeck.h"
4+
#include "SteamSharedModule.h"
5+
#include "Steam/OnDeckSettings.h"
6+
#include "OnDeckLogChannels.h"
7+
#include "DeviceProfiles/DeviceProfileManager.h"
8+
9+
#if ONDECK_STEAM_API_AVAILABLE
10+
#include "steam/steam_api.h"
11+
#include "steam/isteamutils.h"
12+
#include "Slate/SteamBigPictureTextField.h"
13+
#endif
14+
15+
#if !UE_BUILD_SHIPPING
16+
namespace OnDeckConsoleVariables
17+
{
18+
TAutoConsoleVariable CvarFakeRunningOnSteamDeck(
19+
TEXT("OnDeck.FakeRunningOnSteamDeck"),
20+
false,
21+
TEXT("Fake running on Steam Deck for testing purposes."),
22+
ECVF_Default
23+
);
24+
25+
TAutoConsoleVariable CvarFakeRunningInSteamBigPicture(
26+
TEXT("OnDeck.FakeRunningInSteamBigPicture"),
27+
false,
28+
TEXT("Fake running in Steam Big Picture mode for testing purposes."),
29+
ECVF_Default
30+
);
31+
}
32+
#endif
33+
34+
void FOnDeckModule::StartupModule()
35+
{
36+
#if ONDECK_STEAM_API_AVAILABLE
37+
// Get Steam API through SteamShared module
38+
if (!FSteamSharedModule::IsAvailable() || !FSteamSharedModule::Get().AreSteamDllsLoaded() || !SteamUtils())
39+
{
40+
// Steam API isn't available
41+
return;
42+
}
43+
44+
// cache values of Steam Deck and Big Picture mode status
45+
bIsRunningOnSteamDeck = SteamUtils()->IsSteamRunningOnSteamDeck();
46+
bIsRunningInSteamBigPicture = SteamUtils()->IsSteamInBigPictureMode();
47+
48+
const UOnDeckSettings* OnDeckSettings = GetDefault<UOnDeckSettings>();
49+
50+
// check to see if we should enable the Steam Deck device profile
51+
if (OnDeckSettings->bEnableDeviceProfile)
52+
{
53+
// we haven opted into the device profile functionality, check if it should be enabled
54+
CheckDeviceProfile(OnDeckSettings->DeviceProfileName);
55+
}
56+
57+
// set the custom virtual keyboard handler if enabled
58+
if (OnDeckSettings->bEnableVirtualKeyboard && bIsRunningInSteamBigPicture)
59+
{
60+
FSlateApplication::Get().OverridePlatformTextField(MakeUnique<FSteamBigPictureTextField>());
61+
UE_LOGFMT(LogOnDeck, Log, "Overriding virtual keyboard for Steam Big Picture mode.");
62+
}
63+
#endif
64+
}
65+
66+
void FOnDeckModule::ShutdownModule()
67+
{
68+
}
69+
70+
bool FOnDeckModule::IsRunningOnSteamDeck() const
71+
{
72+
#if !UE_BUILD_SHIPPING
73+
if (OnDeckConsoleVariables::CvarFakeRunningOnSteamDeck.GetValueOnGameThread())
74+
{
75+
return true;
76+
}
77+
#endif
78+
79+
return bIsRunningOnSteamDeck;
80+
}
81+
82+
bool FOnDeckModule::IsRunningInSteamBigPicture() const
83+
{
84+
#if !UE_BUILD_SHIPPING
85+
if (OnDeckConsoleVariables::CvarFakeRunningInSteamBigPicture.GetValueOnGameThread())
86+
{
87+
return true;
88+
}
89+
90+
if (IsRunningOnSteamDeck())
91+
{
92+
// Steam Deck is always in Big Picture mode so if we're "emulating" then we should return true here as well
93+
return true;
94+
}
95+
#endif
96+
97+
return bIsRunningInSteamBigPicture;
98+
}
99+
100+
void FOnDeckModule::CheckDeviceProfile(const FString& InDeviceProfileName)
101+
{
102+
if (!bIsRunningOnSteamDeck)
103+
{
104+
// not running on a Steam Deck, ignore
105+
UE_LOGFMT(LogOnDeck, Verbose, "Not running on Steam Deck, skipping device profile.");
106+
return;
107+
}
108+
109+
const FString DeviceProfileName = InDeviceProfileName.IsEmpty() ? TEXT("SteamDeck") : InDeviceProfileName;
110+
UDeviceProfileManager& DeviceProfileManager = UDeviceProfileManager::Get();
111+
UDeviceProfile* SteamDeckProfile = DeviceProfileManager.FindProfile(DeviceProfileName);
112+
113+
if (!SteamDeckProfile)
114+
{
115+
// Steam Deck profile already exists, no need to create it
116+
UE_LOGFMT(LogOnDeck, Error, "Steam Deck device profile \"{0}\" does not exist.", DeviceProfileName);
117+
return;
118+
}
119+
120+
DeviceProfileManager.SetOverrideDeviceProfile(SteamDeckProfile);
121+
UE_LOGFMT(LogOnDeck, Log, "Steam Deck device profile \"{0}\" applied.", DeviceProfileName);
122+
}
123+
124+
IMPLEMENT_MODULE(FOnDeckModule, OnDeck)

Source/OnDeck/Private/OnDeck.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include "CoreMinimal.h"
6+
#include "Modules/ModuleManager.h"
7+
#include "IOnDeckModule.h"
8+
9+
class FOnDeckModule : public IOnDeckModule
10+
{
11+
public:
12+
13+
/** IModuleInterface implementation */
14+
virtual void StartupModule() override;
15+
virtual void ShutdownModule() override;
16+
17+
/** IOnDeckModuleInterface implementation */
18+
virtual bool IsRunningOnSteamDeck() const override;
19+
virtual bool IsRunningInSteamBigPicture() const override;
20+
21+
private:
22+
23+
void CheckDeviceProfile(const FString& InDeviceProfileName);
24+
25+
bool bIsRunningOnSteamDeck = false;
26+
bool bIsRunningInSteamBigPicture = false;
27+
28+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#include "OnDeckLogChannels.h"
4+
5+
DEFINE_LOG_CATEGORY(LogOnDeck);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include "Logging/LogMacros.h"
6+
#include "Logging/StructuredLog.h"
7+
8+
DECLARE_LOG_CATEGORY_EXTERN(LogOnDeck, Log, All);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#include "SteamBigPictureTextField.h"
4+
#include "OnDeckLogChannels.h"
5+
6+
#if ONDECK_STEAM_API_AVAILABLE
7+
#include "steam/isteamutils.h"
8+
#endif
9+
10+
FSteamBigPictureTextField::FSteamBigPictureTextField()
11+
#if ONDECK_STEAM_API_AVAILABLE
12+
: GamepadTextInputDismissed(this, &FSteamBigPictureTextField::OnGamepadTextInputDismissed)
13+
#endif
14+
{
15+
}
16+
17+
void FSteamBigPictureTextField::ShowVirtualKeyboard(bool bShow, int32 UserIndex, TSharedPtr<IVirtualKeyboardEntry> TextEntryWidget)
18+
{
19+
#if ONDECK_STEAM_API_AVAILABLE
20+
const bool bMultiLineEntry = TextEntryWidget->IsMultilineEntry();
21+
const bool bPasswordEntry = TextEntryWidget->GetVirtualKeyboardType() == Keyboard_Password;
22+
23+
const auto HintTextChar = StringCast<UTF8CHAR>(*TextEntryWidget->GetHintText().ToString());
24+
const auto PlaceholderTextChar = StringCast<UTF8CHAR>(*TextEntryWidget->GetText().ToString());
25+
26+
const EGamepadTextInputMode TextInputMode = bPasswordEntry ?
27+
EGamepadTextInputMode::k_EGamepadTextInputModePassword :
28+
EGamepadTextInputMode::k_EGamepadTextInputModeNormal;
29+
30+
const EGamepadTextInputLineMode LineInputMode = bMultiLineEntry ?
31+
EGamepadTextInputLineMode::k_EGamepadTextInputLineModeSingleLine :
32+
EGamepadTextInputLineMode::k_EGamepadTextInputLineModeMultipleLines;
33+
34+
if (SteamUtils()->ShowGamepadTextInput(TextInputMode,
35+
LineInputMode,
36+
reinterpret_cast<const char*>(HintTextChar.Get()),
37+
0,
38+
reinterpret_cast<const char*>(PlaceholderTextChar.Get())
39+
))
40+
{
41+
UE_LOGFMT(LogOnDeck, Verbose, "Showing virtual keyboard.");
42+
43+
// virtual keyboard shown, so store the widget that requested it
44+
CurrentTextEntryWidget = TextEntryWidget;
45+
}
46+
else
47+
{
48+
UE_LOGFMT(LogOnDeck, Warning, "Failed to show virtual keyboard. Overlay might be disabled.");
49+
}
50+
#endif
51+
}
52+
53+
bool FSteamBigPictureTextField::AllowMoveCursor()
54+
{
55+
return true;
56+
}
57+
58+
#if ONDECK_STEAM_API_AVAILABLE
59+
void FSteamBigPictureTextField::OnGamepadTextInputDismissed(GamepadTextInputDismissed_t* Param)
60+
{
61+
const bool bSubmitted = Param->m_bSubmitted;
62+
const uint32 TextLength = Param->m_unSubmittedText;
63+
64+
// delegate back to the game thread to handle setting text, this is going to be called from the online thread
65+
AsyncTask(ENamedThreads::GameThread, [this, bSubmitted, TextLength]()
66+
{
67+
if (CurrentTextEntryWidget.IsValid())
68+
{
69+
if (bSubmitted)
70+
{
71+
// get text entered from steam API
72+
const uint32 BufferLength = TextLength + 1;
73+
TArray<char, TInlineAllocator<64>> EnteredText;
74+
EnteredText.SetNumZeroed(BufferLength);
75+
76+
SteamUtils()->GetEnteredGamepadTextInput(EnteredText.GetData(), BufferLength);
77+
78+
// convert to FString/FText
79+
const FString TextString = UTF8_TO_TCHAR(EnteredText.GetData());
80+
const FText Text = FText::FromString(TextString);
81+
82+
// submit text to widget
83+
CurrentTextEntryWidget->SetTextFromVirtualKeyboard(Text, ETextEntryType::TextEntryAccepted);
84+
}
85+
else
86+
{
87+
CurrentTextEntryWidget->SetTextFromVirtualKeyboard(CurrentTextEntryWidget->GetText(), ETextEntryType::TextEntryCanceled);
88+
}
89+
90+
CurrentTextEntryWidget = nullptr;
91+
}
92+
});
93+
}
94+
#endif
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#pragma once
4+
5+
#include "Framework/Application/IPlatformTextField.h"
6+
7+
#if ONDECK_STEAM_API_AVAILABLE
8+
#include "steam/steam_api_common.h"
9+
#include "steam/isteamutils.h"
10+
#endif
11+
12+
class IVirtualKeyboardEntry;
13+
14+
class FSteamBigPictureTextField : public IPlatformTextField
15+
{
16+
public:
17+
FSteamBigPictureTextField();
18+
19+
virtual void ShowVirtualKeyboard(bool bShow, int32 UserIndex, TSharedPtr<IVirtualKeyboardEntry> TextEntryWidget);
20+
virtual bool AllowMoveCursor() override;
21+
22+
private:
23+
24+
#if ONDECK_STEAM_API_AVAILABLE
25+
STEAM_CALLBACK(FSteamBigPictureTextField, OnGamepadTextInputDismissed, GamepadTextInputDismissed_t, GamepadTextInputDismissed);
26+
#endif
27+
28+
TSharedPtr<IVirtualKeyboardEntry> CurrentTextEntryWidget;
29+
30+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#include "Steam/OnDeckFunctionLibrary.h"
4+
#include "IOnDeckModule.h"
5+
6+
bool UOnDeckFunctionLibrary::IsRunningOnSteamDeck()
7+
{
8+
return IOnDeckModule::Get().IsRunningOnSteamDeck();
9+
}
10+
11+
bool UOnDeckFunctionLibrary::IsRunningInSteamBigPicture()
12+
{
13+
return IOnDeckModule::Get().IsRunningInSteamBigPicture();
14+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Copyright Stephen Swires. All Rights Reserved.
2+
3+
#include "Steam/OnDeckSettings.h"

0 commit comments

Comments
 (0)