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~
|
||||
.gitattributes~
|
||||
*.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/texthelp.lib shared text editing 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
|
||||
|
|
||||
|
|
@ -37,9 +38,11 @@ dvx.exe (loader)
|
|||
| `tasks/` | `bin/libs/libtasks.lib` | Cooperative task switching |
|
||||
| `texthelp/` | `bin/libs/texthelp.lib` | Shared text editing 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 |
|
||||
| `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 |
|
||||
| `rs232/` | `lib/librs232.a` | ISR-driven UART serial driver |
|
||||
| `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:
|
||||
|
||||
```
|
||||
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
|
||||
|
|
@ -80,6 +84,7 @@ bin/
|
|||
texthelp.lib text editing helpers
|
||||
listhelp.lib list/dropdown helpers
|
||||
dvxshell.lib DVX shell
|
||||
taskmgr.lib task manager (Ctrl+Esc)
|
||||
*.dep dependency files for load ordering
|
||||
widgets/
|
||||
box.wgt VBox/HBox/Frame containers
|
||||
|
|
@ -117,9 +122,17 @@ dvxWidget.h.
|
|||
|
||||
* **Dynamic type IDs**: `wgtRegisterClass()` assigns IDs at load time
|
||||
* **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 headers**: `widgets/widgetButton.h` etc. provide typed macros
|
||||
* **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
|
||||
|
|
@ -148,10 +161,12 @@ emulators, games).
|
|||
|
||||
## Crash Recovery
|
||||
|
||||
The shell installs signal handlers for SIGSEGV, SIGFPE, and SIGILL. If
|
||||
an app crashes, the handler `longjmp`s back to the shell's main loop,
|
||||
the crashed app is force-killed, and the shell continues running.
|
||||
Diagnostic information (registers, faulting EIP) is logged to `dvx.log`.
|
||||
The shell installs signal handlers for SIGSEGV, SIGFPE, and SIGILL
|
||||
before loading any apps, so even crashes during app initialization are
|
||||
caught. If an app crashes, the handler `longjmp`s back to the shell's
|
||||
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
|
||||
|
|
@ -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_ds.h | `core/thirdparty/` | Dynamic arrays and hash maps |
|
||||
|
||||
stb_ds implementation is compiled into dvx.exe (the loader) and
|
||||
exported via `dlregsym` to all DXE modules.
|
||||
stb_ds implementation is compiled into dvx.exe (the loader) with
|
||||
`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
|
||||
|
|
|
|||
249
core/README.md
249
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
|
||||
WidgetTypeE enum, no widget union, and no per-widget structs in
|
||||
dvxWidget.h. All widget-specific behavior is dispatched through the
|
||||
WidgetClassT vtable.
|
||||
WidgetClassT dispatch table.
|
||||
|
||||
|
||||
## 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 |
|
||||
| `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 |
|
||||
| `dvxWidgetPlugin.h` | (header only) | Plugin API for widget DXE modules |
|
||||
| -- | `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 |
|
||||
| `dvxDialog.c` | `dvxMessageBox()`, `dvxFileDialog()` -- modal dialogs with own event loops |
|
||||
| `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 |
|
||||
| `widgetClass.c` | `wgtRegisterClass()`, `wgtRegisterApi()`, `wgtGetApi()`, class table |
|
||||
| `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 |
|
||||
| `dvxDialog.h` | Modal dialogs: `dvxMessageBox()`, `dvxFileDialog()` |
|
||||
| `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 |
|
||||
| `dvxFont.h` | Embedded 8x14 and 8x16 bitmap font data (CP437) |
|
||||
| `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 |
|
||||
|------|-------------|
|
||||
| `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) |
|
||||
|
||||
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) |
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
typedef struct WidgetT {
|
||||
int32_t type; // assigned by wgtRegisterClass()
|
||||
const struct WidgetClassT *wclass; // vtable pointer
|
||||
char name[32];
|
||||
const struct WidgetClassT *wclass; // dispatch table pointer
|
||||
char name[MAX_WIDGET_NAME];
|
||||
|
||||
// Tree linkage
|
||||
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.
|
||||
The vtable has 26 function slots plus a flags field:
|
||||
WidgetClassT is an ABI-stable dispatch table. Method IDs are fixed
|
||||
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 |
|
||||
|------|-----------|---------|
|
||||
| `flags` | `uint32_t` | Static properties (see Flags below) |
|
||||
| `paint` | `(w, d, ops, font, colors)` | Render the widget |
|
||||
| `paintOverlay` | `(w, d, ops, font, colors)` | Render overlay (dropdown popup) |
|
||||
| `calcMinSize` | `(w, font)` | Compute minimum size (bottom-up pass) |
|
||||
| `layout` | `(w, font)` | Position children (top-down pass) |
|
||||
| `getLayoutMetrics` | `(w, font, pad, gap, extraTop, borderW)` | Return padding/gap for box layout |
|
||||
| `onMouse` | `(w, root, vx, vy)` | Handle mouse click |
|
||||
| `onKey` | `(w, key, mod)` | Handle keyboard input |
|
||||
| `onAccelActivate` | `(w, root)` | Handle accelerator key match |
|
||||
| `destroy` | `(w)` | Free widget-private data |
|
||||
| `onChildChanged` | `(parent, child)` | Notification when a child changes |
|
||||
| `getText` | `(w)` | Return widget text |
|
||||
| `setText` | `(w, text)` | Set widget text |
|
||||
| `clearSelection` | `(w)` | Clear text/item selection |
|
||||
| `closePopup` | `(w)` | Close dropdown popup |
|
||||
| `getPopupRect` | `(w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle |
|
||||
| `onDragUpdate` | `(w, root, x, y)` | Mouse move during drag |
|
||||
| `onDragEnd` | `(w, root, x, y)` | Mouse release after drag |
|
||||
| `getCursorShape` | `(w, vx, vy)` | Return cursor ID for this position |
|
||||
| `poll` | `(w, win)` | Periodic polling (AnsiTerm comms) |
|
||||
| `quickRepaint` | `(w, outY, outH)` | Fast incremental repaint |
|
||||
```c
|
||||
#define WGT_CLASS_VERSION 1 // bump on breaking ABI change
|
||||
#define WGT_METHOD_MAX 32 // room for future methods
|
||||
|
||||
typedef struct WidgetClassT {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
void *handlers[WGT_METHOD_MAX];
|
||||
} WidgetClassT;
|
||||
```
|
||||
|
||||
### Method ID Table
|
||||
|
||||
21 methods are currently defined (IDs 0--20). WGT_METHOD_MAX is 32,
|
||||
leaving room for 11 future methods without a version bump.
|
||||
|
||||
| ID | Method ID | Signature | Purpose |
|
||||
|----|-----------|-----------|---------|
|
||||
| 0 | `WGT_METHOD_PAINT` | `void (w, d, ops, font, colors)` | Render the widget |
|
||||
| 1 | `WGT_METHOD_PAINT_OVERLAY` | `void (w, d, ops, font, colors)` | Render overlay (dropdown popup) |
|
||||
| 2 | `WGT_METHOD_CALC_MIN_SIZE` | `void (w, font)` | Compute minimum size (bottom-up pass) |
|
||||
| 3 | `WGT_METHOD_LAYOUT` | `void (w, font)` | Position children (top-down pass) |
|
||||
| 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
|
||||
|
||||
|
|
@ -196,22 +328,25 @@ The vtable has 26 function slots plus a flags field:
|
|||
## Widget Registration
|
||||
|
||||
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
|
||||
static int32_t sButtonType;
|
||||
|
||||
static const WidgetClassT sButtonClass = {
|
||||
.version = WGT_CLASS_VERSION,
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_PRESS_RELEASE,
|
||||
.paint = buttonPaint,
|
||||
.calcMinSize = buttonCalcMinSize,
|
||||
.onMouse = buttonOnMouse,
|
||||
.onKey = buttonOnKey,
|
||||
.destroy = buttonDestroy,
|
||||
.getText = buttonGetText,
|
||||
.setText = buttonSetText,
|
||||
.setPressed = buttonSetPressed,
|
||||
.onAccelActivate = buttonAccelActivate,
|
||||
.handlers = {
|
||||
[WGT_METHOD_PAINT] = buttonPaint,
|
||||
[WGT_METHOD_CALC_MIN_SIZE] = buttonCalcMinSize,
|
||||
[WGT_METHOD_ON_MOUSE] = buttonOnMouse,
|
||||
[WGT_METHOD_ON_KEY] = buttonOnKey,
|
||||
[WGT_METHOD_DESTROY] = buttonDestroy,
|
||||
[WGT_METHOD_GET_TEXT] = buttonGetText,
|
||||
[WGT_METHOD_SET_TEXT] = buttonSetText,
|
||||
[WGT_METHOD_ON_ACCEL_ACTIVATE] = buttonAccelActivate,
|
||||
}
|
||||
};
|
||||
|
||||
static const ButtonApiT sApi = { .create = buttonCreate };
|
||||
|
|
@ -268,6 +403,28 @@ Two-pass flexbox-like layout:
|
|||
Extra space beyond minimum is distributed proportionally to weights.
|
||||
`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
|
||||
|
||||
|
|
@ -275,10 +432,10 @@ libdvx.lib exports symbols matching these prefixes:
|
|||
|
||||
```
|
||||
dvx*, wgt*, wm*, prefs*, rect*, draw*, pack*, text*, setClip*,
|
||||
resetClip*, stbi*, dirtyList*, widget*, accelParse*, clipboard*,
|
||||
multiClick*, sCursor*, sDbl*, sDebug*, sClosed*, sFocused*, sKey*,
|
||||
sOpen*, sPressed*, sDrag*, sDrawing*, sResize*, sListView*,
|
||||
sSplitter*, sTreeView*
|
||||
resetClip*, stbi*, stbi_write*, dirtyList*, widget*,
|
||||
accelParse*, clipboard*, multiClick*,
|
||||
sCursor*, sDbl*, sDebug*, sClosed*, sFocused*, sKey*,
|
||||
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)
|
||||
listhelp.lib (deps: libtasks, libdvx)
|
||||
dvxshell.lib (deps: libtasks, libdvx, texthelp, listhelp)
|
||||
taskmgr.lib (deps: dvxshell, libtasks, libdvx, texthelp, listhelp)
|
||||
```
|
||||
|
||||
### Phase 2: Widgets (widgets/*.wgt)
|
||||
|
|
@ -73,7 +74,11 @@ void dvxLog(const char *fmt, ...);
|
|||
|
||||
### 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
|
||||
table.
|
||||
|
||||
|
|
@ -89,6 +94,24 @@ All platform functions are exported to DXE modules. This includes:
|
|||
* Crash: signal handler installation, register dump logging
|
||||
* 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
|
||||
|
||||
|
|
|
|||
|
|
@ -14,14 +14,17 @@ 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. Initializes the cooperative task system (`tsInit()`)
|
||||
5. Sets shell task (task 0) to `TS_PRIORITY_HIGH`
|
||||
6. Gathers system information via the platform layer
|
||||
7. Initializes the app slot table
|
||||
8. Registers idle callback, Ctrl+Esc handler, and title change handler
|
||||
9. Loads the desktop app (default: `apps/progman/progman.app`)
|
||||
10. Installs the crash handler (after init, so init crashes are fatal)
|
||||
11. Enters the main loop
|
||||
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
|
||||
|
|
@ -60,8 +63,9 @@ during graceful shutdown.
|
|||
|
||||
`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. Lifecycle ends when the last window
|
||||
closes.
|
||||
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)
|
||||
|
||||
|
|
@ -87,7 +91,12 @@ Free -> Loaded -> Running -> Terminating -> Free
|
|||
|
||||
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 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:
|
||||
- `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
|
||||
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
|
||||
|
||||
|
|
@ -121,13 +139,15 @@ This gives Windows 3.1-style fault tolerance -- one bad app does not
|
|||
take down the whole system.
|
||||
|
||||
|
||||
## Task Manager
|
||||
## Task Manager Integration
|
||||
|
||||
The built-in Task Manager is always available via Ctrl+Esc. It shows
|
||||
all running apps with their names and provides an "End Task" button
|
||||
for force-killing hung apps. The Task Manager is a shell-level
|
||||
component, not tied to any app -- it persists even if the desktop app
|
||||
is terminated.
|
||||
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
|
||||
|
|
@ -141,13 +161,11 @@ Apps (especially the desktop app) register callbacks via
|
|||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `shellMain.c` | Entry point, main loop, crash recovery, idle callback |
|
||||
| `shellApp.h` | App lifecycle types: `AppDescriptorT`, `DxeAppContextT`, `ShellAppT`, `AppStateE` |
|
||||
| `shellApp.c` | App loading, reaping, task creation, DXE management |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
|
||||
|
|
|
|||
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
|
||||
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
|
||||
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
|
||||
|
||||
26 source files produce 26 `.wgt` modules containing 32 widget types:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue