C++ Extension Examples
C++ recipes for adding new types and extending Operating System Simulator behavior. Pair with C++ Reference for modules/includes and the API Reference for full signatures.
All examples assume a game module with OperatingSystemSimulator in PrivateDependencyModuleNames. Add GlobalMessenger and GameplayTags when using messengers.
flowchart TB
subgraph add["Add new types"]
Prog["UOperatingSystemBaseProgram"]
Cmd["UOperatingSystemTerminalCommand"]
File["UOperatingSystemFile"]
Mod["UOperatingSystemSettingModule"]
end
subgraph extend["Extend runtime"]
OS["UOperatingSystem"]
Statics["UOperatingSystemStatics"]
GM["UGlobalMessageSubsystem"]
DM["Device Messenger"]
end
Prog --> OS
Cmd --> GM
File --> Prog
Statics --> OS
Prog --> DM
1. Add a native program
Section titled “1. Add a native program”Minimal program with validation. Assign widgets via a Blueprint child in the editor.
MyNativeProgram.h
#pragma once
#include "Programs/OperatingSystemBaseProgram.h"#include "MyNativeProgram.generated.h"
UCLASS(Blueprintable)class UMyNativeProgram : public UOperatingSystemBaseProgram{ GENERATED_BODY()
public: UMyNativeProgram();
protected: virtual void OnValidate(FGenericError& OutError) const override; virtual void OnStart(FGenericError& OutError) override; virtual void OnClose(FGenericError& OutError) override;};MyNativeProgram.cpp
#include "MyNativeProgram.h"
UMyNativeProgram::UMyNativeProgram(){ Name = FText::FromString(TEXT("Native Tool")); Identifier = FName(TEXT("com.mygame.nativetool")); Version.Major = 1; SaveMethod = EOperatingSystemProgramSaveMethod::SaveOnExit;}
void UMyNativeProgram::OnValidate(FGenericError& OutError) const{ Super::OnValidate(OutError); if (!OutError.IsValid() && WidgetClass.IsNull()) { OutError = MAKE_ERROR(TEXT("CONFIG"), TEXT("Widget Class is required.")); }}
void UMyNativeProgram::OnStart(FGenericError& OutError){ Super::OnStart(OutError); // First frame — load state, refresh UI via program widget}
void UMyNativeProgram::OnClose(FGenericError& OutError){ // Cleanup before Super if needed Super::OnClose(OutError);}→ Full walkthrough: C++ program from scratch
2. Add program settings and data classes
Section titled “2. Add program settings and data classes”For persistent prefs and runtime state (Mail/Banking pattern).
MyProgramSettings.h
#pragma once
#include "Programs/OperatingSystemProgramSettings.h"#include "MyProgramSettings.generated.h"
UCLASS()class UMyProgramSettings : public UOperatingSystemProgramSettings{ GENERATED_BODY()
public: UPROPERTY(SaveGame, BlueprintReadWrite, Category = "MyProgram") int32 LastSelectedTab = 0;
UPROPERTY(SaveGame, BlueprintReadWrite, Category = "MyProgram") FString LastOpenedPath;};MyProgramData.h
#pragma once
#include "Programs/OperatingSystemProgramData.h"#include "MyProgramData.generated.h"
UCLASS()class UMyProgramData : public UOperatingSystemProgramData{ GENERATED_BODY()
public: UPROPERTY(BlueprintReadWrite, Category = "MyProgram") TArray<FString> InboxIds;};Wire on program constructor:
UMyNativeProgram::UMyNativeProgram(){ SaveMethod = EOperatingSystemProgramSaveMethod::SaveOnExit; SettingsClass = UMyProgramSettings::StaticClass(); DataClass = UMyProgramData::StaticClass(); bRequiresData = true;}Read at runtime:
if (UMyProgramSettings* S = Cast<UMyProgramSettings>(GetProgramSettings())){ S->LastSelectedTab = 2; SaveSettings(); // when SaveMethod is SaveManually}3. Add a program widget (C++)
Section titled “3. Add a program widget (C++)”#pragma once
#include "Widgets/Common/OperatingSystemProgramWidget.h"#include "MyProgramWidget.generated.h"
class UMyNativeProgram;
UCLASS()class UMyProgramWidget : public UOperatingSystemProgramWidget{ GENERATED_BODY()
protected: virtual void NativeConstruct() override;
UPROPERTY(meta = (BindWidget)) TObjectPtr<class UTextBlock> StatusText;};#include "MyProgramWidget.h"#include "MyNativeProgram.h"
void UMyProgramWidget::NativeConstruct(){ Super::NativeConstruct(); if (UMyNativeProgram* Prog = Cast<UMyNativeProgram>(GetOwningProgram())) { if (StatusText) { StatusText->SetText(Prog->GetName()); } }}Set WidgetClass on the program (C++ defaults or Blueprint child).
4. Add a terminal command
Section titled “4. Add a terminal command”#pragma once
#include "Programs/Terminal/OperatingSystemTerminalCommand.h"#include "CMD_ToggleDoor.generated.h"
UCLASS()class UCMD_ToggleDoor : public UOperatingSystemTerminalCommand{ GENERATED_BODY()
public: UCMD_ToggleDoor();
protected: virtual void OnProcessCommand(const FString& Command, const bool bHasRootPermission) override;};#include "CMD_ToggleDoor.h"#include "Programs/Terminal/OperatingSystemTerminal.h"#include "GlobalMessageSubsystem.h"#include "GameplayTagContainer.h"
UCMD_ToggleDoor::UCMD_ToggleDoor(){ MainCommand = FName(TEXT("toggledoor"));}
void UCMD_ToggleDoor::OnProcessCommand(const FString& Command, const bool bHasRootPermission){ if (UOperatingSystemTerminal* Term = ParentTerminal.Get()) { if (const UWorld* World = Term->GetWorld()) { if (UGlobalMessageSubsystem* MS = UGlobalMessageSubsystem::Get(*World)) { static const FGameplayTag Tag = FGameplayTag::RequestGameplayTag(TEXT("Message.OS.Door.Toggle")); MS->BroadcastMessage(Tag, nullptr); } } Term->AppendOutput(TEXT("Door signal sent.")); } FinishCommand(FGenericError());}Register the class on your Terminal program or UOperatingSystemCmdCollection Data Asset.
5. Add a custom file type
Section titled “5. Add a custom file type”#pragma once
#include "Files/OperatingSystemFile.h"#include "MyDocumentFile.generated.h"
UCLASS()class UMyDocumentFile : public UOperatingSystemFile{ GENERATED_BODY()
public: UMyDocumentFile();
protected: virtual void OnOpen() override;};#include "MyDocumentFile.h"#include "Programs/OperatingSystemBaseProgram.h"
UMyDocumentFile::UMyDocumentFile(){ Extension = FText::FromString(TEXT(".mydoc")); bIsDeletable = true; bIsMovable = true; // TargetProgramClass = UMyDocumentProgram::StaticClass(); // or set on Blueprint child}
void UMyDocumentFile::OnOpen(){ Super::OnOpen(); // Optional logic before target program launches}Spawn at runtime (from program or game code):
#include "Core/OperatingSystem.h"#include "Core/OperatingSystemDirectory.h"#include "Global/OperatingSystemStatics.h"
void SpawnMyDoc(UOperatingSystem* OS){ FGenericError Error; if (UOperatingSystemDirectory* Desktop = UOperatingSystemStatics::GetDirectoryFromPath( OS, TEXT("C:\\Users\\Desktop"), Error)) { // Use OS static helpers or program methods to construct UMyDocumentFile instance }}6. Extend program — open file handler
Section titled “6. Extend program — open file handler”Override when your program owns a file extension:
void UMyDocumentProgram::OnOpenFile(UOperatingSystemFile* TargetFile, FGenericError& OutError){ Super::OnOpenFile(TargetFile, OutError); if (!OutError.IsValid() && TargetFile) { // Cast to UMyDocumentFile, load into UI }}7. Add a setting module
Section titled “7. Add a setting module”#pragma once
#include "Programs/Settings/OperatingSystemSettingModule.h"#include "MySettingModule.generated.h"
UCLASS()class UMySettingModule : public UOperatingSystemSettingModule{ GENERATED_BODY()
protected: virtual void OnApplySetting(const FName SettingIdentifier, UObject* Payload) override;};void UMySettingModule::OnApplySetting(const FName SettingIdentifier, UObject* Payload){ Super::OnApplySetting(SettingIdentifier, Payload); // Apply to ParentOS, broadcast via device messenger if needed}Register on BP_OS_Settings → Setting Modules array (duplicate BP_OS_Module_Theme as a template).
8. Device Messenger — broadcast from C++
Section titled “8. Device Messenger — broadcast from C++”#include "Global/OperatingSystemStatics.h"#include "Devices/OperatingSystemDeviceMessenger.h"
void UMyMailProgram::NotifyInboxUpdated(){ if (UOperatingSystem* OS = GetOperatingSystem()) { if (UOperatingSystemDeviceMessenger* Messenger = UOperatingSystemStatics::FindMessengerFromOperatingSystem(OS)) { Messenger->Broadcast(FName(TEXT("MailReceived")), this); } }}
**Listen in widget:**
```cppvoid UMyTaskbarWidget::NativeConstruct(){ Super::NativeConstruct(); if (UOperatingSystem* OS = /* from device or program */) { if (auto* Messenger = UOperatingSystemStatics::FindMessengerFromOperatingSystem(OS)) { FOperatingSystemDeviceMessageDelegate Delegate; Delegate.BindDynamic(this, &UMyTaskbarWidget::OnMailReceived); Messenger->BeginListening(FName(TEXT("MailReceived")), Delegate); } }}9. Global Messenger — level actor listener
Section titled “9. Global Messenger — level actor listener”Terminal command (see example 4) broadcasts; level actor listens:
#pragma once
#include "GameFramework/Actor.h"#include "DoorActor.generated.h"
UCLASS()class ADoorActor : public AActor{ GENERATED_BODY()
protected: virtual void BeginPlay() override;
UFUNCTION() void OnDoorMessage(UObject* Payload);
FGameplayTag DoorTag;};#include "DoorActor.h"#include "GlobalMessageSubsystem.h"
void ADoorActor::BeginPlay(){ Super::BeginPlay(); DoorTag = FGameplayTag::RequestGameplayTag(TEXT("Message.OS.Door.Toggle"));
if (UGlobalMessageSubsystem* MS = UGlobalMessageSubsystem::Get(*GetWorld())) { FGlobalMessageReceiveDelegate Delegate; Delegate.BindDynamic(this, &ADoorActor::OnDoorMessage); MS->ListenToMessage(this, DoorTag, Delegate); }}
void ADoorActor::OnDoorMessage(UObject* Payload){ // Toggle door mesh, collision, etc.}10. Filesystem — resolve path and errors
Section titled “10. Filesystem — resolve path and errors”#include "Global/OperatingSystemStatics.h"#include "Core/OperatingSystem.h"#include "Core/OperatingSystemDirectory.h"
bool GetDesktopDir(UOperatingSystem* OS, UOperatingSystemDirectory*& OutDir, FGenericError& OutError){ OutDir = UOperatingSystemStatics::GetDirectoryFromPath( OS, TEXT("C:\\Users\\Desktop"), OutError); return OutDir != nullptr && !OutError.IsValid();}Use UOperatingSystemUnix paths (/Users/Desktop) when the active OS is Unix-type.
11. Send notification from C++
Section titled “11. Send notification from C++”#include "Core/OperatingSystem.h"
void UMyProgram::NotifyComplete(){ if (UOperatingSystem* OS = GetOperatingSystem()) { OS->SendNotification( FText::FromString(TEXT("Sync complete")), FText::FromString(TEXT("All files uploaded.")), EOperatingSystemNotificationType::Info); }}12. Hook device actor boot
Section titled “12. Hook device actor boot”On your AOperatingSystemDeviceActor subclass:
void AMyDeviceActor::BeginPlay(){ Super::BeginPlay(); // Device starts boot via UOperatingSystemBaseDevice — hook delegates on device if exposed}Device state flow: None → Starting → BIOS → Running — see C++ Reference.
Quick reference — what to subclass
Section titled “Quick reference — what to subclass”| Goal | Class | Key overrides |
|---|---|---|
| New app | UOperatingSystemBaseProgram | OnValidate, OnStart, OnClose, OnOpenFile |
| Saved prefs | UOperatingSystemProgramSettings | SaveGame UPROPERTYs |
| Runtime state | UOperatingSystemProgramData | Program-owned data |
| Program UI | UOperatingSystemProgramWidget | NativeConstruct |
| CLI command | UOperatingSystemTerminalCommand | OnProcessCommand, FinishCommand |
| File extension | UOperatingSystemFile | OnOpen, Extension, TargetProgramClass |
| Settings panel | UOperatingSystemSettingModule | OnApplySetting |
| World events | UGlobalMessageSubsystem | BroadcastMessage, ListenToMessage |
| In-OS events | UOperatingSystemDeviceMessenger | Broadcast, BeginListening |