1192 lines
39 KiB
Markdown
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.
|