DVX_GUI/dvx/README.md
2026-03-16 00:28:36 -05:00

1117 lines
39 KiB
Markdown

# DV/X GUI
A DESQview/X-style windowed GUI compositor for DOS, targeting DJGPP/DPMI with
VESA VBE 2.0+ linear framebuffer.
Motif-style beveled chrome, dirty-rectangle compositing, draggable and
resizable windows, dropdown menus, scrollbars, and a declarative widget/layout
system with buttons, checkboxes, radios, text inputs, dropdowns, combo boxes,
sliders, spinners, progress bars, tab controls, tree views, list views,
scroll panes, toolbars, status bars, images, image buttons, drawable canvases,
password inputs, masked/formatted inputs, and an ANSI BBS terminal emulator.
## Building
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
The GUI is built as a static library (`lib/libdvx.a`). The demo links
against it.
```
cd dvxdemo
make # builds lib/libdvx.a then bin/demo.exe
make clean # removes obj/ and bin/
```
Set `DJGPP_PREFIX` in the Makefile if your toolchain is installed somewhere
other than `~/djgpp/djgpp`.
## Architecture
The library is organized in five layers. Each layer is a `.h`/`.c` pair.
Application code only needs to include `dvxApp.h` (which pulls in the rest)
and optionally `dvxWidget.h`.
```
Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops)
Layer 3 dvxComp Dirty rectangle list, merge, LFB flush
Layer 4 dvxWm Window stack, chrome, drag, resize, focus, menus, scrollbars
Layer 5 dvxApp Event loop, mouse/keyboard input, public API
dvxWidget Widget/layout system (optional, standalone)
```
Supporting files:
| File | Purpose |
|------|---------|
| `dvxTypes.h` | Shared type definitions used by all layers |
| `dvxFont.h` | Built-in 8x14 bitmap font glyph data |
| `dvxCursor.h` | Mouse cursor bitmask data (5 shapes) |
| `dvxPalette.h` | Default VGA palette for 8-bit mode |
| `dvxIcon.c` | stb_image implementation unit (BMP/PNG/JPEG/GIF) |
| `dvxImageWrite.c` | stb_image_write implementation unit (PNG export) |
| `thirdparty/stb_image.h` | Third-party single-header image loader |
| `thirdparty/stb_image_write.h` | Third-party single-header image writer |
The widget system lives in `widgets/`:
| 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 widget 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 in
the core files. Adding a new widget type requires only a new `.c` file
and an entry in the class table.
## Quick start
```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;
}
```
## Quick start (widgets)
```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.");
strncpy(lbl->name, "status", MAX_WIDGET_NAME);
wgtHSeparator(root);
WidgetT *row = wgtHBox(root);
wgtLabel(row, "Name:");
wgtTextInput(row, 64);
WidgetT *btn = wgtButton(root, "Go");
btn->onClick = onButtonClick;
wgtInvalidate(root); // initial layout + paint
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 (8, 15, 16, or 32). Returns
0 on success, -1 on failure.
```c
void dvxRun(AppContextT *ctx);
```
Enter the main event loop. Handles mouse movement, button clicks, keyboard
input, window management, dirty-rectangle compositing, and LFB flush.
Returns when `dvxQuit()` is called or ESC is pressed.
```c
bool dvxUpdate(AppContextT *ctx);
```
Process one iteration of the event loop: poll input, dispatch events,
composite dirty regions, and flush. Returns `true` if the GUI is still
running, `false` when exit has been requested. Use this instead of
`dvxRun()` when embedding the GUI inside an existing main loop.
```c
void dvxShutdown(AppContextT *ctx);
```
Restore text mode, release LFB mapping, and free the backbuffer.
```c
void dvxQuit(AppContextT *ctx);
```
Request exit from the main loop. `dvxRun()` returns on the next iteration.
### Windows
```c
WindowT *dvxCreateWindow(AppContextT *ctx, const char *title,
int32_t x, int32_t y, int32_t w, int32_t h,
bool resizable);
```
Create a window at screen position (`x`, `y`) with outer dimensions `w` x `h`.
If `resizable` is true, the window gets resize handles and a maximize button.
The window is raised to the top and given focus. Returns NULL on failure.
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 |
| `onClose` | `void (WindowT *win)` | Close button double-clicked |
| `onMenu` | `void (WindowT *win, int32_t menuId)` | Menu item selected |
| `onScroll` | `void (WindowT *win, ScrollbarOrientE orient, int32_t value)` | Scrollbar moved |
Mouse/key coordinates are relative to the content area. `buttons` is a
bitmask (bit 0 = left, bit 1 = right, bit 2 = middle).
```c
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);
```
Remove a window from the stack and free its resources.
```c
void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title);
```
Change the title bar text.
```c
int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path);
```
Load a BMP, TGA, or PNG image and assign it as the window's minimized icon.
The image is converted to the display pixel format and scaled to 64x64.
Returns 0 on success.
### 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.
### Accessors
```c
DisplayT *dvxGetDisplay(AppContextT *ctx);
const BlitOpsT *dvxGetBlitOps(const AppContextT *ctx);
const BitmapFontT *dvxGetFont(const AppContextT *ctx);
const ColorSchemeT *dvxGetColors(const AppContextT *ctx);
```
### Window properties
Set these directly on the `WindowT` struct 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) |
| `userData` | `void *` | NULL | Application data pointer, passed through to callbacks |
### Content buffer
Each window has a persistent content backbuffer:
| Field | Description |
|-------|-------------|
| `contentBuf` | Pixel data in 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.
---
## Menu bars
```c
MenuBarT *wmAddMenuBar(WindowT *win);
```
Add a menu bar to a window. Call `wmUpdateContentRect()` and
`wmReallocContentBuf()` after adding the menu bar (the menu bar reduces
the content area).
```c
MenuT *wmAddMenu(MenuBarT *bar, const char *label);
```
Add a top-level menu to the bar. Returns a `MenuT *` to populate with items.
```c
void wmAddMenuItem(MenuT *menu, const char *label, int32_t id);
void wmAddMenuSeparator(MenuT *menu);
```
Add items to a menu. `id` is an application-defined command passed to
`onMenu`. Up to 16 items per menu, 8 menus per bar.
---
## 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);
```
Add a scrollbar to a window. `pageSize` controls the thumb size relative to
the range. Call `wmUpdateContentRect()` and `wmReallocContentBuf()` afterward.
The `onScroll` callback fires when the user drags the thumb or clicks the
arrow buttons / trough.
Widget windows manage their own scrollbars automatically -- do not add them
manually.
---
## Video and drawing (`dvxVideo.h`, `dvxDraw.h`)
These are lower-level APIs. Application code typically only needs `packColor`.
```c
uint32_t packColor(const DisplayT *d, uint8_t r, uint8_t g, uint8_t b);
```
Pack an RGB triple into the display's pixel format.
```c
void rectFill(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
```
Fill a rectangle with a solid color (clipped to the display clip rect).
```c
void drawBevel(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h,
const BevelStyleT *style);
```
Draw a beveled frame. `BevelStyleT` specifies highlight, shadow, face colors
and border width.
```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);
int32_t drawChar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font,
int32_t x, int32_t y, char ch,
uint32_t fg, uint32_t bg, bool opaque);
int32_t textWidth(const BitmapFontT *font, const char *text);
```
Draw text using the built-in 8-pixel-wide bitmap font. `opaque` controls
whether background pixels are drawn. `drawChar` returns the advance width.
```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);
```
Bulk-render a row of terminal character cells. `lineData` points to
`(char, attr)` byte pairs (2 bytes per cell). `palette` is a 16-entry
packed-color table. `blinkVisible` controls blink attribute visibility.
`cursorCol` is the column to draw inverted (-1 for no cursor). This is
much faster than calling `drawChar` per cell because clip bounds are
computed once for the whole row and there is no per-character function
call overhead.
```c
char accelParse(const char *text);
```
Parse an accelerator key from text with `&` markers (e.g. `"E&xit"` returns
`'x'`). Returns the lowercase accelerator character, or 0 if none found.
```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);
```
Draw/measure text with `&` accelerator processing. The character after `&`
is underlined. `&&` produces a literal `&`. `textWidthAccel` returns the
width in pixels, excluding `&` markers.
```c
void drawFocusRect(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h,
uint32_t color);
```
Draw a dotted rectangle outline (every other pixel). Used to indicate
keyboard focus on widgets.
```c
void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops,
int32_t x, int32_t y, int32_t w, int32_t h,
const uint16_t *andMask, const uint16_t *xorData,
uint32_t fgColor, uint32_t bgColor);
```
Draw a 1-bit bitmap with AND/XOR masks (used for mouse cursors). Each row
is a `uint16_t`. Pixels where the AND mask is 0 are drawn using the XOR
data to select `fgColor` or `bgColor`.
```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);
```
```c
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);
```
Blit from a source buffer to the backbuffer with clipping.
```c
void setClipRect(DisplayT *d, int32_t x, int32_t y, int32_t w, int32_t h);
void resetClipRect(DisplayT *d);
```
---
## Widget system (`dvxWidget.h`)
An optional declarative layout system inspired by Amiga MUI and PC GEOS.
Widgets are arranged in a tree of VBox/HBox containers. A two-pass layout
engine (measure minimum sizes, then distribute space) handles geometry
automatically. Scrollbars appear when the window is too small for the
content.
### Initialization
```c
WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win);
```
Attach the widget system to a window. Returns the root container (a VBox
filling the content area). Installs `onPaint`, `onMouse`, `onKey`, and
`onResize` handlers on the window. Build the widget tree by passing the
returned root (or its children) as `parent` to the widget creation
functions below.
Call `wgtInvalidate(root)` after the tree is fully built to trigger the
initial layout and paint.
### Containers
```c
WidgetT *wgtVBox(WidgetT *parent); // vertical stack
WidgetT *wgtHBox(WidgetT *parent); // horizontal stack
WidgetT *wgtFrame(WidgetT *parent, const char *title); // titled border
```
Containers hold child widgets and control layout direction. `wgtFrame`
draws a styled border with a title label and lays out children vertically.
Frame styles (set `w->as.frame.style` after creation):
| Style | Description |
|-------|-------------|
| `FrameInE` | Beveled inward / sunken (default) |
| `FrameOutE` | Beveled outward / raised |
| `FrameFlatE` | Solid color line; set `w->as.frame.color` (0 = windowShadow) |
The title text is vertically centered on the top border line.
Container properties (set directly on the returned `WidgetT *`):
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `align` | `WidgetAlignE` | `AlignStartE` | Main-axis alignment when no children have weight |
| `spacing` | `int32_t` | 0 (4px) | Tagged size: gap between children |
| `padding` | `int32_t` | 0 (4px) | Tagged size: internal padding |
Alignment values:
| Value | HBox meaning | VBox meaning |
|-------|-------------|-------------|
| `AlignStartE` | left | top |
| `AlignCenterE` | center | center |
| `AlignEndE` | right | bottom |
### Widgets
```c
WidgetT *wgtLabel(WidgetT *parent, const char *text);
```
Static text label. Sized to fit its text.
```c
WidgetT *wgtButton(WidgetT *parent, const char *text);
```
Beveled push button with standard press/release behavior: the button
visually depresses on mouse-down, tracks the cursor while held, and
fires `onClick` on release if still over the button. Dragging off the
button cancels the click.
```c
WidgetT *wgtCheckbox(WidgetT *parent, const char *text);
```
Toggle checkbox. Read/write `w->as.checkbox.checked`. Set `onChange` to
be notified.
```c
WidgetT *wgtRadioGroup(WidgetT *parent);
WidgetT *wgtRadio(WidgetT *parent, const char *text);
```
Radio buttons with diamond-shaped indicators (visually distinct from
checkboxes). Create a group, then add options as children. The group
tracks the selected index in `w->as.radioGroup.selectedIdx`. Set
`onChange` on the group to be notified. Radio indices are auto-assigned.
```c
WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen);
```
Single-line text input field. `maxLen` is the buffer capacity. Default
weight is 100 (stretches to fill). Set `onChange` to be notified on edits.
Editing features: cursor movement (arrows, Home, End), insert/overwrite,
selection (Shift+arrows, Shift+Home/End, Ctrl+A, double-click word,
triple-click all), clipboard (Ctrl+C copy, Ctrl+V paste, Ctrl+X cut),
single-level undo (Ctrl+Z), and horizontal scrolling when text exceeds
the visible width. Mouse drag selection auto-scrolls when the cursor
reaches the edge of the field.
```c
WidgetT *wgtPasswordInput(WidgetT *parent, int32_t maxLen);
```
Password input field. Identical to `wgtTextInput` except characters are
displayed as bullets (CP437 `\xF9`). Copy and cut to clipboard are
disabled; paste is allowed. All other editing features work normally.
```c
WidgetT *wgtMaskedInput(WidgetT *parent, const char *mask);
```
Formatted input field with a fixed mask pattern. The mask string defines
the format: `#` accepts a digit, `A` accepts a letter, `*` accepts any
printable character. All other characters in the mask are literals that
are displayed but cannot be edited. The cursor skips over literal
positions automatically.
Example masks:
- `"(###) ###-####"` -- US phone number
- `"##/##/####"` -- date
- `"###-##-####"` -- SSN
The buffer always contains the full formatted text including literals.
`wgtGetText()` returns the formatted string. Supports selection
(Shift+arrows, Ctrl+A), clipboard (Ctrl+C, Ctrl+V, Ctrl+X), and
single-level undo (Ctrl+Z). The mask string must remain valid for the
widget's lifetime.
```c
WidgetT *wgtListBox(WidgetT *parent);
```
List box (basic -- set items with `wgtListBoxSetItems()`).
```c
WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen);
```
Multi-line text editor with vertical and horizontal scrollbars. Supports
all the same editing features as `wgtTextInput` plus multi-line selection
(Shift+Up/Down), Enter for newlines, and vertical/horizontal auto-scroll
during mouse drag selection. A horizontal scrollbar appears automatically
when any line exceeds the visible width. The vertical scrollbar is always
present.
### Dropdown and ComboBox
```c
WidgetT *wgtDropdown(WidgetT *parent);
void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count);
int32_t wgtDropdownGetSelected(const WidgetT *w);
void wgtDropdownSetSelected(WidgetT *w, int32_t idx);
```
Non-editable selection widget. Clicking the arrow button opens a popup
list below (or above if no room). Clicking the arrow again while the
popup is open closes it. Set `onChange` to be notified of selection
changes. The items array must remain valid for the widget's lifetime.
```c
WidgetT *wgtComboBox(WidgetT *parent, int32_t maxLen);
void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count);
int32_t wgtComboBoxGetSelected(const WidgetT *w);
void wgtComboBoxSetSelected(WidgetT *w, int32_t idx);
```
Editable text field with a dropdown list. The text area accepts keyboard
input (same editing keys as `wgtTextInput`). Clicking the arrow button
toggles the popup; clicking the text area focuses it for editing.
Selecting an item copies its text into the edit buffer. Default weight
is 100.
### ProgressBar and Slider
```c
WidgetT *wgtProgressBar(WidgetT *parent);
void wgtProgressBarSetValue(WidgetT *w, int32_t value);
int32_t wgtProgressBarGetValue(const WidgetT *w);
```
Horizontal progress indicator. Value is 0--100 (percentage). The filled
portion draws left-to-right with the system highlight color.
```c
WidgetT *wgtSlider(WidgetT *parent, int32_t minVal, int32_t maxVal);
void wgtSliderSetValue(WidgetT *w, int32_t value);
int32_t wgtSliderGetValue(const WidgetT *w);
```
Draggable slider/trackbar. Horizontal by default; set
`w->as.slider.vertical = true` after creation for vertical orientation.
The thumb tracks the mouse while held. Set `onChange` to be notified.
### Spinner
```c
WidgetT *wgtSpinner(WidgetT *parent, int32_t minVal, int32_t maxVal, int32_t step);
void wgtSpinnerSetValue(WidgetT *w, int32_t value);
int32_t wgtSpinnerGetValue(const WidgetT *w);
void wgtSpinnerSetRange(WidgetT *w, int32_t minVal, int32_t maxVal);
void wgtSpinnerSetStep(WidgetT *w, int32_t step);
```
Numeric up/down input field with increment/decrement buttons. The text
area accepts direct keyboard entry of digits (and minus sign when the
minimum is negative). Up/Down arrows increment/decrement by `step`;
Page Up/Page Down by `step * 10`. Enter commits the typed value; Escape
reverts. The value is clamped to `[minVal, maxVal]` on every change.
Supports all single-line text editing features: cursor movement, selection
(Shift+arrows, Ctrl+A, double/triple-click), clipboard (Ctrl+C/V/X),
and single-level undo (Ctrl+Z). Non-numeric input is filtered out.
The widget has a sunken text area with up/down arrow buttons on the right.
Clicking the top half of the button area increments; the bottom half
decrements. Set `onChange` to be notified of value changes.
### TabControl
```c
WidgetT *wgtTabControl(WidgetT *parent);
WidgetT *wgtTabPage(WidgetT *parent, const char *title);
void wgtTabControlSetActive(WidgetT *w, int32_t idx);
int32_t wgtTabControlGetActive(const WidgetT *w);
```
Tabbed container. Create a tab control, then add pages as children.
Each page is a VBox that holds its own widget subtree. Only the active
page is visible and receives layout; inactive pages are hidden. Clicking
a tab header switches the active page.
### StatusBar and Toolbar
```c
WidgetT *wgtStatusBar(WidgetT *parent);
WidgetT *wgtToolbar(WidgetT *parent);
```
Horizontal containers with specialized chrome. StatusBar draws a sunken
panel background; Toolbar draws a raised background. Both use horizontal
layout with default spacing. Add child widgets (labels, buttons, etc.)
as with any HBox. StatusBar children with `weight > 0` stretch to fill.
### TreeView
```c
WidgetT *wgtTreeView(WidgetT *parent);
WidgetT *wgtTreeItem(WidgetT *parent, const char *text);
void wgtTreeItemSetExpanded(WidgetT *w, bool expanded);
bool wgtTreeItemIsExpanded(const WidgetT *w);
WidgetT *wgtTreeViewGetSelected(const WidgetT *w);
void wgtTreeViewSetSelected(WidgetT *w, WidgetT *item);
```
Hierarchical tree with expand/collapse support. Create a tree view, then
add items as children. Nest items by adding them as children of other
items. Items with children show a `[+]`/`[-]` expand box.
The tree view manages its own internal scrollbars: a vertical scrollbar
appears when expanded items exceed the visible height, and a horizontal
scrollbar appears when item text extends beyond the visible width. Both
scrollbars have arrow buttons, proportional thumbs, and page
scrolling on trough clicks.
Default weight is 100. Set `onClick` on individual tree items to handle
selection, or `onChange` to be notified of expand/collapse.
`wgtTreeViewGetSelected` returns the currently selected tree item (or NULL).
`wgtTreeViewSetSelected` sets the selection programmatically.
### Image
```c
WidgetT *wgtImage(WidgetT *parent, uint8_t *data,
int32_t w, int32_t h, int32_t pitch);
WidgetT *wgtImageFromFile(WidgetT *parent, const char *path);
void wgtImageSetData(WidgetT *w, uint8_t *data,
int32_t imgW, int32_t imgH, int32_t pitch);
```
Display a bitmap image. `wgtImage` takes ownership of the pixel buffer
(freed on destroy). `wgtImageFromFile` loads any format supported by
stb_image and converts to the display pixel format. Set `onClick` to
make the image clickable.
### ImageButton
```c
WidgetT *wgtImageButton(WidgetT *parent, uint8_t *data,
int32_t w, int32_t h, int32_t pitch);
void wgtImageButtonSetData(WidgetT *w, uint8_t *data,
int32_t imgW, int32_t imgH, int32_t pitch);
```
Push button that displays an image instead of a text caption. Behaves
identically to `wgtButton` -- visually depresses on mouse-down, tracks the
cursor while held, and fires `onClick` on release if still over the button.
The image is centered on the button face with a 2px bevel border and no
extra padding. When pressed, the image shifts 1px down and right.
`wgtImageButton` takes ownership of the pixel buffer (freed on destroy).
`wgtImageButtonSetData` replaces the image data, freeing the previous buffer.
The pixel buffer must be in display pixel format (use `packColor()` and the
display's `bytesPerPixel` to prepare it).
### Canvas
```c
WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h);
```
Drawable bitmap canvas with a sunken bevel border. Supports both
interactive freehand drawing (mouse strokes are Bresenham-interpolated)
and programmatic drawing from the API.
```c
void wgtCanvasClear(WidgetT *w, uint32_t color);
void wgtCanvasSetPenColor(WidgetT *w, uint32_t color);
void wgtCanvasSetPenSize(WidgetT *w, int32_t size);
int32_t wgtCanvasSave(WidgetT *w, const char *path);
int32_t wgtCanvasLoad(WidgetT *w, const char *path);
```
`wgtCanvasSave` writes the canvas to a PNG file (converting from display
pixel format to RGB). `wgtCanvasLoad` reads any image format supported by
stb_image and converts it to display pixel format.
Programmatic drawing primitives (all coordinates are in canvas space):
```c
void wgtCanvasSetPixel(WidgetT *w, int32_t x, int32_t y, uint32_t color);
uint32_t wgtCanvasGetPixel(const WidgetT *w, int32_t x, int32_t y);
void wgtCanvasDrawLine(WidgetT *w, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
void wgtCanvasDrawRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
void wgtCanvasFillRect(WidgetT *w, int32_t x, int32_t y, int32_t width, int32_t height);
void wgtCanvasFillCircle(WidgetT *w, int32_t cx, int32_t cy, int32_t radius);
```
`SetPixel`/`GetPixel` take explicit colors. `DrawLine` uses the current
pen color and pen size. `DrawRect` draws a 1px outline, `FillRect` fills
solid, and `FillCircle` fills a circle -- all using the current pen
color. All operations clip to the canvas bounds. Colors are in display
pixel format (use `packColor()` to create them).
### ANSI Terminal
```c
WidgetT *wgtAnsiTerm(WidgetT *parent, int32_t cols, int32_t rows);
```
ANSI BBS terminal emulator widget. Displays a character grid (default
80x25 if cols/rows are 0) with full ANSI escape sequence support and a
16-color CGA palette. The terminal has a 2px sunken bevel border and a
vertical scrollbar for scrollback history. Supports mouse text selection
(click and drag), Ctrl+C to copy selected text (sends `^C` to the
terminal when nothing is selected), and Ctrl+V to paste clipboard text
through the comm interface.
ANSI escape sequences supported:
| Sequence | Description |
|----------|-------------|
| `ESC[H` / `ESC[f` | Cursor position (CUP/HVP) |
| `ESC[A/B/C/D` | Cursor up/down/forward/back |
| `ESC[J` | Erase display (0=to end, 1=to start, 2=all) |
| `ESC[K` | Erase line (0=to end, 1=to start, 2=all) |
| `ESC[m` | SGR: colors 30-37/40-47, bright 90-97/100-107, bold(1), blink(5), reverse(7), reset(0) |
| `ESC[s` / `ESC[u` | Save / restore cursor position |
| `ESC[S` / `ESC[T` | Scroll up / down |
| `ESC[L` / `ESC[M` | Insert / delete lines |
| `ESC[?7h/l` | Enable / disable auto-wrap |
| `ESC[?25h/l` | Show / hide cursor |
Control characters: CR, LF, BS, TAB, BEL (ignored).
```c
void wgtAnsiTermWrite(WidgetT *w, const uint8_t *data, int32_t len);
```
Feed data through the ANSI parser. Use this to display `.ANS` files or
inject content without a communications link.
```c
void wgtAnsiTermClear(WidgetT *w);
```
Clear the screen, push all visible lines to scrollback, and reset the
cursor to the home position.
```c
void wgtAnsiTermSetComm(WidgetT *w, void *ctx,
int32_t (*readFn)(void *, uint8_t *, int32_t),
int32_t (*writeFn)(void *, const uint8_t *, int32_t));
```
Set the communications interface. `readFn` should return bytes read
(0 if none available). `writeFn` sends bytes. Pass NULL function
pointers for a disconnected / display-only terminal.
When connected, keyboard input is translated to ANSI sequences and sent
via `writeFn` (arrows become `ESC[A`..`ESC[D`, etc.).
```c
int32_t wgtAnsiTermPoll(WidgetT *w);
```
Poll the comm interface for incoming data and process it through the
ANSI parser. Also handles cursor blink and text blink timers. Returns
the number of bytes read. When using `dvxUpdate()`, terminal polling
is handled automatically -- call this manually only when running your
own event loop.
```c
int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
```
Fast repaint: renders only dirty rows directly into the window's content
buffer, bypassing the full widget paint pipeline (no clear, no relayout,
no other widgets). Returns the number of rows repainted (0 if nothing
was dirty). If `outY` and `outH` are non-NULL, they receive the
content-buffer-relative Y coordinate and height of the repainted region,
for targeted dirty-rect invalidation. Uses `drawTermRow()` internally
for efficient bulk rendering. When using `dvxUpdate()`, this is called
automatically after `wgtAnsiTermPoll()`.
```c
void wgtAnsiTermSetScrollback(WidgetT *w, int32_t maxLines);
```
Set the scrollback buffer size (default 500 lines). Clears any existing
scrollback. Lines scroll into the buffer when they leave the top of the
screen or when the screen is cleared (`ESC[2J` / `wgtAnsiTermClear`).
A vertical scrollbar appears automatically when there is scrollback
content. Click the arrow buttons for single-line scrolling, or the
trough for page scrolling. The view auto-follows live output unless the
user has scrolled back.
### Spacing and dividers
```c
WidgetT *wgtSpacer(WidgetT *parent);
```
Invisible spacer. Default weight is 100 -- pushes siblings apart.
```c
WidgetT *wgtHSeparator(WidgetT *parent); // horizontal line
WidgetT *wgtVSeparator(WidgetT *parent); // vertical line
```
Visual divider lines (shadow + highlight, 2px).
### Size specifications
Size fields (`minW`, `minH`, `maxW`, `maxH`, `prefW`, `prefH`, `spacing`,
`padding`) accept tagged values created with:
```c
wgtPixels(120) // 120 pixels
wgtChars(15) // 15 character widths
wgtPercent(50) // 50% of parent
```
A raw `0` means auto (use the widget's natural/computed size).
### Weight
The `weight` field controls how extra space is distributed among siblings.
When a container is larger than its children's combined minimum size, the
surplus is divided proportionally by weight.
- `weight = 0` -- fixed size, does not stretch (default for most widgets)
- `weight = 100` -- normal stretch (default for spacers and text inputs)
- Relative values work: weights of 100, 200, 100 give a 1:2:1 split
When all children have weight 0, the container's `align` property
determines where children are placed within the extra space.
### Operations
```c
void wgtSetText(WidgetT *w, const char *text);
const char *wgtGetText(const WidgetT *w);
```
Get/set text for labels, buttons, checkboxes, radios, text inputs,
password inputs, masked inputs, combo boxes, and dropdowns.
```c
void wgtSetEnabled(WidgetT *w, bool enabled);
void wgtSetVisible(WidgetT *w, bool visible);
```
Disabled widgets are drawn grayed out and ignore input. Hidden widgets
are excluded from layout.
```c
void wgtInvalidate(WidgetT *w);
```
Trigger a full relayout and repaint of the widget tree. Call after
modifying widget properties, adding/removing widgets, or changing text.
```c
WidgetT *wgtFind(WidgetT *root, const char *name);
```
Search the subtree for a widget by name. Set `w->name` (up to 31 chars)
to make a widget findable.
```c
void wgtDestroy(WidgetT *w);
```
Remove a widget and all its children from the tree and free memory.
```c
void wgtSetDebugLayout(bool enabled);
```
When enabled, draws 1px neon-colored borders around all layout containers
so their bounds are visible. Each container gets a distinct color derived
from its pointer.
### Layout and paint internals
These are called automatically by `wgtInvalidate()`, but are available
for manual use when embedding the widget system in a custom event loop.
```c
int32_t wgtResolveSize(int32_t taggedSize, int32_t parentSize, int32_t charWidth);
```
Convert a tagged size (from `wgtPixels`, `wgtChars`, or `wgtPercent`) to
a pixel value given the parent's size and the font's character width.
```c
void wgtLayout(WidgetT *root, int32_t availW, int32_t availH,
const BitmapFontT *font);
```
Run the two-pass layout engine on the widget tree: measure minimum sizes
bottom-up, then distribute available space top-down. Sets `x`, `y`, `w`,
`h` on every widget.
```c
void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops,
const BitmapFontT *font, const ColorSchemeT *colors);
```
Paint the entire widget tree into the window's content buffer.
### List box operations
```c
void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count);
int32_t wgtListBoxGetSelected(const WidgetT *w);
void wgtListBoxSetSelected(WidgetT *w, int32_t idx);
```
### ListView
```c
WidgetT *wgtListView(WidgetT *parent);
void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count);
void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount);
int32_t wgtListViewGetSelected(const WidgetT *w);
void wgtListViewSetSelected(WidgetT *w, int32_t idx);
```
Multi-column list with sortable headers, resizable columns, and single or
multi-select support. Column definitions use `ListViewColT`:
```c
typedef struct {
const char *title;
int32_t width; // tagged size (wgtPixels/wgtChars/wgtPercent)
ListViewAlignE align; // ListViewAlignLeftE, CenterE, or RightE
} ListViewColT;
```
Column widths support tagged sizes (`wgtPixels`, `wgtChars`, `wgtPercent`).
Cell data is a flat array of strings in row-major order (row0col0, row0col1,
..., row1col0, ...). Both the column and data arrays must remain valid for
the widget's lifetime.
Clicking a column header sorts the list. An arrow indicator shows the
current sort direction. Column borders can be dragged to resize. Vertical
and horizontal scrollbars appear automatically when needed.
```c
void wgtListViewSetSort(WidgetT *w, int32_t col, ListViewSortE dir);
void wgtListViewSetHeaderClickCallback(WidgetT *w,
void (*cb)(WidgetT *w, int32_t col, ListViewSortE dir));
```
Programmatic sort control. The header click callback fires after the sort
direction is updated, allowing custom sort logic.
```c
void wgtListViewSetMultiSelect(WidgetT *w, bool multi);
bool wgtListViewIsItemSelected(const WidgetT *w, int32_t idx);
void wgtListViewSetItemSelected(WidgetT *w, int32_t idx, bool selected);
void wgtListViewSelectAll(WidgetT *w);
void wgtListViewClearSelection(WidgetT *w);
```
Multi-select mode supports Ctrl+click to toggle individual items and
Shift+click for range selection. Default weight is 100.
### ScrollPane
```c
WidgetT *wgtScrollPane(WidgetT *parent);
```
Scrollable container for child widgets. Children are laid out vertically
(like a VBox) at their natural size. Vertical and horizontal scrollbars
appear automatically when the content exceeds the visible area.
Mouse clicks on the scrollbar arrows scroll by one line/character; clicks
on the trough scroll by one page. Clicks on the content area are forwarded
to child widgets. Keyboard scrolling: Up/Down arrows, Page Up/Down,
Home/End for vertical; Left/Right arrows for horizontal.
Set `padding` and `spacing` to control internal layout. Default weight
is 100.
---
## Types reference (`dvxTypes.h`)
### WindowT
Core window structure. Key fields:
| Field | Type | Description |
|-------|------|-------------|
| `id` | `int32_t` | Unique window identifier |
| `x`, `y`, `w`, `h` | `int32_t` | Outer frame position and size |
| `contentX`, `contentY` | `int32_t` | Content area offset within frame |
| `contentW`, `contentH` | `int32_t` | Content area dimensions |
| `contentBuf` | `uint8_t *` | Pixel buffer (display format) |
| `contentPitch` | `int32_t` | Bytes per scanline in content buffer |
| `title` | `char[128]` | Title bar text |
| `visible` | `bool` | Window is shown |
| `focused` | `bool` | Window has input focus |
| `minimized` | `bool` | Window is minimized to icon |
| `maximized` | `bool` | Window is maximized |
| `resizable` | `bool` | Window has resize handles |
| `maxW`, `maxH` | `int32_t` | Max dimensions for maximize (-1 = screen) |
| `userData` | `void *` | Application data pointer |
### ColorSchemeT
All colors are pre-packed via `packColor()`:
| Field | Usage |
|-------|-------|
| `desktop` | Desktop background |
| `windowFace` | Window frame fill |
| `windowHighlight` | Bevel light edge |
| `windowShadow` | Bevel dark edge |
| `activeTitleBg/Fg` | Focused title bar |
| `inactiveTitleBg/Fg` | Unfocused title bar |
| `contentBg/Fg` | Window content area |
| `menuBg/Fg` | Menu bar and popups |
| `menuHighlightBg/Fg` | Highlighted menu item |
| `buttonFace` | Button interior |
| `scrollbarBg/Fg/Trough` | Scrollbar elements |
### BitmapFontT
Fixed-width bitmap font (8px wide, 14 or 16px tall). Glyphs are packed
1bpp, `charHeight` bytes per glyph, MSB-first.
### BevelStyleT
```c
typedef struct {
uint32_t highlight; // top/left edge color
uint32_t shadow; // bottom/right edge color
uint32_t face; // interior fill (0 = no fill)
int32_t width; // border thickness in pixels
} BevelStyleT;
```
---
## Window chrome
Windows use a Motif/GEOS-style frame:
- 4px beveled outer border with perpendicular groove breaks
- 20px title bar (dark charcoal background when focused)
- Close button on the left edge (requires double-click)
- 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 has an icon image set via `dvxSetWindowIcon()`, that
image is shown; otherwise a nearest-neighbor-scaled thumbnail of the
window's content buffer is used. Thumbnails are refreshed automatically
when the window's content changes, with updates staggered across frames
so only one icon redraws per interval. Double-click an icon to restore.
---
## Hardware requirements
- 486 or later CPU
- VESA VBE 2.0+ compatible video card with linear framebuffer support
- PS/2 mouse (or compatible mouse driver)
- DPMI host (CWSDPMI, Windows DOS box, DOSBox, 86Box)
Tested on 86Box with PCI video cards. DOSBox is a trusted reference
platform.