DVX_GUI/dvxshell
2026-03-19 01:12:15 -05:00
..
Makefile Start of preferences system. 2026-03-19 01:12:15 -05:00
README.md More doc fixes. 2026-03-18 02:08:26 -05:00
shellApp.c Start of preferences system. 2026-03-19 01:12:15 -05:00
shellApp.h Task Manager display fixed. ListView can now auto-size columns to fit content. 2026-03-18 22:19:43 -05:00
shellExport.c Start of preferences system. 2026-03-19 01:12:15 -05:00
shellInfo.c Now reports system information for troubleshooting. 2026-03-18 17:26:49 -05:00
shellInfo.h Now reports system information for troubleshooting. 2026-03-18 17:26:49 -05:00
shellMain.c Start of preferences system. 2026-03-19 01:12:15 -05:00

DVX Shell

Windows 3.x-style desktop shell for DOS. Loads applications as DXE3 shared libraries and includes crash recovery so one bad app doesn't take down the system.

Building

make        # builds ../bin/dvx.exe
make clean  # removes objects and binary

Requires lib/libdvx.a and lib/libtasks.a to be built first.

Files

File Purpose
shellMain.c Entry point, main loop, crash recovery, logging
shellApp.c App loading (dlopen), lifecycle, reaping, resource tracking
shellApp.h ShellAppT, AppDescriptorT, AppStateE, DxeAppContextT, shell API
shellExport.c DXE export table and wrapper functions
Makefile Build rules, links -ldvx -ltasks -ldxe -lm

Shell Main Loop

Each iteration of the main loop:

  1. dvxUpdate() — process input events, dispatch callbacks, composite dirty rects
  2. tsYield() — give CPU time to main-loop app tasks
  3. shellReapApps() — clean up apps that terminated this frame
  4. desktopUpdate() — notify the desktop app if anything changed

An idle callback (idleYield) yields to app tasks during quiet periods when there are no events or dirty rects to process.

DXE App Contract

Every .app file must export these symbols:

// Required: app metadata
AppDescriptorT appDescriptor = {
    .name          = "My App",
    .hasMainLoop   = false,
    .multiInstance = false,
    .stackSize     = SHELL_STACK_DEFAULT,
    .priority      = TS_PRIORITY_NORMAL
};

// Required: entry point
int32_t appMain(DxeAppContextT *ctx);

// Optional: graceful shutdown hook
void appShutdown(void);

AppDescriptorT Fields

Field Type Description
name char[64] Display name shown in Task Manager
hasMainLoop bool true = gets its own cooperative task; false = callback-only
multiInstance bool true = allow multiple instances via temp file copy
stackSize int32_t Task stack size (SHELL_STACK_DEFAULT for 8 KB default)
priority int32_t Task priority (TS_PRIORITY_LOW/NORMAL/HIGH)

DxeAppContextT

Passed to appMain():

Field Type Description
shellCtx AppContextT * The shell's GUI context for creating windows, drawing, etc.
appId int32_t This app's unique ID (1-based slot index)
appDir char[260] Directory containing the .app file for relative resource paths

App Types

Callback-only (hasMainLoop = false):

  • appMain called in shell's task 0, creates windows, registers callbacks, returns 0
  • App lives through event callbacks dispatched by dvxUpdate()
  • Lifecycle ends when the last window is closed

Main-loop (hasMainLoop = true):

  • Shell creates a cooperative task via tsCreate()
  • appMain runs in that task with its own loop calling tsYield()
  • Lifecycle ends when appMain returns

Multi-Instance Support

DXE3's dlopen is reference-counted per path: loading the same .app twice returns the same handle, sharing all global/static state. For apps that support multiple instances (multiInstance = true), the shell copies the .app to a temp file before loading, giving each instance independent code and data. The temp file is cleaned up when the app terminates.

Apps that don't support multiple instances (multiInstance = false, the default) are blocked from loading a second time with an error message.

Temp file paths use the TEMP or TMP environment variable if set, falling back to the current directory.

Resource Tracking

The shell tracks which app owns which windows via sCurrentAppId, a global set before calling any app code. The shell's dvxCreateWindow wrapper stamps win->appId with the current app ID. On termination, the shell destroys all windows belonging to the app.

Crash Recovery

Signal handlers for SIGSEGV, SIGFPE, and SIGILL longjmp back to the shell's main loop. The scheduler is fixed via tsRecoverToMain(), the crashed app is force-killed, and a diagnostic message is displayed. Register state and app identity are logged to dvx.log.

DXE Export Table

The shell registers a symbol export table via dlregsym() before loading any apps. Most symbols (all dvx*, wgt*, ts*, drawing functions, and required libc functions) are exported directly. dvxCreateWindow and dvxDestroyWindow are exported as wrappers that add resource tracking.

Shell API

Function Description
shellAppInit() Initialize the app slot table
shellLoadApp(ctx, path) Load and start an app from a .app file
shellReapApps(ctx) Clean up terminated apps (call each frame)
shellReapApp(ctx, app) Gracefully shut down a single app
shellForceKillApp(ctx, app) Forcibly kill an app (skip shutdown hook)
shellTerminateAllApps(ctx) Kill all running apps (shell shutdown)
shellGetApp(appId) Get app slot by ID
shellRunningAppCount() Count running apps
shellLog(fmt, ...) Write to dvx.log
shellRegisterDesktopUpdate(fn) Register callback for app state changes
shellExportInit() Register DXE symbol export table