DVX_GUI/core/README.md

292 lines
12 KiB
Markdown

# DVX Core Library (libdvx.lib)
The core GUI infrastructure for DVX, built as a DXE3 module. Provides
VESA video setup, 2D drawing primitives, dirty-rectangle compositing,
a window manager with Motif-style chrome, and the widget infrastructure
(layout engine, event dispatch, class registration). Individual widget
type implementations live in `../widgets/` as separate `.wgt` DXE
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.
## 5-Layer Architecture
| Layer | Header | Source | Description |
|-------|--------|--------|-------------|
| 1. Video | `dvxVideo.h` | `dvxVideo.c` | VESA VBE init, LFB mapping, backbuffer, pixel format, `packColor()` |
| 2. Draw | `dvxDraw.h` | `dvxDraw.c` | Rect fills, bevels, text, bitmap cursors, focus rects, lines |
| 3. Compositor | `dvxComp.h` | `dvxComp.c` | Dirty rect tracking, merge, clip, LFB flush |
| 4. Window Manager | `dvxWm.h` | `dvxWm.c` | Window stack, chrome, drag/resize, menus, scrollbars, hit test |
| 5. Application | `dvxApp.h` | `dvxApp.c` | Event loop, input polling, color schemes, wallpaper, public API |
Additional modules built into libdvx.lib:
| Header | Source | Description |
|--------|--------|-------------|
| `dvxDialog.h` | `dvxDialog.c` | Modal message box and file open/save dialogs |
| `dvxPrefs.h` | `dvxPrefs.c` | INI-based preferences (read/write with typed accessors) |
| `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) |
| -- | `dvxImageWrite.c` | PNG export via stb_image_write |
## Source Files
| File | Description |
|------|-------------|
| `dvxVideo.c` | VESA mode negotiation, LFB mapping via DPMI, backbuffer alloc, `packColor()` |
| `dvxDraw.c` | `rectFill()`, `rectCopy()`, `drawBevel()`, `drawText()`, `drawTextN()`, `drawTermRow()`, cursor rendering |
| `dvxComp.c` | `dirtyListAdd()`, `dirtyListMerge()`, `flushRect()`, `rectIntersect()` |
| `dvxWm.c` | Window create/destroy, Z-order, chrome drawing, drag/resize, menu bar, scrollbars, minimize/maximize |
| `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 |
| `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 |
| `widgetEvent.c` | `widgetOnMouse()`, `widgetOnKey()`, `widgetOnPaint()`, `widgetOnResize()`, scrollbar management |
| `widgetLayout.c` | Two-pass flexbox layout: bottom-up `calcMinSize`, top-down space allocation with weights |
| `widgetOps.c` | `wgtPaint()`, `wgtLayout()`, `wgtInitWindow()`, text get/set, invalidation |
| `widgetScrollbar.c` | Scrollbar drawing (H/V), thumb calculation, hit testing, drag update |
## Public Headers
| Header | Purpose |
|--------|---------|
| `dvxTypes.h` | All shared types: DisplayT, RectT, BlitOpsT, BevelStyleT, BitmapFontT, ColorSchemeT, WindowT, MenuT, ScrollbarT, CursorT, PopupStateT |
| `dvxVideo.h` | `videoInit()`, `videoShutdown()`, `packColor()`, `setClipRect()`, `resetClipRect()` |
| `dvxDraw.h` | All drawing functions: `rectFill()`, `drawBevel()`, `drawText()`, `drawTextN()`, `drawTermRow()`, etc. |
| `dvxComp.h` | Dirty list operations: `dirtyListAdd()`, `dirtyListMerge()`, `flushRect()`, `rectIntersect()` |
| `dvxWm.h` | Window management: `wmCreateWindow()`, `wmDestroyWindow()`, `wmRaiseWindow()`, menus, scrollbars, chrome |
| `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 |
| `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) |
| `dvxPalette.h` | Default 256-color VGA palette for 8-bit mode |
## Platform Layer
| File | Description |
|------|-------------|
| `platform/dvxPlatform.h` | Platform abstraction API (video, input, spans, DXE, crash recovery) |
| `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
libdvx.lib. Platform functions are exported to all DXE modules via
`platformRegisterDxeExports()`.
## Third-Party Libraries
| File | Description |
|------|-------------|
| `thirdparty/stb_image.h` | Image loading (implementation compiled into dvxImage.c) |
| `thirdparty/stb_image_write.h` | PNG writing (implementation compiled into dvxImageWrite.c) |
| `thirdparty/stb_ds.h` | Dynamic arrays/hash maps (implementation in loader, exported to all DXEs) |
## WidgetT Structure
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];
// Tree linkage
struct WidgetT *parent, *firstChild, *lastChild, *nextSibling;
WindowT *window;
// Geometry (relative to window content area)
int32_t x, y, w, h;
int32_t calcMinW, calcMinH; // computed minimum size
// Size hints (tagged: wgtPixels/wgtChars/wgtPercent, 0 = auto)
int32_t minW, minH, maxW, maxH;
int32_t prefW, prefH;
int32_t weight; // extra-space distribution (0 = fixed)
// Container properties
WidgetAlignE align;
int32_t spacing, padding; // tagged sizes
// Colors (0 = use color scheme defaults)
uint32_t fgColor, bgColor;
// State
bool visible, enabled, readOnly, focused;
char accelKey;
// User data and callbacks
void *userData;
void *data; // widget-private data (allocated by widget DXE)
const char *tooltip;
MenuT *contextMenu;
void (*onClick)(struct WidgetT *w);
void (*onDblClick)(struct WidgetT *w);
void (*onChange)(struct WidgetT *w);
void (*onFocus)(struct WidgetT *w);
void (*onBlur)(struct WidgetT *w);
} WidgetT;
```
## WidgetClassT Vtable
Each widget type defines a static WidgetClassT with flags and function pointers.
The vtable has 26 function slots plus a flags field:
| 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 |
### WidgetClassT Flags
| Flag | Value | Description |
|------|-------|-------------|
| `WCLASS_FOCUSABLE` | 0x0001 | Can receive keyboard focus |
| `WCLASS_BOX_CONTAINER` | 0x0002 | Uses VBox/HBox layout algorithm |
| `WCLASS_HORIZ_CONTAINER` | 0x0004 | Lays out children horizontally |
| `WCLASS_PAINTS_CHILDREN` | 0x0008 | Widget handles child rendering |
| `WCLASS_NO_HIT_RECURSE` | 0x0010 | Hit testing stops here |
| `WCLASS_FOCUS_FORWARD` | 0x0020 | Accel hit forwards focus to next focusable |
| `WCLASS_HAS_POPUP` | 0x0040 | Has dropdown popup overlay |
| `WCLASS_SCROLLABLE` | 0x0080 | Accepts mouse wheel events |
| `WCLASS_SCROLL_CONTAINER` | 0x0100 | Scroll container (ScrollPane) |
| `WCLASS_NEEDS_POLL` | 0x0200 | Needs periodic polling |
| `WCLASS_SWALLOWS_TAB` | 0x0400 | Tab key goes to widget, not focus nav |
| `WCLASS_RELAYOUT_ON_SCROLL` | 0x0800 | Full relayout on scrollbar drag |
| `WCLASS_PRESS_RELEASE` | 0x1000 | Click = press+release (buttons) |
| `WCLASS_ACCEL_WHEN_HIDDEN` | 0x2000 | Accel matching works when invisible |
## Widget Registration
Each widget DXE exports `wgtRegister()`, called by the loader after
`dlopen`. A typical registration:
```c
static int32_t sButtonType;
static const WidgetClassT sButtonClass = {
.flags = WCLASS_FOCUSABLE | WCLASS_PRESS_RELEASE,
.paint = buttonPaint,
.calcMinSize = buttonCalcMinSize,
.onMouse = buttonOnMouse,
.onKey = buttonOnKey,
.destroy = buttonDestroy,
.getText = buttonGetText,
.setText = buttonSetText,
.setPressed = buttonSetPressed,
.onAccelActivate = buttonAccelActivate,
};
static const ButtonApiT sApi = { .create = buttonCreate };
void wgtRegister(void) {
sButtonType = wgtRegisterClass(&sButtonClass);
wgtRegisterApi("button", &sApi);
}
```
## Per-Widget API Registry
The monolithic WidgetApiT is gone. Each widget registers a small API
struct under a name via `wgtRegisterApi()`. Callers retrieve it via
`wgtGetApi()` and cast to the widget-specific type. Per-widget headers
(e.g. `widgetButton.h`) provide typed accessors and convenience macros:
```c
// widgetButton.h
typedef struct {
WidgetT *(*create)(WidgetT *parent, const char *text);
} ButtonApiT;
static inline const ButtonApiT *dvxButtonApi(void) {
static const ButtonApiT *sApi;
if (!sApi) { sApi = (const ButtonApiT *)wgtGetApi("button"); }
return sApi;
}
#define wgtButton(parent, text) dvxButtonApi()->create(parent, text)
```
## Tagged Size Values
Size hints encode both unit type and numeric value in a single int32_t:
| Macro | Encoding | Example |
|-------|----------|---------|
| `wgtPixels(v)` | Bits 31:30 = 00 | `w->minW = wgtPixels(200);` |
| `wgtChars(v)` | Bits 31:30 = 01 | `w->minW = wgtChars(40);` |
| `wgtPercent(v)` | Bits 31:30 = 10 | `w->minW = wgtPercent(50);` |
| `0` | -- | Auto (use computed minimum) |
## Layout Algorithm
Two-pass flexbox-like layout:
1. **Bottom-up** (`calcMinSize`): Each widget computes its minimum size.
Containers sum children along the main axis, max across the cross axis.
2. **Top-down** (`layout`): Available space is allocated to children.
Extra space beyond minimum is distributed proportionally to weights.
`weight=0` means fixed size, `weight=100` is the default flexible weight.
## Exported Symbols
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*
```
## Build
```
make # builds bin/libs/libdvx.lib + bin/libs/libdvx.dep
make clean # removes objects and library
```
Depends on: `libtasks.lib` (via libdvx.dep).