DVX_GUI/shell/dvxshell.dhs

360 lines
13 KiB
Text

.section Libraries
.topic shell.overview
.title DVX Shell Library
.toc 0 DVX Shell Library
.default
.index DVX Shell
.index shellApp.h
.index DXE Application Loading
.h1 DVX Shell Library
The DVX shell library manages the lifecycle of DXE applications: loading, launching, tracking, and reaping. It provides the bridge between the DVX GUI compositor and dynamically loaded DXE3 application modules.
Header: shell/shellApp.h
.h2 App Model
The shell supports two kinds of DXE apps:
.list
.item Callback-only (hasMainLoop = false) -- appMain() runs in the shell's task 0, creates windows, registers event callbacks, and returns immediately. The app lives through GUI callbacks. Lifecycle ends when the last window is closed. Simpler and cheaper (no extra stack/task).
.item Main-loop (hasMainLoop = true) -- A dedicated cooperative task is created. appMain() runs in that task and can do its own polling loop, calling tsYield() to share CPU. Lifecycle ends when appMain() returns or the task is killed. Needed for terminal emulators, games, or long computations.
.endlist
Both types use the same DXE interface: an exported appDescriptor and appMain function.
.h2 DXE Interface
Every .app DXE module must export these symbols (COFF convention uses leading underscore):
.table
Symbol Type Required
------ ---- --------
_appDescriptor AppDescriptorT Yes
_appMain int32_t (*)(DxeAppContextT *) Yes
_appShutdown void (*)(void) No
.endtable
.h2 State Machine
App slots progress through four states:
.code
Free -> Loaded -> Running -> Terminating -> Free
.endcode
LoadedE is transient (only during shellLoadApp before the entry point is called). TerminatingE means the app's task has exited but cleanup has not yet occurred. The shell's main loop reaps terminating apps each frame via shellReapApps().
.h2 Contents
.link shell.types Types and Constants
.link shell.lifecycle Lifecycle API
.link shell.query Query API
.link shell.config Configuration API
.link shell.desktop Desktop Callbacks
.link shell.info System Information
.topic shell.types
.title Shell Types and Constants
.toc 1 Types and Constants
.index AppDescriptorT
.index DxeAppContextT
.index ShellAppT
.index AppStateE
.index SHELL_APP_NAME_MAX
.index SHELL_STACK_DEFAULT
.index SHELL_DESKTOP_APP
.h1 Types and Constants
.h2 Constants
.table
Constant Value Description
-------- ----- -----------
SHELL_APP_NAME_MAX 64 Maximum length of an app name string.
SHELL_STACK_DEFAULT 0 Use in AppDescriptorT.stackSize to get the default task stack size.
SHELL_DESKTOP_APP "apps/kpunch/progman/progman.app" Default desktop application path.
.endtable
.h2 AppDescriptorT
Exported by every DXE app as a global named appDescriptor. The shell reads it at load time to determine how to launch the app.
.table
Field Type Description
----- ---- -----------
name[SHELL_APP_NAME_MAX] char[] Display name of the application.
hasMainLoop bool false = callback-only, true = dedicated task.
multiInstance bool true = allow multiple simultaneous instances via temp copy.
stackSize int32_t SHELL_STACK_DEFAULT or explicit byte count for the task stack.
priority int32_t TS_PRIORITY_* value or custom priority for the task.
.endtable
.h2 DxeAppContextT
Passed as the sole argument to appMain(). Gives the app access to the shell's GUI context and its own identity.
.table
Field Type Description
----- ---- -----------
shellCtx AppContextT * The shell's GUI context (for creating windows, drawing, etc.).
appId int32_t This app's unique ID (slot index, 1-based).
appPath[DVX_MAX_PATH] char[] Full path to the .app DXE file.
appDir[DVX_MAX_PATH] char[] Directory containing the .app file (for loading resources).
configDir[DVX_MAX_PATH] char[] Writable config directory (e.g. CONFIG/APPS/KPUNCH/DVXBASIC/).
args[1024] char[] Launch arguments (empty string if none).
helpFile[DVX_MAX_PATH] char[] Help file path (for F1 context help).
helpTopic[128] char[] Current help topic ID (updated by the app at runtime).
.endtable
The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS.
.h2 AppStateE
.table
Value Description
----- -----------
AppStateFreeE Slot is available for reuse.
AppStateLoadedE DXE loaded, entry point not yet called (transient).
AppStateRunningE Entry point called, app is active.
AppStateTerminatingE Shutdown in progress, awaiting reap.
.endtable
.h2 ShellAppT
Per-app slot in the shell's app table. Slot 0 is reserved for the shell itself; apps use slots 1 and above.
.table
Field Type Description
----- ---- -----------
appId int32_t Unique ID (slot index, 1-based; 0 = shell).
name[SHELL_APP_NAME_MAX] char[] Display name from AppDescriptorT.
path[DVX_MAX_PATH] char[] Original DXE file path.
tempPath[DVX_MAX_PATH] char[] Temp copy path for multi-instance (empty if not a copy).
dxeHandle void * dlopen() handle for the DXE module.
state AppStateE Current lifecycle state.
hasMainLoop bool Whether this app has a dedicated task.
mainTaskId uint32_t Task ID if hasMainLoop, else 0.
entryFn int32_t (*)(DxeAppContextT *) Pointer to appMain.
shutdownFn void (*)(void) Pointer to appShutdown (may be NULL).
dxeCtx DxeAppContextT * Heap-allocated context (address stable across realloc).
.endtable
.topic shell.lifecycle
.title App Lifecycle API
.toc 1 Lifecycle API
.index shellAppInit
.index shellLoadApp
.index shellLoadAppWithArgs
.index shellReapApps
.index shellReapApp
.index shellForceKillApp
.index shellTerminateAllApps
.h1 App Lifecycle API
.h2 shellAppInit
.code
void shellAppInit(void);
.endcode
Initialize the app slot table. Seeds slot 0 (reserved for the shell). Must be called once at startup before any other shell API function.
.h2 shellLoadApp
.code
int32_t shellLoadApp(AppContextT *ctx, const char *path);
.endcode
Load and start an app from a DXE file. Returns the app ID (>= 1) on success, or -1 on error.
For callback-only apps, appMain() runs synchronously and returns before shellLoadApp returns. For main-loop apps, a cooperative task is created and the app begins running on the next tsYield().
If multiInstance is false in the app's descriptor and the same DXE is already loaded, the call fails with an error dialog. If multiInstance is true, the DXE is copied to a temp file so dlopen gets an independent code and data image.
.h2 shellLoadAppWithArgs
.code
int32_t shellLoadAppWithArgs(AppContextT *ctx, const char *path, const char *args);
.endcode
Load and run an app with arguments. The args string is copied into DxeAppContextT.args before appMain() is called. Otherwise identical to shellLoadApp().
.h2 shellReapApps
.code
bool shellReapApps(AppContextT *ctx);
.endcode
Scan for and reap finished apps. Call once per frame from the main loop.
Returns true if any apps were reaped, so the caller can trigger a desktop refresh. For main-loop apps, termination is detected by the AppStateTerminatingE state (set when appMain returns). For callback-only apps, termination is detected when no windows remain for that app.
.h2 shellReapApp
.code
void shellReapApp(AppContextT *ctx, ShellAppT *app);
.endcode
Gracefully shut down a single app. Calls the app's shutdownFn (if present), destroys all windows belonging to the app, kills its task (if any), closes the DXE handle, and frees the context. The slot returns to AppStateFreeE.
.h2 shellForceKillApp
.code
void shellForceKillApp(AppContextT *ctx, ShellAppT *app);
.endcode
Forcibly kill an app without calling its shutdown hook. Used by the Task Manager "End Task" or when an app has crashed and cannot be trusted to run cleanup code.
Cleanup order: windows first (removes from compositor), then task (frees stack), then DXE handle (unmaps code). Closing the DXE before destroying windows would cause callbacks into unmapped code.
.h2 shellTerminateAllApps
.code
void shellTerminateAllApps(AppContextT *ctx);
.endcode
Force-kill all running apps. Called during shell shutdown. Iterates all slots and calls shellForceKillApp() on each active app.
.topic shell.query
.title Query API
.toc 1 Query API
.index shellGetApp
.index shellAppSlotCount
.index shellRunningAppCount
.h1 Query API
.h2 shellGetApp
.code
ShellAppT *shellGetApp(int32_t appId);
.endcode
Look up an app slot by ID. Returns a pointer to the ShellAppT, or NULL if the ID is out of range or the slot is free. Valid app IDs are 1 through shellAppSlotCount() - 1.
.h2 shellAppSlotCount
.code
int32_t shellAppSlotCount(void);
.endcode
Return the total number of app slots (including slot 0). Use as the iteration bound when scanning all slots. Slot 0 is the shell itself; app slots start at 1.
.h2 shellRunningAppCount
.code
int32_t shellRunningAppCount(void);
.endcode
Count running apps, not counting the shell itself. Includes apps in both AppStateLoadedE and AppStateRunningE states.
.topic shell.config
.title Configuration API
.toc 1 Configuration API
.index shellEnsureConfigDir
.index shellConfigPath
.h1 Configuration API
Each app has a per-app configuration directory derived from its DXE path, mirrored under CONFIG/. For example, an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets the config directory CONFIG/APPS/KPUNCH/DVXBASIC/.
.h2 shellEnsureConfigDir
.code
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);
.endcode
Ensure the app's config directory exists, creating all parent directories as needed. Returns 0 on success, -1 on failure.
Call this before writing any config files. The directory path comes from ctx->configDir.
.h2 shellConfigPath
.code
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
.endcode
Build a full path to a file in the app's config directory by joining ctx->configDir with the given filename.
.code
// Example:
char path[DVX_MAX_PATH];
shellConfigPath(ctx, "settings.ini", path, sizeof(path));
// -> "CONFIG/APPS/KPUNCH/PROGMAN/settings.ini"
.endcode
.topic shell.desktop
.title Desktop Callbacks
.toc 1 Desktop Callbacks
.index shellRegisterDesktopUpdate
.index shellUnregisterDesktopUpdate
.index shellDesktopUpdate
.index shellCtrlEscFn
.h1 Desktop Callbacks
The shell provides a notification mechanism for app state changes (load, reap, crash). Desktop managers register a callback to refresh their display when apps come and go.
.h2 shellRegisterDesktopUpdate
.code
void shellRegisterDesktopUpdate(void (*updateFn)(void));
.endcode
Register a callback for app state change notifications. Multiple callbacks are supported. Apps typically call this during appMain() to receive notifications.
.h2 shellUnregisterDesktopUpdate
.code
void shellUnregisterDesktopUpdate(void (*updateFn)(void));
.endcode
Remove a previously registered callback. Call this before app shutdown to avoid dangling function pointers.
.h2 shellDesktopUpdate
.code
void shellDesktopUpdate(void);
.endcode
Notify all registered desktop callbacks that app state has changed. Called internally by the shell after loading or reaping an app. Can also be called by apps that need to trigger a desktop refresh.
.h2 shellCtrlEscFn
.code
extern void (*shellCtrlEscFn)(AppContextT *ctx);
.endcode
Function pointer set by the taskmgr DXE's constructor. The shell calls this when Ctrl+Esc is pressed. NULL if the task manager is not loaded.
.topic shell.info
.title System Information
.toc 1 System Information
.index shellInfoInit
.index shellGetSystemInfo
.h1 System Information
Header: shell/shellInfo.h
Thin wrapper around the platform layer's hardware detection. Gathers system information at startup, logs it, and caches the result for display in dialogs.
.h2 shellInfoInit
.code
void shellInfoInit(AppContextT *ctx);
.endcode
Gather all hardware information via the platform layer, log each line to DVX.LOG, and store the result for later retrieval. Call once after dvxInit().
.h2 shellGetSystemInfo
.code
const char *shellGetSystemInfo(void);
.endcode
Return the formatted system information text. The returned pointer is valid for the lifetime of the process (static buffer in the platform layer).