DVX_GUI/dvx/README.md
2026-03-20 20:00:05 -05:00

1192 lines
39 KiB
Markdown

# DVX GUI Library (libdvx.a)
The core GUI compositor library for DVX. Provides VESA video setup,
2D drawing primitives, dirty-rectangle compositing, a full window
manager with Motif-style chrome, and a 32-type widget toolkit with
automatic layout. Applications include `dvxApp.h` (which pulls in all
lower layers) and optionally `dvxWidget.h` for the widget system.
## Architecture
The library is organized in five layers, each a `.h`/`.c` pair. Higher
layers depend on lower ones but never the reverse.
```
Layer 5 dvxApp Event loop, mouse/keyboard input, public API
Layer 4 dvxWm Window stack, chrome, drag, resize, focus, menus
Layer 3 dvxComp Dirty rectangle list, merge, LFB flush
Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops)
Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
dvxWidget Widget/layout system (optional, standalone)
```
## File Structure
### Core Layers
| File | Purpose |
|------|---------|
| `dvxVideo.h/.c` | Layer 1: VESA VBE mode negotiation, LFB mapping, system RAM backbuffer, pixel format discovery, color packing |
| `dvxDraw.h/.c` | Layer 2: Rectangle fills, bitmap blits, text rendering, bevels, lines, cursor/icon rendering |
| `dvxComp.h/.c` | Layer 3: Dirty rectangle tracking, merge, backbuffer-to-LFB flush |
| `dvxWm.h/.c` | Layer 4: Window lifecycle, Z-order stack, chrome drawing, hit testing, drag/resize/scroll |
| `dvxApp.h/.c` | Layer 5: AppContextT, event loop, window creation, color scheme, wallpaper, screenshots |
### Supporting Files
| File | Purpose |
|------|---------|
| `dvxTypes.h` | Shared type definitions used by all layers (PixelFormatT, DisplayT, WindowT, ColorSchemeT, etc.) |
| `dvxWidget.h` | Widget system public API (32 widget types, layout, events) |
| `dvxDialog.h/.c` | Modal dialogs (message box, file open/save) |
| `dvxPrefs.h/.c` | INI-based preferences system (read/write with typed accessors) |
| `dvxFont.h` | Embedded 8x16 VGA bitmap font glyph data (CP437, 256 glyphs) |
| `dvxCursor.h` | Mouse cursor bitmask data (5 shapes: arrow, resize H/V/NWSE/NESW) |
| `dvxPalette.h` | Default VGA palette for 8-bit mode |
| `dvxIcon.c` | stb_image implementation unit (BMP/PNG/JPEG/GIF loading) |
| `dvxImageWrite.c` | stb_image_write implementation unit (PNG export) |
### Platform Abstraction
| File | Purpose |
|------|---------|
| `platform/dvxPlatform.h` | OS/CPU-neutral interface: video, input, span ops, filesystem |
| `platform/dvxPlatformDos.c` | DOS/DJGPP: VESA, DPMI, INT 16h/33h, rep stosl/movsd asm spans |
To port DVX to a new platform, implement a new `dvxPlatformXxx.c`
against `platform/dvxPlatform.h` and swap it in the Makefile. No other
files need modification.
### Widget System
| File | Purpose |
|------|---------|
| `widgets/widgetInternal.h` | Shared internal header: vtable type, constants, cross-widget prototypes |
| `widgets/widgetClass.c` | Widget class table: one WidgetClassT vtable entry per type |
| `widgets/widgetCore.c` | Allocation, tree ops, hit testing, flag-based type queries |
| `widgets/widgetLayout.c` | Two-pass layout engine (measure + arrange) |
| `widgets/widgetEvent.c` | Mouse, keyboard, scroll, resize, and paint event dispatch |
| `widgets/widgetOps.c` | Paint dispatcher, public operations (wgtFind, wgtDestroy, etc.) |
| `widgets/widget*.c` | One file per widget type (button, checkbox, slider, etc.) |
Each widget type is self-contained in its own `.c` file. Dispatch for
paint, layout, mouse, keyboard, getText/setText, and destroy is driven
by a per-type vtable (WidgetClassT) rather than switch statements.
Adding a new widget type requires only a new `.c` file and an entry in
the class table.
### Third Party
| File | Purpose |
|------|---------|
| `thirdparty/stb_image.h` | Single-header image loader (BMP, PNG, JPEG, GIF) |
| `thirdparty/stb_image_write.h` | Single-header image writer (PNG) |
## Building
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
```bash
make # builds ../lib/libdvx.a
make clean # removes obj/ and lib/
```
Set `DJGPP_PREFIX` in the Makefile if your toolchain is installed
somewhere other than `~/djgpp/djgpp`.
---
## Quick Start
### Minimal window (raw drawing)
```c
#include "dvxApp.h"
static void onPaint(WindowT *win, RectT *dirty) {
AppContextT *ctx = (AppContextT *)win->userData;
const BlitOpsT *ops = dvxGetBlitOps(ctx);
const DisplayT *d = dvxGetDisplay(ctx);
uint32_t blue = packColor(d, 0, 0, 200);
for (int32_t y = 0; y < win->contentH; y++) {
ops->spanFill(win->contentBuf + y * win->contentPitch,
blue, win->contentW);
}
}
int main(void) {
AppContextT ctx;
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
return 1;
}
WindowT *win = dvxCreateWindow(&ctx, "Hello", 100, 100, 300, 200, true);
win->userData = &ctx;
win->onPaint = onPaint;
RectT r = {0, 0, win->contentW, win->contentH};
win->onPaint(win, &r);
dvxRun(&ctx);
dvxShutdown(&ctx);
return 0;
}
```
### Widget-based window
```c
#include "dvxApp.h"
#include "dvxWidget.h"
static void onButtonClick(WidgetT *w) {
WidgetT *root = w;
while (root->parent) root = root->parent;
WidgetT *lbl = wgtFind(root, "status");
if (lbl) {
wgtSetText(lbl, "Clicked!");
wgtInvalidate(lbl);
}
}
int main(void) {
AppContextT ctx;
dvxInit(&ctx, 1024, 768, 16);
WindowT *win = dvxCreateWindow(&ctx, "Widgets", 100, 100, 260, 200, true);
win->userData = &ctx;
WidgetT *root = wgtInitWindow(&ctx, win);
WidgetT *lbl = wgtLabel(root, "Ready.");
wgtSetName(lbl, "status");
wgtHSeparator(root);
WidgetT *row = wgtHBox(root);
wgtLabel(row, "Name:");
wgtTextInput(row, 64);
WidgetT *btn = wgtButton(root, "Go");
btn->onClick = onButtonClick;
wgtInvalidate(root);
dvxRun(&ctx);
dvxShutdown(&ctx);
return 0;
}
```
---
## Application API (dvxApp.h)
### Lifecycle
```c
int32_t dvxInit(AppContextT *ctx, int32_t requestedW,
int32_t requestedH, int32_t preferredBpp);
```
Initialize VESA video, input, fonts, color scheme, and cursors. Finds
a mode matching the requested resolution and bit depth. Returns 0 on
success, -1 on failure.
```c
void dvxRun(AppContextT *ctx);
```
Enter the main event loop. Handles mouse, keyboard, window management,
compositing, and LFB flush. Returns when `dvxQuit()` is called.
```c
bool dvxUpdate(AppContextT *ctx);
```
Process one iteration of the event loop. Returns `true` if still
running, `false` when exit requested. Use instead of `dvxRun()` when
embedding the GUI in an existing main loop.
```c
void dvxShutdown(AppContextT *ctx);
```
Restore text mode, release LFB mapping, free the backbuffer.
```c
void dvxQuit(AppContextT *ctx);
```
Request exit from the main loop.
### Video Mode
```c
int32_t dvxChangeVideoMode(AppContextT *ctx, int32_t requestedW,
int32_t requestedH, int32_t preferredBpp);
```
Switch to a new video mode live. Reallocates the backbuffer, all window
content buffers, repacks colors, rescales wallpaper, and repositions
windows. Returns 0 on success, -1 on failure (old mode restored).
```c
const VideoModeInfoT *dvxGetVideoModes(const AppContextT *ctx,
int32_t *count);
```
Return the list of available VESA modes (enumerated at init).
### Windows
```c
WindowT *dvxCreateWindow(AppContextT *ctx, const char *title,
int32_t x, int32_t y, int32_t w, int32_t h,
bool resizable);
WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title,
int32_t w, int32_t h, bool resizable);
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);
```
After creation, set `win->userData` and install callbacks:
| Callback | Signature | When Called |
|----------|-----------|------------|
| `onPaint` | `void (WindowT *win, RectT *dirtyArea)` | Content needs redrawing |
| `onKey` | `void (WindowT *win, int32_t key, int32_t mod)` | Key press (focused window) |
| `onMouse` | `void (WindowT *win, int32_t x, int32_t y, int32_t buttons)` | Mouse event in content area |
| `onResize` | `void (WindowT *win, int32_t newW, int32_t newH)` | Window resized by user |
| `onClose` | `void (WindowT *win)` | Close button double-clicked |
| `onMenu` | `void (WindowT *win, int32_t menuId)` | Menu item selected or accelerator triggered |
| `onScroll` | `void (WindowT *win, ScrollbarOrientE orient, int32_t value)` | Scrollbar moved |
Mouse/key coordinates are relative to the content area. `buttons` is
a bitmask: `MOUSE_LEFT` (1), `MOUSE_RIGHT` (2), `MOUSE_MIDDLE` (4).
Example:
```c
static void onClose(WindowT *win) {
AppContextT *ctx = (AppContextT *)win->userData;
dvxDestroyWindow(ctx, win);
}
WindowT *win = dvxCreateWindow(&ctx, "My Window", 50, 50, 400, 300, true);
win->userData = &ctx;
win->onClose = onClose;
win->onPaint = myPaintHandler;
```
### Window Properties
Set directly on WindowT after creation:
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `maxW` | `int32_t` | -1 | Maximum width when maximized (-1 = screen width) |
| `maxH` | `int32_t` | -1 | Maximum height when maximized (-1 = screen height) |
| `modal` | `bool` | false | When true, only this window receives input |
| `userData` | `void *` | NULL | Application data pointer |
### Window Operations
```c
void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title);
int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path);
void dvxMinimizeWindow(AppContextT *ctx, WindowT *win);
void dvxMaximizeWindow(AppContextT *ctx, WindowT *win);
void dvxFitWindow(AppContextT *ctx, WindowT *win);
```
`dvxFitWindow` resizes the window to exactly fit its widget tree's
computed minimum size (plus chrome). Useful for dialog boxes.
### Window Arrangement
```c
dvxCascadeWindows(&ctx); // staggered diagonal cascade
dvxTileWindows(&ctx); // NxM grid fill
dvxTileWindowsH(&ctx); // side-by-side, equal width
dvxTileWindowsV(&ctx); // stacked, equal height
```
### Invalidation
```c
void dvxInvalidateRect(AppContextT *ctx, WindowT *win,
int32_t x, int32_t y, int32_t w, int32_t h);
void dvxInvalidateWindow(AppContextT *ctx, WindowT *win);
```
Mark a region (or the entire content area) as needing repaint. The
compositor flushes dirty rectangles to the LFB on the next frame.
### Content Buffer
Each window has a persistent content backbuffer in display pixel format:
| Field | Description |
|-------|-------------|
| `contentBuf` | Pixel data in native display format |
| `contentPitch` | Bytes per scanline |
| `contentW` | Width in pixels |
| `contentH` | Height in pixels |
Paint callbacks write directly into `contentBuf`. The compositor copies
visible portions to the screen backbuffer, then flushes dirty rects to
the LFB.
### Clipboard
```c
dvxClipboardCopy("Hello", 5);
int32_t len;
const char *text = dvxClipboardGet(&len);
```
Process-wide static buffer. Adequate for copy/paste within DVX on
single-tasking DOS.
### Screenshots
```c
dvxScreenshot(&ctx, "screen.png"); // entire screen
dvxWindowScreenshot(&ctx, win, "window.png"); // single window content
```
Converts from native pixel format to RGB for the PNG encoder.
### Image Loading
```c
uint8_t *dvxLoadImage(const AppContextT *ctx, const char *path,
int32_t *outW, int32_t *outH, int32_t *outPitch);
void dvxFreeImage(uint8_t *data);
int32_t dvxSaveImage(const AppContextT *ctx, const uint8_t *data,
int32_t w, int32_t h, int32_t pitch, const char *path);
```
Load BMP/PNG/JPEG/GIF files and convert to the display's native pixel
format. Save native-format pixel data to PNG.
### Accessors
```c
DisplayT *dvxGetDisplay(AppContextT *ctx);
const BlitOpsT *dvxGetBlitOps(const AppContextT *ctx);
const BitmapFontT *dvxGetFont(const AppContextT *ctx);
const ColorSchemeT *dvxGetColors(const AppContextT *ctx);
```
### Mouse Configuration
```c
void dvxSetMouseConfig(AppContextT *ctx, int32_t wheelDir,
int32_t dblClickMs, int32_t accelThreshold);
```
`wheelDir`: 1 = normal, -1 = reversed.
`dblClickMs`: double-click speed in milliseconds.
`accelThreshold`: double-speed threshold in mickeys/sec (0 = unchanged).
---
## Color System
DVX uses a 20-color scheme that controls the entire UI appearance. All
colors are pre-packed into native pixel format at init time for
zero-cost per-pixel rendering.
### ColorSchemeT Fields
| Field | Usage |
|-------|-------|
| `desktop` | Desktop background fill |
| `windowFace` | Window frame fill |
| `windowHighlight` | Bevel light edge (top/left) |
| `windowShadow` | Bevel dark edge (bottom/right) |
| `activeTitleBg` / `activeTitleFg` | Focused window title bar |
| `inactiveTitleBg` / `inactiveTitleFg` | Unfocused window title bar |
| `contentBg` / `contentFg` | Window content area defaults |
| `menuBg` / `menuFg` | Menu bar and popup menus |
| `menuHighlightBg` / `menuHighlightFg` | Highlighted menu item |
| `buttonFace` | Push button interior |
| `scrollbarBg` / `scrollbarFg` / `scrollbarTrough` | Scrollbar elements |
| `cursorFg` / `cursorBg` | Mouse cursor colors |
### ColorIdE Enum
Each color has an integer ID for programmatic access:
```
ColorDesktopE, ColorWindowFaceE, ColorWindowHighlightE,
ColorWindowShadowE, ColorActiveTitleBgE, ColorActiveTitleFgE,
ColorInactiveTitleBgE, ColorInactiveTitleFgE, ColorContentBgE,
ColorContentFgE, ColorMenuBgE, ColorMenuFgE, ColorMenuHighlightBgE,
ColorMenuHighlightFgE, ColorButtonFaceE, ColorScrollbarBgE,
ColorScrollbarFgE, ColorScrollbarTroughE, ColorCursorFgE,
ColorCursorBgE, ColorCountE
```
### Color API
```c
void dvxSetColor(AppContextT *ctx, ColorIdE id, uint8_t r, uint8_t g, uint8_t b);
void dvxGetColor(const AppContextT *ctx, ColorIdE id, uint8_t *r, uint8_t *g, uint8_t *b);
void dvxApplyColorScheme(AppContextT *ctx);
void dvxResetColorScheme(AppContextT *ctx);
```
### Theme Files
Theme files are INI-format with a `[colors]` section containing RGB
values for each of the 20 color IDs:
```c
bool dvxLoadTheme(AppContextT *ctx, const char *filename);
bool dvxSaveTheme(const AppContextT *ctx, const char *filename);
const char *dvxColorName(ColorIdE id); // INI key name
const char *dvxColorLabel(ColorIdE id); // human-readable label
```
Bundled themes: `cde.thm`, `geos.thm`, `win31.thm`.
---
## Wallpaper System
```c
bool dvxSetWallpaper(AppContextT *ctx, const char *path);
void dvxSetWallpaperMode(AppContextT *ctx, WallpaperModeE mode);
```
Three display modes:
| Mode | Enum | Description |
|------|------|-------------|
| Stretch | `WallpaperStretchE` | Bilinear scaling to fill screen; ordered dithering for 16bpp |
| Tile | `WallpaperTileE` | Repeating pattern from top-left |
| Center | `WallpaperCenterE` | Centered with desktop color fill around edges |
The wallpaper is pre-rendered to screen dimensions in native pixel
format. Pass NULL to `dvxSetWallpaper` to clear. The wallpaper path is
stored so the image can be reloaded after a video mode change.
Supported formats: BMP, PNG, JPEG, GIF (via stb_image).
---
## Preferences System (dvxPrefs.h)
INI-based configuration with typed read/write accessors and
caller-supplied defaults.
```c
bool prefsLoad(const char *filename);
bool prefsSave(void);
bool prefsSaveAs(const char *filename);
void prefsFree(void);
const char *prefsGetString(const char *section, const char *key, const char *defaultVal);
int32_t prefsGetInt(const char *section, const char *key, int32_t defaultVal);
bool prefsGetBool(const char *section, const char *key, bool defaultVal);
void prefsSetString(const char *section, const char *key, const char *value);
void prefsSetInt(const char *section, const char *key, int32_t value);
void prefsSetBool(const char *section, const char *key, bool value);
void prefsRemove(const char *section, const char *key);
```
Only one file may be loaded at a time. If the file is missing or a key
is absent, getters return the caller-supplied default silently. Boolean
values recognize `true`/`yes`/`1` and `false`/`no`/`0`.
---
## Menu System
### Menu Bars
```c
MenuBarT *wmAddMenuBar(WindowT *win);
MenuT *wmAddMenu(MenuBarT *bar, const char *label);
void wmAddMenuItem(MenuT *menu, const char *label, int32_t id);
void wmAddMenuCheckItem(MenuT *menu, const char *label, int32_t id, bool checked);
void wmAddMenuRadioItem(MenuT *menu, const char *label, int32_t id, bool checked);
void wmAddMenuSeparator(MenuT *menu);
MenuT *wmAddSubMenu(MenuT *parentMenu, const char *label);
```
The `&` prefix marks accelerator keys -- `"&File"` means Alt+F opens
the menu. Up to 8 menus per bar, 16 items per menu, submenus nested up
to 4 levels deep.
After adding menus, call `wmUpdateContentRect(win)` and
`wmReallocContentBuf(win, &ctx.display)` to adjust the content area.
Example:
```c
MenuBarT *bar = wmAddMenuBar(win);
MenuT *fileMenu = wmAddMenu(bar, "&File");
wmAddMenuItem(fileMenu, "&New", CMD_NEW);
wmAddMenuItem(fileMenu, "&Open...", CMD_OPEN);
wmAddMenuSeparator(fileMenu);
wmAddMenuItem(fileMenu, "E&xit", CMD_EXIT);
MenuT *viewMenu = wmAddMenu(bar, "&View");
wmAddMenuCheckItem(viewMenu, "Tool&bar", CMD_TOOLBAR, true);
wmAddMenuRadioItem(viewMenu, "&Small", CMD_SMALL, false);
wmAddMenuRadioItem(viewMenu, "&Large", CMD_LARGE, true);
MenuT *zoomMenu = wmAddSubMenu(viewMenu, "&Zoom");
wmAddMenuItem(zoomMenu, "Zoom &In", CMD_ZOOM_IN);
wmAddMenuItem(zoomMenu, "Zoom &Out", CMD_ZOOM_OUT);
wmUpdateContentRect(win);
wmReallocContentBuf(win, &ctx.display);
```
### Context Menus
Right-click menus attached to windows or widgets:
```c
MenuT *ctxMenu = wmCreateMenu();
wmAddMenuItem(ctxMenu, "Cu&t", CMD_CUT);
wmAddMenuItem(ctxMenu, "&Copy", CMD_COPY);
wmAddMenuItem(ctxMenu, "&Paste", CMD_PASTE);
win->contextMenu = ctxMenu;
// Widget-level context menu
WidgetT *lb = wgtListBox(root);
lb->contextMenu = wmCreateMenu();
wmAddMenuItem(lb->contextMenu, "&Delete", CMD_DELETE);
```
Context menus route through the window's `onMenu` callback. The caller
owns the MenuT allocation (free with `wmFreeMenu()`).
### Accelerator Tables
Keyboard shortcuts routed through `onMenu`:
```c
AccelTableT *accel = dvxCreateAccelTable();
dvxAddAccel(accel, 'N', ACCEL_CTRL, CMD_NEW);
dvxAddAccel(accel, 'O', ACCEL_CTRL, CMD_OPEN);
dvxAddAccel(accel, 'S', ACCEL_CTRL, CMD_SAVE);
dvxAddAccel(accel, KEY_F1, 0, CMD_HELP);
win->accelTable = accel;
```
Modifier constants: `ACCEL_SHIFT` (0x03), `ACCEL_CTRL` (0x04),
`ACCEL_ALT` (0x08).
Key constants for extended keys: `KEY_F1`--`KEY_F12`, `KEY_INSERT`,
`KEY_DELETE`, `KEY_HOME`, `KEY_END`, `KEY_PGUP`, `KEY_PGDN`.
Up to 32 entries per table. Free with `dvxFreeAccelTable()`.
---
## Scrollbars
```c
ScrollbarT *wmAddVScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);
ScrollbarT *wmAddHScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);
```
After adding scrollbars, call `wmUpdateContentRect(win)` and
`wmReallocContentBuf(win, &ctx.display)`. The `onScroll` callback fires
when the user drags the thumb or clicks arrow buttons / trough.
Widget-managed windows handle their own scrollbars automatically -- do
not add them manually to widget windows.
---
## Dialogs (dvxDialog.h)
### Message Box
```c
int32_t result = dvxMessageBox(&ctx, "Confirm",
"Are you sure?", MB_YESNO | MB_ICONQUESTION);
if (result == ID_YES) {
dvxQuit(&ctx);
}
```
Button flags: `MB_OK`, `MB_OKCANCEL`, `MB_YESNO`, `MB_YESNOCANCEL`,
`MB_RETRYCANCEL`.
Icon flags: `MB_ICONINFO`, `MB_ICONWARNING`, `MB_ICONERROR`,
`MB_ICONQUESTION`.
Return values: `ID_OK`, `ID_CANCEL`, `ID_YES`, `ID_NO`, `ID_RETRY`.
### File Dialog
```c
static const FileFilterT filters[] = {
{"All Files (*.*)", "*.*"},
{"Text Files (*.txt)", "*.txt"},
{"Bitmap Files (*.bmp)", "*.bmp"}
};
char path[260];
if (dvxFileDialog(&ctx, "Open File", FD_OPEN, NULL,
filters, 3, path, sizeof(path))) {
// path contains the selected filename
}
```
Flags: `FD_OPEN`, `FD_SAVE`. Pass NULL for `initialDir` to use the
current directory. Filename validation is platform-specific (8.3 on DOS).
---
## Video and Drawing (dvxVideo.h, dvxDraw.h)
Lower-level APIs. Application code typically only needs `packColor`.
### Color Packing
```c
uint32_t packColor(const DisplayT *d, uint8_t r, uint8_t g, uint8_t b);
```
Pack RGB into the display's native pixel format. For 8-bit mode,
returns the nearest palette index.
### Clip Rectangle
```c
void setClipRect(DisplayT *d, int32_t x, int32_t y, int32_t w, int32_t h);
void resetClipRect(DisplayT *d);
```
### Rectangle Operations
```c
void rectFill(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
void rectCopy(DisplayT *d, const BlitOpsT *ops,
int32_t dstX, int32_t dstY, const uint8_t *srcBuf,
int32_t srcPitch, int32_t srcX, int32_t srcY,
int32_t w, int32_t h);
```
### Bevel Drawing
```c
void drawBevel(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h,
const BevelStyleT *style);
```
Convenience macros for common bevel styles:
- `BEVEL_RAISED(cs, bw)` -- standard raised (buttons, window frames)
- `BEVEL_SUNKEN(cs, face, bw)` -- sunken (text fields, list boxes)
- `BEVEL_TROUGH(cs)` -- scrollbar trough (1px sunken)
- `BEVEL_SB_BUTTON(cs)` -- scrollbar arrow button (1px raised)
### Text Rendering
```c
void drawText(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font,
int32_t x, int32_t y, const char *text,
uint32_t fg, uint32_t bg, bool opaque);
void drawTextN(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font,
int32_t x, int32_t y, const char *text, int32_t count,
uint32_t fg, uint32_t bg, bool opaque);
int32_t textWidth(const BitmapFontT *font, const char *text);
```
`drawTextN` is the fast path for bulk rendering -- computes clip bounds
once and fills the background in a single pass.
```c
void drawTextAccel(DisplayT *d, const BlitOpsT *ops,
const BitmapFontT *font, int32_t x, int32_t y,
const char *text, uint32_t fg, uint32_t bg, bool opaque);
int32_t textWidthAccel(const BitmapFontT *font, const char *text);
char accelParse(const char *text);
```
Text with `&` accelerator markers. The character after `&` is
underlined. `&&` produces a literal `&`.
### Terminal Row Rendering
```c
void drawTermRow(DisplayT *d, const BlitOpsT *ops,
const BitmapFontT *font, int32_t x, int32_t y,
int32_t cols, const uint8_t *lineData,
const uint32_t *palette, bool blinkVisible,
int32_t cursorCol);
```
Renders an entire 80-column terminal row in one call. `lineData` points
to `(char, attr)` byte pairs. Much faster than per-character rendering.
### Lines and Focus
```c
void drawHLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, uint32_t color);
void drawVLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t h, uint32_t color);
void drawFocusRect(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
```
`drawFocusRect` draws a dotted rectangle (alternating pixels) for
keyboard focus indicators.
---
## Compositor (dvxComp.h)
```c
void dirtyListInit(DirtyListT *dl);
void dirtyListAdd(DirtyListT *dl, int32_t x, int32_t y, int32_t w, int32_t h);
void dirtyListMerge(DirtyListT *dl);
void dirtyListClear(DirtyListT *dl);
void flushRect(DisplayT *d, const RectT *r);
bool rectIntersect(const RectT *a, const RectT *b, RectT *result);
bool rectIsEmpty(const RectT *r);
```
The compositing pipeline each frame:
1. Layers above call `dirtyListAdd()` for changed regions
2. `dirtyListMerge()` consolidates overlapping/adjacent rects
3. For each merged dirty rect, redraw desktop then each window
(back-to-front, painter's algorithm)
4. `flushRect()` copies each dirty rect from backBuf to the LFB
Up to 128 dirty rects per frame (`MAX_DIRTY_RECTS`).
---
## Window Manager (dvxWm.h)
### Window Stack
Windows are stored in an array of pointers ordered front-to-back: index
`count-1` is the topmost window. The compositor iterates back-to-front
for painting and front-to-back for hit testing.
```c
void wmInit(WindowStackT *stack);
WindowT *wmCreateWindow(WindowStackT *stack, DisplayT *d, const char *title,
int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
void wmDestroyWindow(WindowStackT *stack, WindowT *win);
void wmRaiseWindow(WindowStackT *stack, DirtyListT *dl, int32_t idx);
void wmSetFocus(WindowStackT *stack, DirtyListT *dl, int32_t idx);
```
### Hit Testing
```c
int32_t wmHitTest(const WindowStackT *stack, int32_t mx, int32_t my, int32_t *hitPart);
```
Returns the stack index and hit region:
`HIT_CONTENT`, `HIT_TITLE`, `HIT_CLOSE`, `HIT_RESIZE`, `HIT_MENU`,
`HIT_VSCROLL`, `HIT_HSCROLL`, `HIT_MINIMIZE`, `HIT_MAXIMIZE`,
`HIT_NONE`.
### Window Operations
```c
void wmMaximize(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);
void wmMinimize(WindowStackT *stack, DirtyListT *dl, WindowT *win);
void wmRestore(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);
void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, WindowT *win);
```
### System Menu
The system menu (control menu) appears on single-click of the close
gadget. Commands: Restore, Move, Size, Minimize, Maximize, Close,
Screenshot, Window Screenshot. Matches Windows 3.x behavior.
### Keyboard Window Management
| Key | Action |
|-----|--------|
| Alt+Tab | Cycle windows forward |
| Shift+Alt+Tab | Cycle windows backward |
| Alt+F4 | Close focused window |
| Alt+Space | Open system menu |
| F10 | Activate menu bar |
---
## Widget System (dvxWidget.h)
A retained-mode widget toolkit layered on top of the window manager.
Widgets form a tree rooted at a per-window VBox container. Layout is
automatic via a flexbox-like algorithm with weighted space distribution.
### Widget Catalog (32 types)
#### Containers
| Widget | Constructor | Description |
|--------|-------------|-------------|
| VBox | `wgtVBox(parent)` | Vertical stack layout |
| HBox | `wgtHBox(parent)` | Horizontal stack layout |
| Frame | `wgtFrame(parent, title)` | Titled groupbox with bevel border |
| TabControl | `wgtTabControl(parent)` | Tabbed page container with scrollable headers |
| TabPage | `wgtTabPage(tabs, title)` | Single page within a TabControl |
| ScrollPane | `wgtScrollPane(parent)` | Scrollable container with automatic scrollbars |
| Splitter | `wgtSplitter(parent, vertical)` | Draggable divider between two child regions |
| Toolbar | `wgtToolbar(parent)` | Horizontal raised container for toolbar buttons |
| StatusBar | `wgtStatusBar(parent)` | Horizontal sunken container for status text |
#### Basic Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| Label | `wgtLabel(parent, text)` | Static text display with optional alignment |
| Button | `wgtButton(parent, text)` | Beveled push button with press animation |
| Checkbox | `wgtCheckbox(parent, text)` | Toggle checkbox with checkmark glyph |
| RadioGroup | `wgtRadioGroup(parent)` | Container for mutually exclusive radio buttons |
| Radio | `wgtRadio(group, text)` | Individual radio button with diamond indicator |
#### Text Input Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| TextInput | `wgtTextInput(parent, maxLen)` | Single-line text field with selection, undo, clipboard |
| PasswordInput | `wgtPasswordInput(parent, maxLen)` | Bullet-masked text field (no copy/cut) |
| MaskedInput | `wgtMaskedInput(parent, mask)` | Formatted input (e.g. `"(###) ###-####"`) |
| TextArea | `wgtTextArea(parent, maxLen)` | Multi-line text editor with scrollbars |
#### Selection Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| ListBox | `wgtListBox(parent)` | Scrollable item list with optional multi-select and drag reorder |
| Dropdown | `wgtDropdown(parent)` | Non-editable selection with popup list |
| ComboBox | `wgtComboBox(parent, maxLen)` | Editable text field with dropdown suggestions |
| TreeView | `wgtTreeView(parent)` | Hierarchical tree with expand/collapse, multi-select, drag reorder |
| TreeItem | `wgtTreeItem(parent, text)` | Item node within a TreeView |
| ListView | `wgtListView(parent)` | Multi-column list with sortable/resizable headers, multi-select |
#### Value Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| Slider | `wgtSlider(parent, min, max)` | Draggable track bar (horizontal or vertical) |
| Spinner | `wgtSpinner(parent, min, max, step)` | Numeric up/down input with increment buttons |
| ProgressBar | `wgtProgressBar(parent)` | Percentage display bar (horizontal or vertical) |
#### Visual Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| Image | `wgtImage(parent, data, w, h, pitch)` | Static image display (BMP/PNG/JPEG/GIF) |
| ImageButton | `wgtImageButton(parent, data, w, h, pitch)` | Clickable image button with press animation |
| Canvas | `wgtCanvas(parent, w, h)` | Drawable bitmap surface with programmatic drawing API |
| Spacer | `wgtSpacer(parent)` | Invisible flexible space (weight=100) |
| Separator | `wgtHSeparator(parent)` / `wgtVSeparator(parent)` | Beveled divider line |
#### Special Widgets
| Widget | Constructor | Description |
|--------|-------------|-------------|
| AnsiTerm | `wgtAnsiTerm(parent, cols, rows)` | VT100/ANSI terminal emulator with 16-color CGA palette, scrollback, blink |
| Timer | `wgtTimer(parent, intervalMs, repeat)` | Invisible callback timer (one-shot or repeating) |
### Widget Event Model
All widget types share a universal set of event callbacks set directly
on the WidgetT struct:
| Callback | Signature | Description |
|----------|-----------|-------------|
| `onClick` | `void (WidgetT *w)` | Mouse click completed on the widget |
| `onDblClick` | `void (WidgetT *w)` | Double-click on the widget |
| `onChange` | `void (WidgetT *w)` | Value or state changed (checkbox toggle, text edit, slider move, timer fire) |
| `onFocus` | `void (WidgetT *w)` | Widget received keyboard focus |
| `onBlur` | `void (WidgetT *w)` | Widget lost keyboard focus |
Type-specific handlers (button press animation, listbox selection
highlight) run first, then these universal callbacks fire.
Additional per-widget fields: `userData` (void pointer), `tooltip`
(hover text), `contextMenu` (right-click menu).
### Initialization
```c
WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win);
```
Attach the widget system to a window. Returns the root VBox container.
Installs `onPaint`, `onMouse`, `onKey`, and `onResize` handlers. Build
the UI by adding child widgets to the returned root.
Call `wgtInvalidate(root)` after the tree is fully built to trigger the
initial layout and paint.
### Layout System
The layout engine runs in two passes:
1. **Measure** (bottom-up): compute minimum sizes for every widget
2. **Arrange** (top-down): distribute available space according to
weights, respecting min/max constraints
#### Size Specifications
Size fields (`minW`, `minH`, `maxW`, `maxH`, `prefW`, `prefH`,
`spacing`, `padding`) accept tagged values:
```c
wgtPixels(120) // 120 pixels
wgtChars(15) // 15 character widths (15 * 8 = 120 pixels)
wgtPercent(50) // 50% of parent dimension
```
A raw `0` means auto (use the widget's natural/computed size).
#### Weight
The `weight` field controls extra-space distribution among siblings:
- `weight = 0` -- fixed size, does not stretch (default for most widgets)
- `weight = 100` -- normal stretch (default for spacers, text inputs)
- Relative ratios work: weights of 100, 200, 100 give a 1:2:1 split
When all children have weight 0, the container's `align` property
(`AlignStartE`, `AlignCenterE`, `AlignEndE`) positions children within
the extra space.
#### Container Properties
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `align` | `WidgetAlignE` | `AlignStartE` | Main-axis alignment |
| `spacing` | `int32_t` | 0 (4px) | Tagged gap between children |
| `padding` | `int32_t` | 0 (4px) | Tagged internal padding |
### Widget Operations
```c
void wgtSetText(WidgetT *w, const char *text);
const char *wgtGetText(const WidgetT *w);
void wgtSetEnabled(WidgetT *w, bool enabled);
void wgtSetReadOnly(WidgetT *w, bool readOnly);
void wgtSetVisible(WidgetT *w, bool visible);
void wgtSetFocused(WidgetT *w);
WidgetT *wgtGetFocused(void);
void wgtSetName(WidgetT *w, const char *name);
WidgetT *wgtFind(WidgetT *root, const char *name);
void wgtDestroy(WidgetT *w);
void wgtInvalidate(WidgetT *w);
void wgtInvalidatePaint(WidgetT *w);
void wgtSetTooltip(WidgetT *w, const char *text);
void wgtSetDebugLayout(AppContextT *ctx, bool enabled);
AppContextT *wgtGetContext(const WidgetT *w);
```
- `wgtInvalidate` triggers full relayout + repaint (use after
structural changes)
- `wgtInvalidatePaint` triggers repaint only (use for visual-only
changes like checkbox toggle)
- `wgtFind` searches by name using djb2 hash for fast rejection
- `wgtGetContext` walks up the tree to retrieve the AppContextT
### Timer Widget
```c
WidgetT *wgtTimer(WidgetT *parent, int32_t intervalMs, bool repeat);
void wgtTimerStart(WidgetT *w);
void wgtTimerStop(WidgetT *w);
void wgtTimerSetInterval(WidgetT *w, int32_t intervalMs);
bool wgtTimerIsRunning(const WidgetT *w);
```
Invisible widget that fires `onChange` at the specified interval. Does
not participate in layout. Set `repeat = false` for one-shot timers
that auto-stop after the first fire. `wgtUpdateTimers()` is called
automatically by `dvxUpdate()` each frame.
### Layout and Paint Internals
Available for manual use when embedding in a custom event loop:
```c
int32_t wgtResolveSize(int32_t taggedSize, int32_t parentSize, int32_t charWidth);
void wgtLayout(WidgetT *root, int32_t availW, int32_t availH, const BitmapFontT *font);
void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops,
const BitmapFontT *font, const ColorSchemeT *colors);
```
---
## Font System
DVX uses a fixed-width 8x16 bitmap font covering the full IBM Code Page
437 character set (256 glyphs). The font data is compiled in as a static
array -- no external font files.
| Property | Value |
|----------|-------|
| Width | 8 pixels (fixed) |
| Height | 16 pixels (8x14 also available) |
| Encoding | CP437 (full 256 glyphs) |
| Format | 1 bit per pixel, MSB = leftmost, 16 bytes per glyph |
| Box drawing | Characters 176--223 (scrollbar arrows, window gadgets) |
Character positions are pure multiplication (`x = col * 8`), and each
glyph scanline is exactly one byte, enabling simple per-scanline
rendering without bit shifting across byte boundaries.
## Cursor System
Five software-rendered cursor shapes, stored as 16x16 AND/XOR bitmask
pairs (the standard IBM VGA hardware cursor format):
| ID | Constant | Shape |
|----|----------|-------|
| 0 | `CURSOR_ARROW` | Standard arrow pointer |
| 1 | `CURSOR_RESIZE_H` | Horizontal resize (left-right) |
| 2 | `CURSOR_RESIZE_V` | Vertical resize (up-down) |
| 3 | `CURSOR_RESIZE_DIAG_NWSE` | NW-SE diagonal resize |
| 4 | `CURSOR_RESIZE_DIAG_NESW` | NE-SW diagonal resize |
Cursors are painted in software into the backbuffer because VESA VBE
does not provide a hardware sprite. The affected region is flushed to
the LFB each frame.
## Window Chrome
Windows use a Motif/GEOS-style frame:
- 4px beveled outer border with perpendicular groove breaks
- 20px title bar (dark background when focused)
- Close button on the left edge (single-click opens system menu,
double-click closes)
- Minimize button on the right edge (always present)
- Maximize button to the left of minimize (resizable windows only)
- Optional menu bar below the title bar (20px)
- Optional scrollbars along the right and bottom edges (16px)
Minimized windows appear as 64x64 beveled icons along the bottom of the
screen. If a window icon is set via `dvxSetWindowIcon()`, that image is
shown; otherwise a scaled thumbnail of the window's content buffer is
used. Thumbnails refresh automatically one per frame (staggered).
Double-click an icon to restore.
## Platform Abstraction (platform/dvxPlatform.h)
All OS-specific code is behind a single interface. To port DVX,
implement these functions in a new `dvxPlatformXxx.c`:
| Function | Purpose |
|----------|---------|
| `platformInit()` | One-time setup (signal handlers, etc.) |
| `platformYield()` | Cooperative multitasking yield |
| `platformVideoInit()` | Set up video mode, LFB, backbuffer |
| `platformVideoShutdown()` | Restore text mode, free resources |
| `platformVideoEnumModes()` | Enumerate available video modes |
| `platformVideoSetPalette()` | Set 8-bit palette entries |
| `platformVideoFreeBuffers()` | Free backbuffer without restoring text mode |
| `platformFlushRect()` | Copy rectangle from backbuffer to LFB |
| `platformSpanFill8/16/32()` | Optimized pixel fill (rep stosl on DOS) |
| `platformSpanCopy8/16/32()` | Optimized pixel copy (rep movsd on DOS) |
| `platformMouseInit()` | Initialize mouse driver |
| `platformMousePoll()` | Read mouse position and buttons |
| `platformMouseWheelInit()` | Detect and activate mouse wheel |
| `platformMouseWheelPoll()` | Read wheel delta |
| `platformMouseWarp()` | Move cursor to absolute position |
| `platformMouseSetAccel()` | Set double-speed threshold |
| `platformKeyboardGetModifiers()` | Read shift/ctrl/alt state |
| `platformKeyboardRead()` | Non-blocking key read |
| `platformAltScanToChar()` | Map Alt+scancode to ASCII |
| `platformValidateFilename()` | Check filename for OS constraints |
| `platformGetSystemInfo()` | Gather hardware info string |
| `platformGetMemoryInfo()` | Query total/free RAM |
| `platformMkdirRecursive()` | Create directory tree |
| `platformChdir()` | Change directory (with drive support on DOS) |
| `platformPathDirEnd()` | Find last path separator |
| `platformLineEnding()` | Native line ending string |
| `platformStripLineEndings()` | Remove CR from CR+LF pairs |
## Key Constants (dvxTypes.h)
### Extended Key Codes
Non-ASCII keys encoded as `(scancode | 0x100)`:
```
KEY_F1 through KEY_F12
KEY_INSERT, KEY_DELETE
KEY_HOME, KEY_END
KEY_PGUP, KEY_PGDN
```
### Chrome Constants
```
CHROME_BORDER_WIDTH 4 Border thickness
CHROME_TITLE_HEIGHT 20 Title bar height
CHROME_MENU_HEIGHT 20 Menu bar height
CHROME_CLOSE_BTN_SIZE 16 Close gadget size
SCROLLBAR_WIDTH 16 Scrollbar track width
MAX_WINDOWS 64 Maximum simultaneous windows
MAX_DIRTY_RECTS 128 Dirty rects per frame
```
### Mouse Constants
```
MOUSE_LEFT 1 Left button bitmask
MOUSE_RIGHT 2 Right button bitmask
MOUSE_MIDDLE 4 Middle button bitmask
MOUSE_WHEEL_STEP 3 Lines per wheel notch
```
## Hardware Requirements
- 486 or later CPU
- VESA VBE 2.0+ compatible video card with linear framebuffer
- PS/2 mouse (or compatible driver)
- DPMI host (CWSDPMI, Windows DOS box, DOSBox, 86Box)
Tested on 86Box with PCI video cards.