# 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//`) ### 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).