DVX_GUI/shell
2026-04-12 22:26:18 -05:00
..
dvxshell.dhs Fixed merge order of help files. 2026-04-09 19:03:01 -05:00
Makefile Reorganizing things preparing for next testing release. SDK added! 2026-04-09 01:27:40 -05:00
README.md Code and docs cleanup. 2026-03-26 21:15:20 -05:00
shellApp.c Reorganizing things preparing for next testing release. SDK added! 2026-04-09 01:27:40 -05:00
shellApp.h An insane number of performance, logic, and feature enhancements; bug fixess; and other things. 2026-04-12 22:26:18 -05:00
shellInf.h Reorganizing things preparing for next testing release. SDK added! 2026-04-09 01:27:40 -05:00
shellInfo.c Reorganizing things preparing for next testing release. SDK added! 2026-04-09 01:27:40 -05:00
shellMain.c An insane number of performance, logic, and feature enhancements; bug fixess; and other things. 2026-04-12 22:26:18 -05:00

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 longjmps 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).