DVX_GUI/shell/README.md

180 lines
6.6 KiB
Markdown

# DVX Shell (dvxshell.lib)
The DVX Shell is a DXE3 module loaded by the DVX loader at startup.
It initializes the GUI subsystem, loads DXE3 application modules on
demand, runs the cooperative main loop, and provides crash recovery so
a faulting app does not bring down the entire system.
## Entry Point
The loader finds and calls `shellMain()` after all libs and widgets
are loaded. `shellMain()`:
1. Loads preferences from `CONFIG/DVX.INI`
2. Initializes the GUI via `dvxInit()` with configured video mode
3. Applies saved mouse, color, and wallpaper settings from INI
4. Shows a splash screen ("DVX - DOS Visual eXecutive / Loading...")
5. Initializes the cooperative task system (`tsInit()`)
6. Sets shell task (task 0) to `TS_PRIORITY_HIGH`
7. Gathers system information via the platform layer
8. Initializes the app slot table
9. Points the memory tracker at `currentAppId` for per-app attribution
10. Registers idle callback, Ctrl+Esc handler, and title change handler
11. Installs the crash handler (before loading apps, so init crashes are caught)
12. Loads the desktop app (default: `apps/progman/progman.app`)
13. Dismisses the splash screen
14. Enters the main loop
## Main Loop
Each iteration of the main loop does four things:
1. `dvxUpdate()` -- process input events, dispatch callbacks, composite
dirty rects, flush to LFB
2. `tsYield()` -- give CPU time to app tasks (if any are active)
3. `shellReapApps()` -- clean up any apps that terminated this frame
4. `shellDesktopUpdate()` -- notify desktop app if apps were reaped
An idle callback (`idleYield`) is also registered so that `dvxUpdate()`
yields to app tasks during quiet frames.
## App Lifecycle
### DXE App Contract
Every DXE app exports two symbols:
* `appDescriptor` (`AppDescriptorT`) -- metadata:
- `name` -- display name (max 64 chars)
- `hasMainLoop` -- true for main-loop apps, false for callback-only
- `multiInstance` -- true to allow multiple instances via temp copy
- `stackSize` -- `SHELL_STACK_DEFAULT` (32KB) or explicit byte count
- `priority` -- `TS_PRIORITY_NORMAL` or custom
* `appMain` (`int appMain(DxeAppContextT *)`) -- entry point
Optional export: `appShutdown` (`void appShutdown(void)`) -- called
during graceful shutdown.
### Callback-Only Apps (hasMainLoop = false)
`appMain()` is called directly in the shell's task 0. It creates
windows, registers callbacks, and returns immediately. The app lives
entirely through GUI callbacks. The shell reaps callback-only apps
automatically when their last window closes -- `shellReapApps()` checks
each frame for running callback apps with zero remaining windows.
### Main-Loop Apps (hasMainLoop = true)
A dedicated cooperative task is created. `appMain()` runs in that task
and can do its own polling/processing loop, calling `tsYield()` to
share CPU. Lifecycle ends when `appMain()` returns or the task is
killed.
### App States
```
Free -> Loaded -> Running -> Terminating -> Free
```
| State | Description |
|-------|-------------|
| `AppStateFreeE` | Slot available for reuse |
| `AppStateLoadedE` | DXE loaded, not yet started (transient) |
| `AppStateRunningE` | Entry point called, active |
| `AppStateTerminatingE` | Shutdown in progress, awaiting reap |
### App Slots
App slots are managed as a stb_ds dynamic array (no fixed max). Each
slot tracks: app ID, name, path, DXE handle, state, task ID, entry/
shutdown function pointers, and a pointer to the `DxeAppContextT`
passed to the app.
`DxeAppContextT` is heap-allocated (via `calloc`) so its address is
stable across `sApps` array reallocs -- apps save this pointer in their
static globals and it must not move. The shell frees it during reap.
The `DxeAppContextT` gives each app:
- `shellCtx` -- pointer to the shell's `AppContextT`
- `appId` -- this app's unique ID
- `appDir` -- directory containing the `.app` file (for resources)
- `configDir` -- writable config directory (`CONFIG/<apppath>/`)
### App ID Tracking
`ctx->currentAppId` on AppContextT tracks which app is currently
executing. The shell sets this before calling app code.
`dvxCreateWindow()` stamps `win->appId` directly so the shell can
associate windows with apps for cleanup.
For main-loop apps, `appTaskWrapper` receives the app ID (as an int
cast to `void *`), not a direct pointer to `ShellAppT`. This is because
the `sApps` dynamic array may reallocate between `tsCreate` and the
first time the task runs, which would invalidate a direct pointer.
The shell calls `dvxSetBusy()` before `dlopen` to show the hourglass
cursor during app loading, and clears it after `appMain` returns (for
callback apps) or after task creation (for main-loop apps).
## Crash Recovery
The platform layer installs signal handlers for SIGSEGV, SIGFPE, and
SIGILL via `platformInstallCrashHandler()`. If a crash occurs:
1. Platform handler logs signal name and register dump (DJGPP)
2. Handler `longjmp`s to the `setjmp` point in `shellMain()`
3. `tsRecoverToMain()` fixes the scheduler's bookkeeping
4. Shell logs app-specific info (name, path, task ID)
5. Crashed app is force-killed (`shellForceKillApp()`)
6. Error dialog is shown to the user
7. Desktop is notified to refresh
8. Main loop continues normally
This gives Windows 3.1-style fault tolerance -- one bad app does not
take down the whole system.
## Task Manager Integration
The Task Manager is a separate DXE (`taskmgr.lib` in `taskmgr/`), not
built into the shell. It registers itself at load time via a DXE
constructor that sets the `shellCtrlEscFn` function pointer. The shell
calls this pointer on Ctrl+Esc. If `taskmgr.lib` is not loaded,
`shellCtrlEscFn` is NULL and Ctrl+Esc does nothing.
See `taskmgr/README.md` for full Task Manager documentation.
## Desktop Update Notifications
Apps (especially the desktop app) register callbacks via
`shellRegisterDesktopUpdate()` to be notified when app state changes
(load, reap, crash, title change). Multiple callbacks are supported.
## Files
| File | Description |
|------|-------------|
| `shellMain.c` | Entry point, main loop, crash recovery, splash screen, idle callback |
| `shellApp.h` | App lifecycle types: `AppDescriptorT`, `DxeAppContextT`, `ShellAppT`, `AppStateE`; `shellCtrlEscFn` extern |
| `shellApp.c` | App loading, reaping, task creation, DXE management, per-app memory tracking |
| `shellInfo.h` | System information wrapper |
| `shellInfo.c` | Gathers and caches hardware info via platform layer |
| `Makefile` | Builds `bin/libs/dvxshell.lib` + config/themes/wallpapers |
## Build
```
make # builds dvxshell.lib + dvxshell.dep + config files
make clean # removes objects, library, and config output
```
Depends on: `libtasks.lib`, `libdvx.lib`, `texthelp.lib`, `listhelp.lib`
(via dvxshell.dep).