# 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.