Code and docs cleanup.
This commit is contained in:
parent
be7473ff27
commit
bf610ba95b
9 changed files with 2372 additions and 1917 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ lib/
|
||||||
.gitignore~
|
.gitignore~
|
||||||
.gitattributes~
|
.gitattributes~
|
||||||
*.SWP
|
*.SWP
|
||||||
|
.claude/
|
||||||
|
|
|
||||||
35
README.md
35
README.md
|
|
@ -20,7 +20,8 @@ dvx.exe (loader)
|
||||||
+-- libs/libdvx.lib core GUI (draw, comp, wm, app, widget infra)
|
+-- libs/libdvx.lib core GUI (draw, comp, wm, app, widget infra)
|
||||||
+-- libs/texthelp.lib shared text editing helpers
|
+-- libs/texthelp.lib shared text editing helpers
|
||||||
+-- libs/listhelp.lib shared list/dropdown helpers
|
+-- libs/listhelp.lib shared list/dropdown helpers
|
||||||
+-- libs/dvxshell.lib shell (app lifecycle, desktop, task manager)
|
+-- libs/dvxshell.lib shell (app lifecycle, desktop)
|
||||||
|
+-- libs/taskmgr.lib task manager (Ctrl+Esc, separate DXE)
|
||||||
|
|
|
|
||||||
+-- widgets/*.wgt 26 widget type plugins
|
+-- widgets/*.wgt 26 widget type plugins
|
||||||
|
|
|
|
||||||
|
|
@ -37,9 +38,11 @@ dvx.exe (loader)
|
||||||
| `tasks/` | `bin/libs/libtasks.lib` | Cooperative task switching |
|
| `tasks/` | `bin/libs/libtasks.lib` | Cooperative task switching |
|
||||||
| `texthelp/` | `bin/libs/texthelp.lib` | Shared text editing helpers |
|
| `texthelp/` | `bin/libs/texthelp.lib` | Shared text editing helpers |
|
||||||
| `listhelp/` | `bin/libs/listhelp.lib` | Shared list/dropdown helpers |
|
| `listhelp/` | `bin/libs/listhelp.lib` | Shared list/dropdown helpers |
|
||||||
| `shell/` | `bin/libs/dvxshell.lib` | DVX Shell (app lifecycle, task manager) |
|
| `shell/` | `bin/libs/dvxshell.lib` | DVX Shell (app lifecycle, desktop) |
|
||||||
|
| `taskmgr/` | `bin/libs/taskmgr.lib` | Task Manager (separate DXE, Ctrl+Esc) |
|
||||||
| `widgets/` | `bin/widgets/*.wgt` | 26 individual widget DXE modules |
|
| `widgets/` | `bin/widgets/*.wgt` | 26 individual widget DXE modules |
|
||||||
| `apps/` | `bin/apps/*/*.app` | Application DXE modules |
|
| `apps/` | `bin/apps/*/*.app` | Application DXE modules |
|
||||||
|
| `tools/` | `bin/dvxres` | Resource tool (host native, not DXE) |
|
||||||
| `config/` | `bin/config/`, `bin/libs/`, `bin/widgets/` | INI config, themes, wallpapers, dep files |
|
| `config/` | `bin/config/`, `bin/libs/`, `bin/widgets/` | INI config, themes, wallpapers, dep files |
|
||||||
| `rs232/` | `lib/librs232.a` | ISR-driven UART serial driver |
|
| `rs232/` | `lib/librs232.a` | ISR-driven UART serial driver |
|
||||||
| `packet/` | `lib/libpacket.a` | HDLC framing, CRC-16, Go-Back-N ARQ |
|
| `packet/` | `lib/libpacket.a` | HDLC framing, CRC-16, Go-Back-N ARQ |
|
||||||
|
|
@ -62,7 +65,8 @@ make clean # remove all build artifacts
|
||||||
The top-level Makefile builds in dependency order:
|
The top-level Makefile builds in dependency order:
|
||||||
|
|
||||||
```
|
```
|
||||||
core -> tasks -> loader -> texthelp -> listhelp -> widgets -> shell -> apps
|
core -> tasks -> loader -> texthelp -> listhelp -> widgets -> shell -> taskmgr -> apps
|
||||||
|
tools (host native, parallel)
|
||||||
```
|
```
|
||||||
|
|
||||||
Build output goes to `bin/` (executables, DXE modules, config) and
|
Build output goes to `bin/` (executables, DXE modules, config) and
|
||||||
|
|
@ -80,6 +84,7 @@ bin/
|
||||||
texthelp.lib text editing helpers
|
texthelp.lib text editing helpers
|
||||||
listhelp.lib list/dropdown helpers
|
listhelp.lib list/dropdown helpers
|
||||||
dvxshell.lib DVX shell
|
dvxshell.lib DVX shell
|
||||||
|
taskmgr.lib task manager (Ctrl+Esc)
|
||||||
*.dep dependency files for load ordering
|
*.dep dependency files for load ordering
|
||||||
widgets/
|
widgets/
|
||||||
box.wgt VBox/HBox/Frame containers
|
box.wgt VBox/HBox/Frame containers
|
||||||
|
|
@ -117,9 +122,17 @@ dvxWidget.h.
|
||||||
|
|
||||||
* **Dynamic type IDs**: `wgtRegisterClass()` assigns IDs at load time
|
* **Dynamic type IDs**: `wgtRegisterClass()` assigns IDs at load time
|
||||||
* **void *data**: Each widget allocates its own private data struct
|
* **void *data**: Each widget allocates its own private data struct
|
||||||
|
* **ABI-stable dispatch**: `WidgetClassT.handlers[]` is an array of function
|
||||||
|
pointers indexed by `WGT_METHOD_*` constants (0-20, room for 32). Core
|
||||||
|
dispatches via `w->wclass->handlers[WGT_METHOD_PAINT]` etc., so adding
|
||||||
|
new methods does not break existing widget DXE binaries
|
||||||
|
* **Generic drag**: `WGT_METHOD_ON_DRAG_UPDATE` and `WGT_METHOD_ON_DRAG_END`
|
||||||
|
provide widget-level drag support without per-widget hacks in core
|
||||||
* **Per-widget API registry**: `wgtRegisterApi("name", &api)` replaces the monolithic API
|
* **Per-widget API registry**: `wgtRegisterApi("name", &api)` replaces the monolithic API
|
||||||
* **Per-widget headers**: `widgets/widgetButton.h` etc. provide typed macros
|
* **Per-widget headers**: `widgets/widgetButton.h` etc. provide typed macros
|
||||||
* **Shared helpers**: texthelp.lib (text editing) and listhelp.lib (dropdown/list)
|
* **Shared helpers**: texthelp.lib (text editing) and listhelp.lib (dropdown/list)
|
||||||
|
* **All limits dynamic**: widget child arrays, app slots, and desktop callbacks
|
||||||
|
are stb_ds dynamic arrays with no fixed maximums
|
||||||
|
|
||||||
|
|
||||||
## DXE Module System
|
## DXE Module System
|
||||||
|
|
@ -148,10 +161,12 @@ emulators, games).
|
||||||
|
|
||||||
## Crash Recovery
|
## Crash Recovery
|
||||||
|
|
||||||
The shell installs signal handlers for SIGSEGV, SIGFPE, and SIGILL. If
|
The shell installs signal handlers for SIGSEGV, SIGFPE, and SIGILL
|
||||||
an app crashes, the handler `longjmp`s back to the shell's main loop,
|
before loading any apps, so even crashes during app initialization are
|
||||||
the crashed app is force-killed, and the shell continues running.
|
caught. If an app crashes, the handler `longjmp`s back to the shell's
|
||||||
Diagnostic information (registers, faulting EIP) is logged to `dvx.log`.
|
main loop, the crashed app is force-killed, and the shell continues
|
||||||
|
running. Diagnostic information (registers, faulting EIP) is logged to
|
||||||
|
`dvx.log`.
|
||||||
|
|
||||||
|
|
||||||
## Bundled Applications
|
## Bundled Applications
|
||||||
|
|
@ -190,8 +205,10 @@ All third-party code is vendored as single-header libraries:
|
||||||
| stb_image_write.h | `core/thirdparty/` | PNG export for screenshots |
|
| stb_image_write.h | `core/thirdparty/` | PNG export for screenshots |
|
||||||
| stb_ds.h | `core/thirdparty/` | Dynamic arrays and hash maps |
|
| stb_ds.h | `core/thirdparty/` | Dynamic arrays and hash maps |
|
||||||
|
|
||||||
stb_ds implementation is compiled into dvx.exe (the loader) and
|
stb_ds implementation is compiled into dvx.exe (the loader) with
|
||||||
exported via `dlregsym` to all DXE modules.
|
`STBDS_REALLOC`/`STBDS_FREE` overridden to use `dvxRealloc`/`dvxFree`,
|
||||||
|
so all `arrput`/`arrfree` calls in DXE code are tracked per-app. The
|
||||||
|
functions are exported via `dlregsym` to all DXE modules.
|
||||||
|
|
||||||
|
|
||||||
## Target Hardware
|
## Target Hardware
|
||||||
|
|
|
||||||
251
core/README.md
251
core/README.md
|
|
@ -10,7 +10,7 @@ modules that register themselves at runtime via `wgtRegisterClass()`.
|
||||||
Core knows nothing about individual widget types. There is no
|
Core knows nothing about individual widget types. There is no
|
||||||
WidgetTypeE enum, no widget union, and no per-widget structs in
|
WidgetTypeE enum, no widget union, and no per-widget structs in
|
||||||
dvxWidget.h. All widget-specific behavior is dispatched through the
|
dvxWidget.h. All widget-specific behavior is dispatched through the
|
||||||
WidgetClassT vtable.
|
WidgetClassT dispatch table.
|
||||||
|
|
||||||
|
|
||||||
## 5-Layer Architecture
|
## 5-Layer Architecture
|
||||||
|
|
@ -29,6 +29,8 @@ Additional modules built into libdvx.lib:
|
||||||
|--------|--------|-------------|
|
|--------|--------|-------------|
|
||||||
| `dvxDialog.h` | `dvxDialog.c` | Modal message box and file open/save dialogs |
|
| `dvxDialog.h` | `dvxDialog.c` | Modal message box and file open/save dialogs |
|
||||||
| `dvxPrefs.h` | `dvxPrefs.c` | INI-based preferences (read/write with typed accessors) |
|
| `dvxPrefs.h` | `dvxPrefs.c` | INI-based preferences (read/write with typed accessors) |
|
||||||
|
| `dvxResource.h` | `dvxResource.c` | Resource system -- icons, text, and binary data appended to DXE files |
|
||||||
|
| `dvxMem.h` | (header only) | Per-app memory tracking API declarations |
|
||||||
| `dvxWidget.h` | `widgetClass.c`, `widgetCore.c`, `widgetEvent.c`, `widgetLayout.c`, `widgetOps.c`, `widgetScrollbar.c` | Widget infrastructure |
|
| `dvxWidget.h` | `widgetClass.c`, `widgetCore.c`, `widgetEvent.c`, `widgetLayout.c`, `widgetOps.c`, `widgetScrollbar.c` | Widget infrastructure |
|
||||||
| `dvxWidgetPlugin.h` | (header only) | Plugin API for widget DXE modules |
|
| `dvxWidgetPlugin.h` | (header only) | Plugin API for widget DXE modules |
|
||||||
| -- | `dvxImage.c` | Image loading via stb_image (BMP, PNG, JPEG, GIF) |
|
| -- | `dvxImage.c` | Image loading via stb_image (BMP, PNG, JPEG, GIF) |
|
||||||
|
|
@ -46,7 +48,8 @@ Additional modules built into libdvx.lib:
|
||||||
| `dvxApp.c` | `dvxInit()`, `dvxRun()`, `dvxUpdate()`, `dvxCreateWindow()`, color schemes, wallpaper, screenshots |
|
| `dvxApp.c` | `dvxInit()`, `dvxRun()`, `dvxUpdate()`, `dvxCreateWindow()`, color schemes, wallpaper, screenshots |
|
||||||
| `dvxDialog.c` | `dvxMessageBox()`, `dvxFileDialog()` -- modal dialogs with own event loops |
|
| `dvxDialog.c` | `dvxMessageBox()`, `dvxFileDialog()` -- modal dialogs with own event loops |
|
||||||
| `dvxPrefs.c` | `prefsLoad()`, `prefsSave()`, typed get/set for string/int/bool |
|
| `dvxPrefs.c` | `prefsLoad()`, `prefsSave()`, typed get/set for string/int/bool |
|
||||||
| `dvxImage.c` | `dvxLoadImage()` -- stb_image loader, converts to native pixel format |
|
| `dvxResource.c` | `dvxResOpen()`, `dvxResRead()`, `dvxResFind()`, `dvxResClose()` -- resource system |
|
||||||
|
| `dvxImage.c` | `dvxLoadImage()`, `dvxLoadImageFromMemory()` -- stb_image loader, converts to native pixel format |
|
||||||
| `dvxImageWrite.c` | `dvxSaveImage()` -- PNG writer for screenshots |
|
| `dvxImageWrite.c` | `dvxSaveImage()` -- PNG writer for screenshots |
|
||||||
| `widgetClass.c` | `wgtRegisterClass()`, `wgtRegisterApi()`, `wgtGetApi()`, class table |
|
| `widgetClass.c` | `wgtRegisterClass()`, `wgtRegisterApi()`, `wgtGetApi()`, class table |
|
||||||
| `widgetCore.c` | Widget allocation, tree ops, focus management, clipboard, hit testing, cursor blink |
|
| `widgetCore.c` | Widget allocation, tree ops, focus management, clipboard, hit testing, cursor blink |
|
||||||
|
|
@ -68,7 +71,9 @@ Additional modules built into libdvx.lib:
|
||||||
| `dvxApp.h` | Application API: `dvxInit()`, `dvxRun()`, `dvxUpdate()`, `dvxCreateWindow()`, color schemes, wallpaper, image I/O |
|
| `dvxApp.h` | Application API: `dvxInit()`, `dvxRun()`, `dvxUpdate()`, `dvxCreateWindow()`, color schemes, wallpaper, image I/O |
|
||||||
| `dvxDialog.h` | Modal dialogs: `dvxMessageBox()`, `dvxFileDialog()` |
|
| `dvxDialog.h` | Modal dialogs: `dvxMessageBox()`, `dvxFileDialog()` |
|
||||||
| `dvxPrefs.h` | INI preferences: `prefsLoad()`, `prefsSave()`, typed accessors |
|
| `dvxPrefs.h` | INI preferences: `prefsLoad()`, `prefsSave()`, typed accessors |
|
||||||
| `dvxWidget.h` | Widget system public API: WidgetT, WidgetClassT, size tags, layout, API registry |
|
| `dvxResource.h` | Resource system: `dvxResOpen()`, `dvxResRead()`, `dvxResFind()`, `dvxResClose()` |
|
||||||
|
| `dvxMem.h` | Per-app memory tracking: `dvxMalloc()`, `dvxFree()`, `dvxMemGetAppUsage()`, etc. |
|
||||||
|
| `dvxWidget.h` | Widget system public API: WidgetT, WidgetClassT, size tags, layout, API registry, `wclsFoo()` dispatch helpers |
|
||||||
| `dvxWidgetPlugin.h` | Plugin API for widget DXE authors: tree ops, focus, scrollbar helpers, shared state |
|
| `dvxWidgetPlugin.h` | Plugin API for widget DXE authors: tree ops, focus, scrollbar helpers, shared state |
|
||||||
| `dvxFont.h` | Embedded 8x14 and 8x16 bitmap font data (CP437) |
|
| `dvxFont.h` | Embedded 8x14 and 8x16 bitmap font data (CP437) |
|
||||||
| `dvxCursor.h` | Mouse cursor AND/XOR mask data (arrow, resize H/V/diag, busy) |
|
| `dvxCursor.h` | Mouse cursor AND/XOR mask data (arrow, resize H/V/diag, busy) |
|
||||||
|
|
@ -79,7 +84,7 @@ Additional modules built into libdvx.lib:
|
||||||
|
|
||||||
| File | Description |
|
| File | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `platform/dvxPlatform.h` | Platform abstraction API (video, input, spans, DXE, crash recovery) |
|
| `platform/dvxPlatform.h` | Platform abstraction API (video, input, spans, DXE, crash recovery, memory tracking) |
|
||||||
| `platform/dvxPlatformDos.c` | DJGPP/DPMI implementation (VESA VBE, INT 33h mouse, INT 16h keyboard, asm spans) |
|
| `platform/dvxPlatformDos.c` | DJGPP/DPMI implementation (VESA VBE, INT 33h mouse, INT 16h keyboard, asm spans) |
|
||||||
|
|
||||||
The platform layer is compiled into dvx.exe (the loader), not into
|
The platform layer is compiled into dvx.exe (the loader), not into
|
||||||
|
|
@ -96,6 +101,83 @@ libdvx.lib. Platform functions are exported to all DXE modules via
|
||||||
| `thirdparty/stb_ds.h` | Dynamic arrays/hash maps (implementation in loader, exported to all DXEs) |
|
| `thirdparty/stb_ds.h` | Dynamic arrays/hash maps (implementation in loader, exported to all DXEs) |
|
||||||
|
|
||||||
|
|
||||||
|
## Dynamic Limits
|
||||||
|
|
||||||
|
All major data structures grow dynamically via realloc. There are no
|
||||||
|
fixed-size limits for:
|
||||||
|
|
||||||
|
- **Windows** -- `WindowStackT.windows` is a dynamic array
|
||||||
|
- **Menus** -- `MenuBarT.menus` and `MenuT.items` are dynamic arrays
|
||||||
|
- **Accelerator entries** -- `AccelTableT.entries` is a dynamic array
|
||||||
|
- **Dirty rectangles** -- `DirtyListT.rects` is a dynamic array
|
||||||
|
- **Submenu depth** -- `PopupStateT.parentStack` is a dynamic array
|
||||||
|
|
||||||
|
The only fixed-size buffers remaining are per-element string fields
|
||||||
|
(`MAX_TITLE_LEN = 128`, `MAX_MENU_LABEL = 32`, `MAX_WIDGET_NAME = 32`)
|
||||||
|
and the system menu (`SYS_MENU_MAX_ITEMS = 10`).
|
||||||
|
|
||||||
|
|
||||||
|
## Resource System
|
||||||
|
|
||||||
|
Resources are appended to DXE3 files (.app, .wgt, .lib) after the
|
||||||
|
normal DXE content. The DXE loader never reads past the DXE header,
|
||||||
|
so appended data is invisible to dlopen.
|
||||||
|
|
||||||
|
File layout:
|
||||||
|
|
||||||
|
[DXE3 content]
|
||||||
|
[resource data entries] -- sequential, variable length
|
||||||
|
[resource directory] -- fixed-size entries (48 bytes each)
|
||||||
|
[footer] -- magic + directory offset + count (16 bytes)
|
||||||
|
|
||||||
|
### Resource Types
|
||||||
|
|
||||||
|
| Define | Value | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| `DVX_RES_ICON` | 1 | Image data (BMP icon: 16x16, 32x32, etc.) |
|
||||||
|
| `DVX_RES_TEXT` | 2 | Null-terminated string (author, copyright, etc.) |
|
||||||
|
| `DVX_RES_BINARY` | 3 | Arbitrary binary data (app-specific) |
|
||||||
|
|
||||||
|
### Resource API
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `dvxResOpen(path)` | Open a resource handle by reading the footer and directory. Returns NULL if no resources. |
|
||||||
|
| `dvxResRead(h, name, outSize)` | Find a resource by name and read its data into a malloc'd buffer. Caller frees. |
|
||||||
|
| `dvxResFind(h, name)` | Find a resource by name and return its directory entry pointer. |
|
||||||
|
| `dvxResClose(h)` | Close the handle and free associated memory. |
|
||||||
|
|
||||||
|
### Key Types
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `DvxResDirEntryT` | Directory entry: name[32], type, offset, size, reserved (48 bytes) |
|
||||||
|
| `DvxResFooterT` | Footer: magic (`0x52585644` = "DVXR"), dirOffset, entryCount, reserved (16 bytes) |
|
||||||
|
| `DvxResHandleT` | Runtime handle: path, entries array, entry count |
|
||||||
|
|
||||||
|
|
||||||
|
## Memory Tracking (dvxMem.h)
|
||||||
|
|
||||||
|
Per-app memory tracking wraps malloc/free/calloc/realloc/strdup with a
|
||||||
|
small header per allocation that records the owning app ID and size.
|
||||||
|
DXE code does not need to include dvxMem.h -- the DXE export table maps
|
||||||
|
the standard allocator names to these wrappers transparently.
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `dvxMalloc(size)` | Tracked malloc |
|
||||||
|
| `dvxCalloc(nmemb, size)` | Tracked calloc |
|
||||||
|
| `dvxRealloc(ptr, size)` | Tracked realloc |
|
||||||
|
| `dvxFree(ptr)` | Tracked free (falls through to real free on non-tracked pointers) |
|
||||||
|
| `dvxStrdup(s)` | Tracked strdup |
|
||||||
|
| `dvxMemSnapshotLoad(appId)` | Record baseline memory for leak detection |
|
||||||
|
| `dvxMemGetAppUsage(appId)` | Query current memory usage for an app (bytes) |
|
||||||
|
| `dvxMemResetApp(appId)` | Free all allocations charged to an app |
|
||||||
|
|
||||||
|
The global `dvxMemAppIdPtr` pointer is set by the shell to
|
||||||
|
`&ctx->currentAppId` so the allocator knows which app to charge.
|
||||||
|
|
||||||
|
|
||||||
## WidgetT Structure
|
## WidgetT Structure
|
||||||
|
|
||||||
The WidgetT struct is generic -- no widget-specific fields or union:
|
The WidgetT struct is generic -- no widget-specific fields or union:
|
||||||
|
|
@ -103,8 +185,8 @@ The WidgetT struct is generic -- no widget-specific fields or union:
|
||||||
```c
|
```c
|
||||||
typedef struct WidgetT {
|
typedef struct WidgetT {
|
||||||
int32_t type; // assigned by wgtRegisterClass()
|
int32_t type; // assigned by wgtRegisterClass()
|
||||||
const struct WidgetClassT *wclass; // vtable pointer
|
const struct WidgetClassT *wclass; // dispatch table pointer
|
||||||
char name[32];
|
char name[MAX_WIDGET_NAME];
|
||||||
|
|
||||||
// Tree linkage
|
// Tree linkage
|
||||||
struct WidgetT *parent, *firstChild, *lastChild, *nextSibling;
|
struct WidgetT *parent, *firstChild, *lastChild, *nextSibling;
|
||||||
|
|
@ -144,34 +226,84 @@ typedef struct WidgetT {
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## WidgetClassT Vtable
|
## WidgetClassT Dispatch Table
|
||||||
|
|
||||||
Each widget type defines a static WidgetClassT with flags and function pointers.
|
WidgetClassT is an ABI-stable dispatch table. Method IDs are fixed
|
||||||
The vtable has 26 function slots plus a flags field:
|
constants that never change -- adding new methods appends new IDs
|
||||||
|
without shifting existing ones. Widget DXEs compiled against an older
|
||||||
|
DVX version continue to work unmodified.
|
||||||
|
|
||||||
| Slot | Signature | Purpose |
|
```c
|
||||||
|------|-----------|---------|
|
#define WGT_CLASS_VERSION 1 // bump on breaking ABI change
|
||||||
| `flags` | `uint32_t` | Static properties (see Flags below) |
|
#define WGT_METHOD_MAX 32 // room for future methods
|
||||||
| `paint` | `(w, d, ops, font, colors)` | Render the widget |
|
|
||||||
| `paintOverlay` | `(w, d, ops, font, colors)` | Render overlay (dropdown popup) |
|
typedef struct WidgetClassT {
|
||||||
| `calcMinSize` | `(w, font)` | Compute minimum size (bottom-up pass) |
|
uint32_t version;
|
||||||
| `layout` | `(w, font)` | Position children (top-down pass) |
|
uint32_t flags;
|
||||||
| `getLayoutMetrics` | `(w, font, pad, gap, extraTop, borderW)` | Return padding/gap for box layout |
|
void *handlers[WGT_METHOD_MAX];
|
||||||
| `onMouse` | `(w, root, vx, vy)` | Handle mouse click |
|
} WidgetClassT;
|
||||||
| `onKey` | `(w, key, mod)` | Handle keyboard input |
|
```
|
||||||
| `onAccelActivate` | `(w, root)` | Handle accelerator key match |
|
|
||||||
| `destroy` | `(w)` | Free widget-private data |
|
### Method ID Table
|
||||||
| `onChildChanged` | `(parent, child)` | Notification when a child changes |
|
|
||||||
| `getText` | `(w)` | Return widget text |
|
21 methods are currently defined (IDs 0--20). WGT_METHOD_MAX is 32,
|
||||||
| `setText` | `(w, text)` | Set widget text |
|
leaving room for 11 future methods without a version bump.
|
||||||
| `clearSelection` | `(w)` | Clear text/item selection |
|
|
||||||
| `closePopup` | `(w)` | Close dropdown popup |
|
| ID | Method ID | Signature | Purpose |
|
||||||
| `getPopupRect` | `(w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle |
|
|----|-----------|-----------|---------|
|
||||||
| `onDragUpdate` | `(w, root, x, y)` | Mouse move during drag |
|
| 0 | `WGT_METHOD_PAINT` | `void (w, d, ops, font, colors)` | Render the widget |
|
||||||
| `onDragEnd` | `(w, root, x, y)` | Mouse release after drag |
|
| 1 | `WGT_METHOD_PAINT_OVERLAY` | `void (w, d, ops, font, colors)` | Render overlay (dropdown popup) |
|
||||||
| `getCursorShape` | `(w, vx, vy)` | Return cursor ID for this position |
|
| 2 | `WGT_METHOD_CALC_MIN_SIZE` | `void (w, font)` | Compute minimum size (bottom-up pass) |
|
||||||
| `poll` | `(w, win)` | Periodic polling (AnsiTerm comms) |
|
| 3 | `WGT_METHOD_LAYOUT` | `void (w, font)` | Position children (top-down pass) |
|
||||||
| `quickRepaint` | `(w, outY, outH)` | Fast incremental repaint |
|
| 4 | `WGT_METHOD_GET_LAYOUT_METRICS` | `void (w, font, pad, gap, extraTop, borderW)` | Return padding/gap for box layout |
|
||||||
|
| 5 | `WGT_METHOD_ON_MOUSE` | `void (w, root, vx, vy)` | Handle mouse click |
|
||||||
|
| 6 | `WGT_METHOD_ON_KEY` | `void (w, key, mod)` | Handle keyboard input |
|
||||||
|
| 7 | `WGT_METHOD_ON_ACCEL_ACTIVATE` | `void (w, root)` | Handle accelerator key match |
|
||||||
|
| 8 | `WGT_METHOD_DESTROY` | `void (w)` | Free widget-private data |
|
||||||
|
| 9 | `WGT_METHOD_ON_CHILD_CHANGED` | `void (parent, child)` | Notification when a child changes |
|
||||||
|
| 10 | `WGT_METHOD_GET_TEXT` | `const char *(w)` | Return widget text |
|
||||||
|
| 11 | `WGT_METHOD_SET_TEXT` | `void (w, text)` | Set widget text |
|
||||||
|
| 12 | `WGT_METHOD_CLEAR_SELECTION` | `bool (w)` | Clear text/item selection |
|
||||||
|
| 13 | `WGT_METHOD_CLOSE_POPUP` | `void (w)` | Close dropdown popup |
|
||||||
|
| 14 | `WGT_METHOD_GET_POPUP_RECT` | `void (w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle |
|
||||||
|
| 15 | `WGT_METHOD_ON_DRAG_UPDATE` | `void (w, root, x, y)` | Mouse move during drag |
|
||||||
|
| 16 | `WGT_METHOD_ON_DRAG_END` | `void (w, root, x, y)` | Mouse release after drag |
|
||||||
|
| 17 | `WGT_METHOD_GET_CURSOR_SHAPE` | `int32_t (w, vx, vy)` | Return cursor ID for this position |
|
||||||
|
| 18 | `WGT_METHOD_POLL` | `void (w, win)` | Periodic polling (AnsiTerm comms) |
|
||||||
|
| 19 | `WGT_METHOD_QUICK_REPAINT` | `int32_t (w, outY, outH)` | Fast incremental repaint |
|
||||||
|
| 20 | `WGT_METHOD_SCROLL_CHILD_INTO_VIEW` | `void (parent, child)` | Scroll to make a child visible |
|
||||||
|
|
||||||
|
### Typed Dispatch Helpers
|
||||||
|
|
||||||
|
Each `wclsFoo()` inline function extracts a handler by stable method
|
||||||
|
ID, casts it to the correct function pointer type, and calls it with
|
||||||
|
a NULL check. This gives callers type-safe dispatch with the same
|
||||||
|
codegen as a direct struct field call.
|
||||||
|
|
||||||
|
| Helper | Wraps Method ID |
|
||||||
|
|--------|-----------------|
|
||||||
|
| `wclsHas(w, methodId)` | Check if a method is implemented (non-NULL) |
|
||||||
|
| `wclsPaint(w, d, ops, font, colors)` | `WGT_METHOD_PAINT` |
|
||||||
|
| `wclsPaintOverlay(w, d, ops, font, colors)` | `WGT_METHOD_PAINT_OVERLAY` |
|
||||||
|
| `wclsCalcMinSize(w, font)` | `WGT_METHOD_CALC_MIN_SIZE` |
|
||||||
|
| `wclsLayout(w, font)` | `WGT_METHOD_LAYOUT` |
|
||||||
|
| `wclsGetLayoutMetrics(w, font, pad, gap, extraTop, borderW)` | `WGT_METHOD_GET_LAYOUT_METRICS` |
|
||||||
|
| `wclsOnMouse(w, root, vx, vy)` | `WGT_METHOD_ON_MOUSE` |
|
||||||
|
| `wclsOnKey(w, key, mod)` | `WGT_METHOD_ON_KEY` |
|
||||||
|
| `wclsOnAccelActivate(w, root)` | `WGT_METHOD_ON_ACCEL_ACTIVATE` |
|
||||||
|
| `wclsDestroy(w)` | `WGT_METHOD_DESTROY` |
|
||||||
|
| `wclsOnChildChanged(parent, child)` | `WGT_METHOD_ON_CHILD_CHANGED` |
|
||||||
|
| `wclsGetText(w)` | `WGT_METHOD_GET_TEXT` |
|
||||||
|
| `wclsSetText(w, text)` | `WGT_METHOD_SET_TEXT` |
|
||||||
|
| `wclsClearSelection(w)` | `WGT_METHOD_CLEAR_SELECTION` |
|
||||||
|
| `wclsClosePopup(w)` | `WGT_METHOD_CLOSE_POPUP` |
|
||||||
|
| `wclsGetPopupRect(w, font, contentH, popX, popY, popW, popH)` | `WGT_METHOD_GET_POPUP_RECT` |
|
||||||
|
| `wclsOnDragUpdate(w, root, x, y)` | `WGT_METHOD_ON_DRAG_UPDATE` |
|
||||||
|
| `wclsOnDragEnd(w, root, x, y)` | `WGT_METHOD_ON_DRAG_END` |
|
||||||
|
| `wclsGetCursorShape(w, vx, vy)` | `WGT_METHOD_GET_CURSOR_SHAPE` |
|
||||||
|
| `wclsPoll(w, win)` | `WGT_METHOD_POLL` |
|
||||||
|
| `wclsQuickRepaint(w, outY, outH)` | `WGT_METHOD_QUICK_REPAINT` |
|
||||||
|
| `wclsScrollChildIntoView(parent, child)` | `WGT_METHOD_SCROLL_CHILD_INTO_VIEW` |
|
||||||
|
|
||||||
### WidgetClassT Flags
|
### WidgetClassT Flags
|
||||||
|
|
||||||
|
|
@ -196,22 +328,25 @@ The vtable has 26 function slots plus a flags field:
|
||||||
## Widget Registration
|
## Widget Registration
|
||||||
|
|
||||||
Each widget DXE exports `wgtRegister()`, called by the loader after
|
Each widget DXE exports `wgtRegister()`, called by the loader after
|
||||||
`dlopen`. A typical registration:
|
`dlopen`. The WidgetClassT uses the `handlers[]` array indexed by
|
||||||
|
method IDs:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
static int32_t sButtonType;
|
static int32_t sButtonType;
|
||||||
|
|
||||||
static const WidgetClassT sButtonClass = {
|
static const WidgetClassT sButtonClass = {
|
||||||
.flags = WCLASS_FOCUSABLE | WCLASS_PRESS_RELEASE,
|
.version = WGT_CLASS_VERSION,
|
||||||
.paint = buttonPaint,
|
.flags = WCLASS_FOCUSABLE | WCLASS_PRESS_RELEASE,
|
||||||
.calcMinSize = buttonCalcMinSize,
|
.handlers = {
|
||||||
.onMouse = buttonOnMouse,
|
[WGT_METHOD_PAINT] = buttonPaint,
|
||||||
.onKey = buttonOnKey,
|
[WGT_METHOD_CALC_MIN_SIZE] = buttonCalcMinSize,
|
||||||
.destroy = buttonDestroy,
|
[WGT_METHOD_ON_MOUSE] = buttonOnMouse,
|
||||||
.getText = buttonGetText,
|
[WGT_METHOD_ON_KEY] = buttonOnKey,
|
||||||
.setText = buttonSetText,
|
[WGT_METHOD_DESTROY] = buttonDestroy,
|
||||||
.setPressed = buttonSetPressed,
|
[WGT_METHOD_GET_TEXT] = buttonGetText,
|
||||||
.onAccelActivate = buttonAccelActivate,
|
[WGT_METHOD_SET_TEXT] = buttonSetText,
|
||||||
|
[WGT_METHOD_ON_ACCEL_ACTIVATE] = buttonAccelActivate,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const ButtonApiT sApi = { .create = buttonCreate };
|
static const ButtonApiT sApi = { .create = buttonCreate };
|
||||||
|
|
@ -268,6 +403,28 @@ Two-pass flexbox-like layout:
|
||||||
Extra space beyond minimum is distributed proportionally to weights.
|
Extra space beyond minimum is distributed proportionally to weights.
|
||||||
`weight=0` means fixed size, `weight=100` is the default flexible weight.
|
`weight=0` means fixed size, `weight=100` is the default flexible weight.
|
||||||
|
|
||||||
|
### Cross-Axis Centering
|
||||||
|
|
||||||
|
When a child widget has a `maxW` (in a VBox) or `maxH` (in an HBox)
|
||||||
|
that constrains it smaller than the available cross-axis space, the
|
||||||
|
layout engine automatically centers the child on the cross axis. This
|
||||||
|
means setting `maxW` or `maxH` on a child inside a container will both
|
||||||
|
cap its size and center it within the remaining space.
|
||||||
|
|
||||||
|
|
||||||
|
## Image Loading
|
||||||
|
|
||||||
|
Two image loading functions are available:
|
||||||
|
|
||||||
|
| Function | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `dvxLoadImage(ctx, path, outW, outH, outPitch)` | Load from a file path |
|
||||||
|
| `dvxLoadImageFromMemory(ctx, data, dataLen, outW, outH, outPitch)` | Load from a memory buffer (e.g. resource data) |
|
||||||
|
|
||||||
|
Both convert to the display's native pixel format. Caller frees the
|
||||||
|
returned buffer with `dvxFreeImage()`. Supported formats: BMP, PNG,
|
||||||
|
JPEG, GIF (via stb_image).
|
||||||
|
|
||||||
|
|
||||||
## Exported Symbols
|
## Exported Symbols
|
||||||
|
|
||||||
|
|
@ -275,10 +432,10 @@ libdvx.lib exports symbols matching these prefixes:
|
||||||
|
|
||||||
```
|
```
|
||||||
dvx*, wgt*, wm*, prefs*, rect*, draw*, pack*, text*, setClip*,
|
dvx*, wgt*, wm*, prefs*, rect*, draw*, pack*, text*, setClip*,
|
||||||
resetClip*, stbi*, dirtyList*, widget*, accelParse*, clipboard*,
|
resetClip*, stbi*, stbi_write*, dirtyList*, widget*,
|
||||||
multiClick*, sCursor*, sDbl*, sDebug*, sClosed*, sFocused*, sKey*,
|
accelParse*, clipboard*, multiClick*,
|
||||||
sOpen*, sPressed*, sDrag*, sDrawing*, sResize*, sListView*,
|
sCursor*, sDbl*, sDebug*, sClosed*, sFocused*, sKey*,
|
||||||
sSplitter*, sTreeView*
|
sOpen*, sDrag*, sClosed*, sKey*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
3682
core/dvxApp.c
3682
core/dvxApp.c
File diff suppressed because it is too large
Load diff
|
|
@ -34,6 +34,7 @@ libdvx.lib (deps: libtasks)
|
||||||
texthelp.lib (deps: libtasks, libdvx)
|
texthelp.lib (deps: libtasks, libdvx)
|
||||||
listhelp.lib (deps: libtasks, libdvx)
|
listhelp.lib (deps: libtasks, libdvx)
|
||||||
dvxshell.lib (deps: libtasks, libdvx, texthelp, listhelp)
|
dvxshell.lib (deps: libtasks, libdvx, texthelp, listhelp)
|
||||||
|
taskmgr.lib (deps: dvxshell, libtasks, libdvx, texthelp, listhelp)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 2: Widgets (widgets/*.wgt)
|
### Phase 2: Widgets (widgets/*.wgt)
|
||||||
|
|
@ -73,7 +74,11 @@ void dvxLog(const char *fmt, ...);
|
||||||
|
|
||||||
### stb_ds
|
### stb_ds
|
||||||
|
|
||||||
The `STB_DS_IMPLEMENTATION` is compiled into the loader. stb_ds
|
The `STB_DS_IMPLEMENTATION` is compiled into the loader with
|
||||||
|
`STBDS_REALLOC` and `STBDS_FREE` overridden to use `dvxRealloc` and
|
||||||
|
`dvxFree`. This means all `arrput`/`hmput`/`arrfree` calls in DXE
|
||||||
|
code route through the per-app memory tracker, so stb_ds memory is
|
||||||
|
attributed correctly in the Task Manager's memory column. stb_ds
|
||||||
functions are exported to all DXE modules via the platform export
|
functions are exported to all DXE modules via the platform export
|
||||||
table.
|
table.
|
||||||
|
|
||||||
|
|
@ -89,6 +94,24 @@ All platform functions are exported to DXE modules. This includes:
|
||||||
* Crash: signal handler installation, register dump logging
|
* Crash: signal handler installation, register dump logging
|
||||||
* System: memory info, directory creation, path utilities
|
* System: memory info, directory creation, path utilities
|
||||||
|
|
||||||
|
### Per-App Memory Tracking
|
||||||
|
|
||||||
|
The DXE export table maps standard C allocation symbols to tracked
|
||||||
|
wrappers:
|
||||||
|
|
||||||
|
| C Symbol | Mapped To | Effect |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| `malloc` | `dvxMalloc` | Allocations attributed to `currentAppId` |
|
||||||
|
| `calloc` | `dvxCalloc` | Same |
|
||||||
|
| `realloc` | `dvxRealloc` | Transfers attribution on resize |
|
||||||
|
| `free` | `dvxFree` | Decrements app's tracked usage |
|
||||||
|
| `strdup` | `dvxStrdup` | Tracks the duplicated string |
|
||||||
|
|
||||||
|
This is transparent to DXE code -- apps call `malloc` normally and the
|
||||||
|
tracked wrapper runs instead. The Task Manager reads per-app usage via
|
||||||
|
`dvxMemGetAppUsage()`. When an app is reaped, `dvxMemResetApp()` zeroes
|
||||||
|
its counter.
|
||||||
|
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,17 @@ are loaded. `shellMain()`:
|
||||||
1. Loads preferences from `CONFIG/DVX.INI`
|
1. Loads preferences from `CONFIG/DVX.INI`
|
||||||
2. Initializes the GUI via `dvxInit()` with configured video mode
|
2. Initializes the GUI via `dvxInit()` with configured video mode
|
||||||
3. Applies saved mouse, color, and wallpaper settings from INI
|
3. Applies saved mouse, color, and wallpaper settings from INI
|
||||||
4. Initializes the cooperative task system (`tsInit()`)
|
4. Shows a splash screen ("DVX - DOS Visual eXecutive / Loading...")
|
||||||
5. Sets shell task (task 0) to `TS_PRIORITY_HIGH`
|
5. Initializes the cooperative task system (`tsInit()`)
|
||||||
6. Gathers system information via the platform layer
|
6. Sets shell task (task 0) to `TS_PRIORITY_HIGH`
|
||||||
7. Initializes the app slot table
|
7. Gathers system information via the platform layer
|
||||||
8. Registers idle callback, Ctrl+Esc handler, and title change handler
|
8. Initializes the app slot table
|
||||||
9. Loads the desktop app (default: `apps/progman/progman.app`)
|
9. Points the memory tracker at `currentAppId` for per-app attribution
|
||||||
10. Installs the crash handler (after init, so init crashes are fatal)
|
10. Registers idle callback, Ctrl+Esc handler, and title change handler
|
||||||
11. Enters the main loop
|
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
|
## Main Loop
|
||||||
|
|
@ -60,8 +63,9 @@ during graceful shutdown.
|
||||||
|
|
||||||
`appMain()` is called directly in the shell's task 0. It creates
|
`appMain()` is called directly in the shell's task 0. It creates
|
||||||
windows, registers callbacks, and returns immediately. The app lives
|
windows, registers callbacks, and returns immediately. The app lives
|
||||||
entirely through GUI callbacks. Lifecycle ends when the last window
|
entirely through GUI callbacks. The shell reaps callback-only apps
|
||||||
closes.
|
automatically when their last window closes -- `shellReapApps()` checks
|
||||||
|
each frame for running callback apps with zero remaining windows.
|
||||||
|
|
||||||
### Main-Loop Apps (hasMainLoop = true)
|
### Main-Loop Apps (hasMainLoop = true)
|
||||||
|
|
||||||
|
|
@ -87,7 +91,12 @@ Free -> Loaded -> Running -> Terminating -> Free
|
||||||
|
|
||||||
App slots are managed as a stb_ds dynamic array (no fixed max). Each
|
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/
|
slot tracks: app ID, name, path, DXE handle, state, task ID, entry/
|
||||||
shutdown function pointers, and the `DxeAppContextT` passed to the app.
|
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:
|
The `DxeAppContextT` gives each app:
|
||||||
- `shellCtx` -- pointer to the shell's `AppContextT`
|
- `shellCtx` -- pointer to the shell's `AppContextT`
|
||||||
|
|
@ -102,6 +111,15 @@ executing. The shell sets this before calling app code.
|
||||||
`dvxCreateWindow()` stamps `win->appId` directly so the shell can
|
`dvxCreateWindow()` stamps `win->appId` directly so the shell can
|
||||||
associate windows with apps for cleanup.
|
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
|
## Crash Recovery
|
||||||
|
|
||||||
|
|
@ -121,13 +139,15 @@ This gives Windows 3.1-style fault tolerance -- one bad app does not
|
||||||
take down the whole system.
|
take down the whole system.
|
||||||
|
|
||||||
|
|
||||||
## Task Manager
|
## Task Manager Integration
|
||||||
|
|
||||||
The built-in Task Manager is always available via Ctrl+Esc. It shows
|
The Task Manager is a separate DXE (`taskmgr.lib` in `taskmgr/`), not
|
||||||
all running apps with their names and provides an "End Task" button
|
built into the shell. It registers itself at load time via a DXE
|
||||||
for force-killing hung apps. The Task Manager is a shell-level
|
constructor that sets the `shellCtrlEscFn` function pointer. The shell
|
||||||
component, not tied to any app -- it persists even if the desktop app
|
calls this pointer on Ctrl+Esc. If `taskmgr.lib` is not loaded,
|
||||||
is terminated.
|
`shellCtrlEscFn` is NULL and Ctrl+Esc does nothing.
|
||||||
|
|
||||||
|
See `taskmgr/README.md` for full Task Manager documentation.
|
||||||
|
|
||||||
|
|
||||||
## Desktop Update Notifications
|
## Desktop Update Notifications
|
||||||
|
|
@ -141,13 +161,11 @@ Apps (especially the desktop app) register callbacks via
|
||||||
|
|
||||||
| File | Description |
|
| File | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `shellMain.c` | Entry point, main loop, crash recovery, idle callback |
|
| `shellMain.c` | Entry point, main loop, crash recovery, splash screen, idle callback |
|
||||||
| `shellApp.h` | App lifecycle types: `AppDescriptorT`, `DxeAppContextT`, `ShellAppT`, `AppStateE` |
|
| `shellApp.h` | App lifecycle types: `AppDescriptorT`, `DxeAppContextT`, `ShellAppT`, `AppStateE`; `shellCtrlEscFn` extern |
|
||||||
| `shellApp.c` | App loading, reaping, task creation, DXE management |
|
| `shellApp.c` | App loading, reaping, task creation, DXE management, per-app memory tracking |
|
||||||
| `shellInfo.h` | System information wrapper |
|
| `shellInfo.h` | System information wrapper |
|
||||||
| `shellInfo.c` | Gathers and caches hardware info via platform layer |
|
| `shellInfo.c` | Gathers and caches hardware info via platform layer |
|
||||||
| `shellTaskMgr.h` | Task Manager API |
|
|
||||||
| `shellTaskMgr.c` | Task Manager window (list of running apps, End Task) |
|
|
||||||
| `Makefile` | Builds `bin/libs/dvxshell.lib` + config/themes/wallpapers |
|
| `Makefile` | Builds `bin/libs/dvxshell.lib` + config/themes/wallpapers |
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
75
taskmgr/README.md
Normal file
75
taskmgr/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# DVX Task Manager (taskmgr.lib)
|
||||||
|
|
||||||
|
A separate DXE3 module that provides the system Task Manager window.
|
||||||
|
Built as `taskmgr.lib` and loaded alongside the other libs at startup.
|
||||||
|
The Task Manager is a shell-level component, not tied to any app -- it
|
||||||
|
persists even if the desktop app (Program Manager) is terminated.
|
||||||
|
|
||||||
|
|
||||||
|
## Registration
|
||||||
|
|
||||||
|
The module uses a GCC `__attribute__((constructor))` function to
|
||||||
|
register itself with the shell at load time. The constructor sets the
|
||||||
|
`shellCtrlEscFn` function pointer (declared in `shellApp.h`) to
|
||||||
|
`shellTaskMgrOpen`. The shell calls this pointer when Ctrl+Esc is
|
||||||
|
pressed. If `taskmgr.lib` is not loaded, the pointer remains NULL and
|
||||||
|
Ctrl+Esc does nothing.
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **App list**: 6-column ListView showing Name, Title, File, Type,
|
||||||
|
Memory, and Status for all running apps
|
||||||
|
- **Memory column**: Per-app memory usage from `dvxMemGetAppUsage()`,
|
||||||
|
displayed in KB or MB
|
||||||
|
- **Switch To**: Raises and focuses the selected app's topmost window;
|
||||||
|
restores it if minimized. Also triggered by double-clicking a row.
|
||||||
|
- **End Task**: Force-kills the selected app via `shellForceKillApp()`
|
||||||
|
- **Run...**: Opens a file dialog to browse for and launch a `.app` file
|
||||||
|
- **Accelerator keys**: `&Switch To` (Alt+S), `&End Task` (Alt+E),
|
||||||
|
`&Run...` (Alt+R)
|
||||||
|
- **Status bar**: Shows running app count and system memory usage
|
||||||
|
- **Live refresh**: Registers a desktop update callback so the list
|
||||||
|
refreshes automatically when apps are loaded, reaped, or crash
|
||||||
|
|
||||||
|
|
||||||
|
## Window Behavior
|
||||||
|
|
||||||
|
- Owned by the shell (appId = 0), not by any app
|
||||||
|
- Single-instance: if already open, Ctrl+Esc raises and focuses it
|
||||||
|
- Closing the window unregisters the desktop update callback and
|
||||||
|
cleans up dynamic arrays
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Open or raise the Task Manager window.
|
||||||
|
void shellTaskMgrOpen(AppContextT *ctx);
|
||||||
|
|
||||||
|
// Refresh the task list (called by desktop update notification).
|
||||||
|
void shellTaskMgrRefresh(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Loaded after: `dvxshell`, `libtasks`, `libdvx`, `texthelp`, `listhelp`
|
||||||
|
(via `taskmgr.dep`).
|
||||||
|
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `shellTaskMgr.h` | Public API (open, refresh) |
|
||||||
|
| `shellTaskMgr.c` | Task Manager window, list, buttons, DXE constructor |
|
||||||
|
| `Makefile` | Builds `bin/libs/taskmgr.lib` + dep file |
|
||||||
|
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```
|
||||||
|
make # builds bin/libs/taskmgr.lib + taskmgr.dep
|
||||||
|
make clean # removes objects, library, and dep file
|
||||||
|
```
|
||||||
117
tools/README.md
Normal file
117
tools/README.md
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
# DVX Tools
|
||||||
|
|
||||||
|
Host-native utilities that run on the development machine (Linux or
|
||||||
|
DOS). These are not DXE modules and do not cross-compile with DJGPP --
|
||||||
|
they build with the system GCC.
|
||||||
|
|
||||||
|
|
||||||
|
## dvxres -- Resource Tool
|
||||||
|
|
||||||
|
Command-line tool for managing resource blocks appended to DXE3 files
|
||||||
|
(`.app`, `.wgt`, `.lib`). Resources are invisible to `dlopen` because
|
||||||
|
they are appended after the DXE3 content.
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```
|
||||||
|
dvxres add <file> <name> <type> <data|@file>
|
||||||
|
dvxres build <file> <manifest.res>
|
||||||
|
dvxres list <file>
|
||||||
|
dvxres get <file> <name> [outfile]
|
||||||
|
dvxres strip <file>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `add` | Add or replace a single resource in a DXE file |
|
||||||
|
| `build` | Add all resources listed in a manifest file (replaces any existing resources) |
|
||||||
|
| `list` | List all resources in a DXE file |
|
||||||
|
| `get` | Extract a resource to a file or stdout |
|
||||||
|
| `strip` | Remove all appended resources, leaving only the DXE content |
|
||||||
|
|
||||||
|
### Resource Types
|
||||||
|
|
||||||
|
| Type | Keyword | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `DVX_RES_ICON` | `icon` or `image` | Image data (BMP icons, etc.) |
|
||||||
|
| `DVX_RES_TEXT` | `text` | Null-terminated string (author, copyright) |
|
||||||
|
| `DVX_RES_BINARY` | `binary` | Arbitrary binary data (app-specific) |
|
||||||
|
|
||||||
|
For `add` with type `text`, the data argument is the string value
|
||||||
|
directly. For `icon` or `binary`, the data argument is a file path.
|
||||||
|
|
||||||
|
### Resource File Format
|
||||||
|
|
||||||
|
Resources are appended after the normal DXE3 content:
|
||||||
|
|
||||||
|
```
|
||||||
|
[DXE3 content] -- untouched, loaded by dlopen
|
||||||
|
[resource data entries] -- sequential, variable length
|
||||||
|
[resource directory] -- fixed-size 48-byte entries
|
||||||
|
[footer] -- 16 bytes: magic + dir offset + count
|
||||||
|
```
|
||||||
|
|
||||||
|
The footer is at the very end of the file. Reading starts from
|
||||||
|
`EOF - 16` bytes. The magic value is `0x52585644` ("DVXR" in
|
||||||
|
little-endian). The directory offset points to the start of the
|
||||||
|
directory entries, and the entry count gives the number of resources.
|
||||||
|
|
||||||
|
Each directory entry (48 bytes) contains:
|
||||||
|
- `name[32]` -- resource name (null-terminated)
|
||||||
|
- `type` (uint32) -- DVX_RES_ICON, DVX_RES_TEXT, or DVX_RES_BINARY
|
||||||
|
- `offset` (uint32) -- absolute file offset of data
|
||||||
|
- `size` (uint32) -- data size in bytes
|
||||||
|
- `reserved` (uint32) -- padding
|
||||||
|
|
||||||
|
### Manifest File Format (.res)
|
||||||
|
|
||||||
|
Plain text, one resource per line:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Comment lines start with #
|
||||||
|
name type data
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
icon32 icon icons/myapp32.bmp
|
||||||
|
icon16 icon icons/myapp16.bmp
|
||||||
|
author text "John Doe"
|
||||||
|
appdata binary data/config.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line has three fields: name, type, and data. Text data can be
|
||||||
|
quoted. Empty lines and lines starting with `#` are ignored.
|
||||||
|
|
||||||
|
|
||||||
|
## mkicon -- Icon Generator
|
||||||
|
|
||||||
|
Generates simple 32x32 24-bit BMP pixel-art icons for DVX apps.
|
||||||
|
|
||||||
|
```
|
||||||
|
mkicon <output.bmp> <type>
|
||||||
|
```
|
||||||
|
|
||||||
|
Available icon types: `clock`, `notepad`, `cpanel`, `dvxdemo`, `imgview`.
|
||||||
|
|
||||||
|
Note: mkicon is defined in `mkicon.c` but is not currently built by the
|
||||||
|
Makefile (it was used during initial development).
|
||||||
|
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `dvxres.c` | Resource tool implementation |
|
||||||
|
| `mkicon.c` | Icon generator (not built by default) |
|
||||||
|
| `Makefile` | Builds `bin/dvxres` (host native) |
|
||||||
|
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```
|
||||||
|
make # builds bin/dvxres
|
||||||
|
make clean # removes bin/dvxres
|
||||||
|
```
|
||||||
|
|
||||||
|
Uses the system GCC, not the DJGPP cross-compiler. Links against
|
||||||
|
`core/dvxResource.c` for the runtime resource API (`dvxResOpen`,
|
||||||
|
`dvxResRead`, `dvxResClose`).
|
||||||
|
|
@ -7,11 +7,50 @@ which registers its widget class(es) and API struct with the core.
|
||||||
|
|
||||||
Core knows nothing about individual widgets. All per-widget state lives
|
Core knows nothing about individual widgets. All per-widget state lives
|
||||||
in `w->data` (allocated by the widget DXE). All per-widget behavior is
|
in `w->data` (allocated by the widget DXE). All per-widget behavior is
|
||||||
dispatched through the WidgetClassT vtable. Each widget provides a
|
dispatched through the `WidgetClassT` vtable. Each widget provides a
|
||||||
public header with its API struct, typed accessor, and convenience
|
public header with its API struct, typed accessor, and convenience
|
||||||
macros.
|
macros.
|
||||||
|
|
||||||
|
|
||||||
|
## ABI-Stable Handler Dispatch
|
||||||
|
|
||||||
|
`WidgetClassT` contains a `handlers[WGT_METHOD_MAX]` array of function
|
||||||
|
pointers. Core dispatches via integer index:
|
||||||
|
`w->wclass->handlers[WGT_METHOD_PAINT]`, etc. Currently 21 methods are
|
||||||
|
defined (`WGT_METHOD_COUNT`), with room for 32 (`WGT_METHOD_MAX`).
|
||||||
|
Adding new methods does not break existing widget DXE binaries -- old
|
||||||
|
widgets simply have NULL in the new slots.
|
||||||
|
|
||||||
|
Key method constants:
|
||||||
|
|
||||||
|
| Constant | Index | Signature |
|
||||||
|
|----------|-------|-----------|
|
||||||
|
| `WGT_METHOD_PAINT` | 0 | `(w, d, ops, font, colors)` |
|
||||||
|
| `WGT_METHOD_PAINT_OVERLAY` | 1 | `(w, d, ops, font, colors)` |
|
||||||
|
| `WGT_METHOD_CALC_MIN_SIZE` | 2 | `(w, font)` |
|
||||||
|
| `WGT_METHOD_LAYOUT` | 3 | `(w, font)` |
|
||||||
|
| `WGT_METHOD_ON_MOUSE` | 5 | `(w, root, vx, vy)` |
|
||||||
|
| `WGT_METHOD_ON_KEY` | 6 | `(w, key, mod)` |
|
||||||
|
| `WGT_METHOD_DESTROY` | 8 | `(w)` |
|
||||||
|
| `WGT_METHOD_ON_DRAG_UPDATE` | 15 | `(w, root, x, y)` |
|
||||||
|
| `WGT_METHOD_ON_DRAG_END` | 16 | `(w, root, x, y)` |
|
||||||
|
| `WGT_METHOD_QUICK_REPAINT` | 19 | `(w, outY, outH)` |
|
||||||
|
|
||||||
|
## Generic Drag Support
|
||||||
|
|
||||||
|
Widgets that need drag behavior implement `WGT_METHOD_ON_DRAG_UPDATE`
|
||||||
|
and `WGT_METHOD_ON_DRAG_END`. Core tracks the drag widget and calls
|
||||||
|
these methods on mouse move/release during a drag. This replaces
|
||||||
|
per-widget drag hacks. Used by Slider, Splitter, ListBox (reorder),
|
||||||
|
ListView (reorder, column resize), and TreeView (reorder).
|
||||||
|
|
||||||
|
## Dynamic Limits
|
||||||
|
|
||||||
|
All widget child arrays, app slot tables, and callback lists use stb_ds
|
||||||
|
dynamic arrays. There are no fixed maximums for child count, app count,
|
||||||
|
or registered callbacks.
|
||||||
|
|
||||||
|
|
||||||
## Widget Summary
|
## Widget Summary
|
||||||
|
|
||||||
26 source files produce 26 `.wgt` modules containing 32 widget types:
|
26 source files produce 26 `.wgt` modules containing 32 widget types:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue