Terminal Apps
RatatuiTerminalApps is a Runtime framework for terminal apps that boot before the first scene, live in DontDestroyOnLoad GameObjects, and expose a static open/close API from anywhere in your project.
Each app is a RatatuiTerminalApp subclass with its own RatatuiRenderer window. The Developer Console, Notepad, and Profiler samples are built-in apps using this system.
Architecture
flowchart TD
SubsystemReg["SubsystemRegistration\nRatatuiTerminalApps.ResetStatics()"]
AfterAssemblies["AfterAssembliesLoaded\nEach app: Register<T>()"]
Bootstrap["BeforeSceneLoad\nRatatuiTerminalApps.Bootstrap()"]
SubsystemReg --> AfterAssemblies --> Bootstrap
Bootstrap -->|"sort + per app"| GO["GameObject + DontDestroyOnLoad"]
GO --> App["RatatuiTerminalApp subclass"]
API["RatatuiTerminalApps.Open(id)"] --> App
| Type | Role |
|---|---|
RatatuiTerminalAppAttribute |
Marker attribute: identifies a class as a terminal app (documentation only) |
RatatuiTerminalApp |
Abstract base: open/close/toggle, toggle key, 4-finger touch toggle, render guards |
TerminalAppHandle |
Registry entry: metadata + live Instance + factory delegate |
RatatuiTerminalApps |
Static manager: bootstrap, app list, Register<T>, Open / Close / Toggle / Get |
No reflection — each app explicitly registers itself via Register<T>() in a [RuntimeInitializeOnLoadMethod(AfterAssembliesLoaded)] static method.
Writing an App
1. Subclass RatatuiTerminalApp and self-register
using UnityEngine;
namespace MyGame
{
[RatatuiTerminalApp("debug", DisplayName = "Debug Panel", Order = 10)]
public sealed class DebugPanelApp : RatatuiTerminalApp
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
private static void RegisterApp()
{
RatatuiTerminalApps.Register<DebugPanelApp>("debug", "Debug Panel", order: 10);
}
protected override KeyCode ToggleKey => KeyCode.F1;
protected override void Awake()
{
Cols = 80;
Rows = 24;
OnGuiDisplayMode = OnGuiMode.Window;
EnableInput = true;
base.Awake();
}
protected override void BuildFrame(RatatuiTerminal term)
{
if (!IsOpen) return;
term.Block(term.RootArea, " DEBUG ", Borders.All);
term.DrawText(term.Inner(term.RootArea), "Hello from a terminal app.");
}
protected override void OnOpened()
{
// App-specific setup when the window opens
}
protected override void OnClosed()
{
// App-specific cleanup when the window closes
}
}
}
RegisterApp() runs at AfterAssembliesLoaded — before RatatuiTerminalApps.Bootstrap() instantiates all registered apps at BeforeSceneLoad. Do not add the component to a scene manually.
2. Open / close from game code
using RatatuiUnity;
RatatuiTerminalApps.Open("debug");
RatatuiTerminalApps.Close("debug");
RatatuiTerminalApps.Toggle("debug");
bool open = RatatuiTerminalApps.IsOpen("debug");
var app = RatatuiTerminalApps.Get<DebugPanelApp>();
RatatuiTerminalApps.Open<DebugPanelApp>();
foreach (var handle in RatatuiTerminalApps.Apps)
Debug.Log($"{handle.Id}: {handle.DisplayName} (open={handle.IsOpen})");
3. Register<T> parameters
| Parameter | Required | Purpose |
|---|---|---|
id |
yes | Unique string used by Open(id) / Close(id) |
displayName |
no | Human label (defaults to id); also used as the GameObject name |
order |
no | Sort order in RatatuiTerminalApps.Apps (lower first) |
Duplicate id values are skipped with a warning.
[RatatuiTerminalApp] is a marker attribute — it does not trigger registration. Registration is always explicit via Register<T>().
Base Class Behaviour
RatatuiTerminalApp provides shared lifecycle:
IsOpen/SetOpen(bool)/Toggle()— visibility;SetOpen(true)always callsRequestFocus()— including when the app is already open — soRatatuiTerminalApps.Open(id)brings keyboard focus and window z-order to the front.ToggleKey— override to bind a keyboard toggle (defaultKeyCode.None).TouchToggleFingerCount— default 4; simultaneous touch count that toggles on full release.Update()— always runs toggle handling; render pipeline runs only when open.OnOpened()/OnClosed()— override hooks for app-specific state.ShouldSuppressToggleKeyEvent(e)— call fromOnTerminalKeyDownto drop toggle-key leakage.ShouldIgnoreMouseThisFrame()— call fromOnTerminalMouseEventafter opening (area map stale for one frame).
Developer Console Integration
The Developer Console sample registers as:
[RatatuiTerminalApp(Id = "console", DisplayName = "Developer Console", Order = 0)]
public sealed class RatatuiConsoleRenderer : RatatuiTerminalApp
RatatuiConsole remains the public facade for logs, commands, and history. Open/close delegates to the framework:
RatatuiConsole.Open(); // → RatatuiTerminalApps.Open("console")
RatatuiConsole.Toggle(); // → RatatuiTerminalApps.Toggle("console")
Console services bootstrap separately in RatatuiConsole.EnsureServicesBooted(); the renderer calls this in Awake so services exist regardless of bootstrap order.
At boot, the console registers open_<id> / close_<id> built-in commands for every entry in RatatuiTerminalApps.Apps (e.g. open_console, close_console). RatatuiConsole.TerminalApps exposes the same list from game code.
Caveats
Input System
Terminal apps use UnityEngine.Input (legacy). If Player Settings → Active Input Handling = "Input System Package (New)", RatatuiTerminalApps logs a warning at boot and does not instantiate apps. Set "Both" or "Input Manager (Old)".
Sample assembly loading
Apps in sample assemblies (e.g. Console with autoReferenced: false) are discovered only when that assembly is compiled into the project (import the sample via Package Manager).
Eager instantiation
All discovered apps are created at boot. Closed apps still run Update() (for toggle keys and background work like log draining) but skip rendering.
Notepad Integration
The Notepad sample registers as:
[RatatuiTerminalApp("notepad", DisplayName = "Notepad", Order = 10)]
public sealed class RatatuiNotepadRenderer : RatatuiTerminalApp
RatatuiNotepad is the public facade. Notes persist under Application.persistentDataPath/ratatui-notepad/. Toggle with F9 (configurable via RatatuiNotepadConfig).
RatatuiNotepad.Open(); // → RatatuiTerminalApps.Open("notepad")
RatatuiNotepad.Toggle(); // → RatatuiTerminalApps.Toggle("notepad")