298 lines
13 KiB
Markdown
298 lines
13 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 |
|
|
| `dragSelect` | `(w, root, vx, vy)` | Handle drag selection |
|
|
| `openPopup` | `(w)` | Open dropdown popup |
|
|
| `closePopup` | `(w)` | Close dropdown popup |
|
|
| `getPopupItemCount` | `(w)` | Return number of popup items |
|
|
| `getPopupRect` | `(w, font, contentH, popX, popY, popW, popH)` | Compute popup rectangle |
|
|
| `setPressed` | `(w, pressed)` | Set button press visual state |
|
|
| `reorderDrop` | `(w)` | Complete drag-reorder operation |
|
|
| `reorderUpdate` | `(w, root, x, y)` | Update drag-reorder position |
|
|
| `getCursorShape` | `(w, vx, vy)` | Return cursor ID for this position |
|
|
| `poll` | `(w, win)` | Periodic polling (AnsiTerm comms) |
|
|
| `quickRepaint` | `(w, outY, outH)` | Fast incremental repaint |
|
|
| `getTextFieldWidth` | `(w)` | Text field width for scroll calc |
|
|
| `scrollDragUpdate` | `(w, orient, dragOff, mouseX, mouseY)` | Scrollbar drag update |
|
|
|
|
### 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).
|