DVX_GUI/dvx
2026-03-17 21:31:40 -05:00
..
platform Working on bugs found via real hardware. 2026-03-17 21:31:40 -05:00
thirdparty More widgets! 2026-03-09 22:50:06 -05:00
widgets More code cleanup. 2026-03-17 18:22:16 -05:00
dvxApp.c More code cleanup. 2026-03-17 18:22:16 -05:00
dvxApp.h ProgMan is now it's own app. About app moved into ProgMan. Minor API improvements. 2026-03-17 16:11:31 -05:00
dvxComp.c dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxComp.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxCursor.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxDialog.c More code cleanup. 2026-03-17 18:22:16 -05:00
dvxDialog.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxDraw.c dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxDraw.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxFont.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxIcon.c dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxImageWrite.c dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxPalette.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxTypes.h More code cleanup. 2026-03-17 18:22:16 -05:00
dvxVideo.c dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxVideo.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
dvxWidget.h More code cleanup. 2026-03-17 18:22:16 -05:00
dvxWm.c More code cleanup. 2026-03-17 18:22:16 -05:00
dvxWm.h dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00
Makefile More code cleanup. 2026-03-17 18:22:16 -05:00
README.md dvxshell debugging. Starting to work! 2026-03-17 01:03:25 -05:00

DVX GUI

A DOS Visual eXecutive 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, splitters, 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
dvxDialog.h Modal dialogs (message box, file dialog)
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 platform abstraction lives in platform/:

File Purpose
platform/dvxPlatform.h OS/CPU-neutral interface: video, input, span ops, filename validation
platform/dvxPlatformDos.c DOS/DJGPP implementation: VESA, DPMI, INT 16h/33h, rep stosl/movsl

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.

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

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

#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);   // initial layout + paint

    dvxRun(&ctx);
    dvxShutdown(&ctx);

    return 0;
}

Application API (dvxApp.h)

Lifecycle

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.

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.

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.

void dvxShutdown(AppContextT *ctx);

Restore text mode, release LFB mapping, and free the backbuffer.

void dvxQuit(AppContextT *ctx);

Request exit from the main loop. dvxRun() returns on the next iteration.

Windows

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

Example:

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;
win->onMenu   = myMenuHandler;
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);

Remove a window from the stack and free its resources.

void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title);

Change the title bar text.

int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path);

Load a BMP, PNG, JPEG, or GIF 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.

void dvxMinimizeWindow(AppContextT *ctx, WindowT *win);
void dvxMaximizeWindow(AppContextT *ctx, WindowT *win);
void dvxFitWindow(AppContextT *ctx, WindowT *win);

dvxFitWindow resizes the window to fit its widget tree's natural size.

Window arrangement

dvxCascadeWindows(&ctx);      // staggered cascade
dvxTileWindows(&ctx);         // grid tile
dvxTileWindowsH(&ctx);        // side-by-side
dvxTileWindowsV(&ctx);        // top-to-bottom

Invalidation

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.

Clipboard

dvxClipboardCopy("Hello", 5);

int32_t len;
const char *text = dvxClipboardGet(&len);

Screenshots

dvxScreenshot(&ctx, "screen.png");               // entire screen
dvxWindowScreenshot(&ctx, win, "window.png");    // single window content

Accessors

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

WindowT *win = dvxCreateWindow(&ctx, "Menus", 50, 50, 400, 300, true);
win->onMenu = onMenuCb;

MenuBarT *bar = wmAddMenuBar(win);

MenuT *fileMenu = wmAddMenu(bar, "&File");
wmAddMenuItem(fileMenu, "&New", CMD_FILE_NEW);
wmAddMenuItem(fileMenu, "&Open...", CMD_FILE_OPEN);
wmAddMenuItem(fileMenu, "&Save", CMD_FILE_SAVE);
wmAddMenuSeparator(fileMenu);
wmAddMenuItem(fileMenu, "E&xit", CMD_FILE_EXIT);

MenuT *viewMenu = wmAddMenu(bar, "&View");
wmAddMenuCheckItem(viewMenu, "Tool&bar", CMD_VIEW_TOOLBAR, true);
wmAddMenuCheckItem(viewMenu, "&Status Bar", CMD_VIEW_STATUSBAR, true);
wmAddMenuSeparator(viewMenu);
wmAddMenuRadioItem(viewMenu, "S&mall", CMD_VIEW_SIZE_SMALL, false);
wmAddMenuRadioItem(viewMenu, "Me&dium", CMD_VIEW_SIZE_MED, true);
wmAddMenuRadioItem(viewMenu, "&Large", CMD_VIEW_SIZE_LARGE, false);

// Cascading submenu
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);

The & prefix marks accelerator keys -- Alt+F opens "&File". Up to 8 menus per bar, 16 items per menu, submenus nested up to 4 deep.

Menu callback:

static void onMenuCb(WindowT *win, int32_t menuId) {
    AppContextT *ctx = (AppContextT *)win->userData;

    switch (menuId) {
        case CMD_FILE_EXIT:
            dvxQuit(ctx);
            break;
    }
}

Context menus

Right-click menus can be attached to any window or widget:

// Window-level context menu
MenuT *ctxMenu = wmCreateMenu();
wmAddMenuItem(ctxMenu, "Cu&t", CMD_CUT);
wmAddMenuItem(ctxMenu, "&Copy", CMD_COPY);
wmAddMenuItem(ctxMenu, "&Paste", CMD_PASTE);
wmAddMenuSeparator(ctxMenu);
wmAddMenuItem(ctxMenu, "&Properties...", CMD_PROPS);
win->contextMenu = ctxMenu;

// Widget-level context menu
WidgetT *lb = wgtListBox(root);
MenuT *lbCtx = wmCreateMenu();
wmAddMenuItem(lbCtx, "&Delete", CMD_DELETE);
wmAddMenuItem(lbCtx, "Select &All", CMD_SELALL);
lb->contextMenu = lbCtx;

Context menus route through the window's onMenu callback. The caller owns the MenuT (not freed by the widget system).


Accelerator tables

Global hotkeys routed through the window's onMenu callback:

AccelTableT *accel = dvxCreateAccelTable();
dvxAddAccel(accel, 'N', ACCEL_CTRL, CMD_FILE_NEW);
dvxAddAccel(accel, 'O', ACCEL_CTRL, CMD_FILE_OPEN);
dvxAddAccel(accel, 'S', ACCEL_CTRL, CMD_FILE_SAVE);
dvxAddAccel(accel, 'Q', ACCEL_CTRL, CMD_FILE_EXIT);
dvxAddAccel(accel, KEY_F1, 0, CMD_HELP_ABOUT);
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. The table is freed with dvxFreeAccelTable().


Scrollbars

wmAddVScrollbar(win, 0, 100, 25);  // vertical, range 0-100, page size 25
wmAddHScrollbar(win, 0, 100, 25);  // horizontal
wmUpdateContentRect(win);
wmReallocContentBuf(win, &ctx.display);

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.


Dialogs (dvxDialog.h)

Message box

int32_t result = dvxMessageBox(&ctx, "Confirm",
    "Are you sure you want to exit?",
    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

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
}

if (dvxFileDialog(&ctx, "Save As", FD_SAVE, NULL, filters, 3, path, sizeof(path))) {
    // path contains the chosen filename
}

Flags: FD_OPEN, FD_SAVE. Pass NULL for initialDir to use the current directory. Filename validation is platform-specific (DOS 8.3 on the DOS platform).


Video and drawing (dvxVideo.h, dvxDraw.h)

These are lower-level APIs. Application code typically only needs packColor.

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.

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

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.

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.

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);

Bulk-render exactly count characters. Much faster than calling drawChar per character because clip bounds are computed once and background is filled in bulk.

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.

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.

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.

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.

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.

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

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

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

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.

Example:

WidgetT *root = wgtInitWindow(&ctx, win);

WidgetT *frame = wgtFrame(root, "User Input");
WidgetT *row1 = wgtHBox(frame);
wgtLabel(row1, "Name:");
wgtTextInput(row1, 64);

WidgetT *row2 = wgtHBox(frame);
wgtLabel(row2, "Email:");
wgtTextInput(row2, 128);

WidgetT *btnRow = wgtHBox(root);
btnRow->align = AlignEndE;
wgtButton(btnRow, "OK");
wgtButton(btnRow, "Cancel");

wgtInvalidate(root);

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)

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

Label

WidgetT *wgtLabel(WidgetT *parent, const char *text);

Static text label. Sized to fit its text. Supports & accelerator markers.

WidgetT *lbl = wgtLabel(root, "&Status:");
wgtSetText(lbl, "Connected");  // change text later

Button

WidgetT *wgtButton(WidgetT *parent, const char *text);

Beveled push button. Visually depresses on mouse-down, tracks the cursor while held, fires onClick on release if still over the button.

static void onOkClick(WidgetT *w) {
    // handle button press
}

WidgetT *btn = wgtButton(root, "&OK");
btn->onClick = onOkClick;

Checkbox

WidgetT *wgtCheckbox(WidgetT *parent, const char *text);

Toggle checkbox. Read/write w->as.checkbox.checked.

WidgetT *chk = wgtCheckbox(root, "Enable feature &A");
chk->as.checkbox.checked = true;  // pre-check
chk->onChange = onCheckChanged;    // notified on toggle

Radio buttons

WidgetT *wgtRadioGroup(WidgetT *parent);
WidgetT *wgtRadio(WidgetT *parent, const char *text);

Radio buttons with diamond-shaped indicators. Create a group, then add options as children. The group tracks the selected index.

WidgetT *rg = wgtRadioGroup(root);
wgtRadio(rg, "Option &1");
wgtRadio(rg, "Option &2");
wgtRadio(rg, "Option &3");
rg->onChange = onRadioChanged;

// Read selection:
int32_t idx = rg->as.radioGroup.selectedIdx;

TextInput

WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen);

Single-line text input field. maxLen is the buffer capacity. Default weight is 100 (stretches to fill).

WidgetT *row = wgtHBox(root);
wgtLabel(row, "&Name:");
WidgetT *input = wgtTextInput(row, 64);
wgtSetText(input, "Default text");
input->onChange = onTextChanged;

// Read text:
const char *text = wgtGetText(input);

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.

PasswordInput

WidgetT *wgtPasswordInput(WidgetT *parent, int32_t maxLen);

Identical to wgtTextInput except characters are displayed as bullets. Copy and cut to clipboard are disabled; paste is allowed.

WidgetT *pw = wgtPasswordInput(root, 32);

MaskedInput

WidgetT *wgtMaskedInput(WidgetT *parent, const char *mask);

Formatted input field. Mask characters: # = digit, A = letter, * = any printable. All other characters are fixed literals.

wgtMaskedInput(root, "(###) ###-####");  // US phone
wgtMaskedInput(root, "##/##/####");      // date
wgtMaskedInput(root, "###-##-####");     // SSN

The mask string must remain valid for the widget's lifetime.

TextArea

WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen);

Multi-line text editor with vertical and horizontal scrollbars.

WidgetT *ta = wgtTextArea(root, 4096);
ta->weight = 100;
wgtSetText(ta, "Line 1\nLine 2\nLine 3");

Supports all TextInput editing features plus multi-line selection, Enter for newlines, and vertical/horizontal auto-scroll.

ListBox

WidgetT *wgtListBox(WidgetT *parent);
void    wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count);
int32_t wgtListBoxGetSelected(const WidgetT *w);
void    wgtListBoxSetSelected(WidgetT *w, int32_t idx);
static const char *items[] = {"Alpha", "Beta", "Gamma", "Delta"};

WidgetT *lb = wgtListBox(root);
wgtListBoxSetItems(lb, items, 4);
wgtListBoxSetSelected(lb, 0);
lb->weight = 100;
lb->onClick = onItemSelected;

Multi-select mode:

wgtListBoxSetMultiSelect(lb, true);
wgtListBoxSetItemSelected(lb, 0, true);
wgtListBoxSetItemSelected(lb, 2, true);
bool sel = wgtListBoxIsItemSelected(lb, 1);
wgtListBoxSelectAll(lb);
wgtListBoxClearSelection(lb);

Reorderable (drag-and-drop items):

wgtListBoxSetReorderable(lb, true);

Dropdown

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 with a popup list.

static const char *colors[] = {"Red", "Green", "Blue", "Yellow"};

WidgetT *dd = wgtDropdown(root);
wgtDropdownSetItems(dd, colors, 4);
wgtDropdownSetSelected(dd, 0);
dd->onChange = onColorChanged;

The items array must remain valid for the widget's lifetime.

ComboBox

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.

static const char *sizes[] = {"Small", "Medium", "Large", "Extra Large"};

WidgetT *cb = wgtComboBox(root, 32);
wgtComboBoxSetItems(cb, sizes, 4);
wgtComboBoxSetSelected(cb, 1);
cb->onChange = onSizeChanged;

// Read typed/selected text:
const char *text = wgtGetText(cb);

ProgressBar

WidgetT *wgtProgressBar(WidgetT *parent);    // horizontal
WidgetT *wgtProgressBarV(WidgetT *parent);   // vertical
void     wgtProgressBarSetValue(WidgetT *w, int32_t value);
int32_t  wgtProgressBarGetValue(const WidgetT *w);
WidgetT *pb = wgtProgressBar(root);
pb->weight = 100;
wgtProgressBarSetValue(pb, 65);

WidgetT *pbV = wgtProgressBarV(root);
wgtProgressBarSetValue(pbV, 75);

Value is 0--100 (percentage).

Slider

WidgetT *wgtSlider(WidgetT *parent, int32_t minVal, int32_t maxVal);
void     wgtSliderSetValue(WidgetT *w, int32_t value);
int32_t  wgtSliderGetValue(const WidgetT *w);
WidgetT *slider = wgtSlider(root, 0, 100);
wgtSliderSetValue(slider, 50);
slider->onChange = onVolumeChanged;

// Vertical slider:
WidgetT *vSlider = wgtSlider(root, 0, 255);
vSlider->as.slider.vertical = true;

Spinner

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 with increment/decrement buttons.

WidgetT *spin = wgtSpinner(root, 0, 999, 1);
wgtSpinnerSetValue(spin, 42);
spin->weight = 50;
spin->onChange = onQuantityChanged;

Up/Down arrows increment/decrement by step; Page Up/Down by step * 10. Enter commits the typed value; Escape reverts.

TabControl

WidgetT *wgtTabControl(WidgetT *parent);
WidgetT *wgtTabPage(WidgetT *parent, const char *title);
void     wgtTabControlSetActive(WidgetT *w, int32_t idx);
int32_t  wgtTabControlGetActive(const WidgetT *w);
WidgetT *tabs = wgtTabControl(root);

WidgetT *page1 = wgtTabPage(tabs, "&General");
wgtLabel(page1, "General settings");
wgtCheckbox(page1, "&Enable logging");

WidgetT *page2 = wgtTabPage(tabs, "&Advanced");
wgtLabel(page2, "Advanced settings");
WidgetT *slider = wgtSlider(page2, 0, 100);
wgtSliderSetValue(slider, 75);

WidgetT *page3 = wgtTabPage(tabs, "A&bout");
wgtLabel(page3, "Version 1.0");

Each page is a VBox. Only the active page is visible and receives layout.

StatusBar

WidgetT *wgtStatusBar(WidgetT *parent);

Horizontal container with sunken panel background. Place at the bottom of the root VBox.

WidgetT *sb = wgtStatusBar(root);
WidgetT *msg = wgtLabel(sb, "Ready");
msg->weight = 100;  // stretch to fill
wgtLabel(sb, "Line 1, Col 1");

Toolbar

WidgetT *wgtToolbar(WidgetT *parent);

Horizontal container with raised background. Place at the top of the root VBox.

WidgetT *tb = wgtToolbar(root);
WidgetT *btnNew  = wgtImageButton(tb, newPixels, 16, 16, 16 * bpp);
WidgetT *btnOpen = wgtImageButton(tb, openPixels, 16, 16, 16 * bpp);
WidgetT *btnSave = wgtImageButton(tb, savePixels, 16, 16, 16 * bpp);
wgtVSeparator(tb);
wgtButton(tb, "&Help");

TreeView

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);
WidgetT *tree = wgtTreeView(root);

WidgetT *docs = wgtTreeItem(tree, "Documents");
wgtTreeItemSetExpanded(docs, true);
wgtTreeItem(docs, "README.md");
wgtTreeItem(docs, "DESIGN.md");

WidgetT *src = wgtTreeItem(docs, "src");
wgtTreeItemSetExpanded(src, true);
wgtTreeItem(src, "main.c");
wgtTreeItem(src, "utils.c");

WidgetT *images = wgtTreeItem(tree, "Images");
wgtTreeItem(images, "logo.png");
wgtTreeItem(images, "icon.bmp");

Multi-select and reorderable:

wgtTreeViewSetMultiSelect(tree, true);
wgtTreeViewSetReorderable(tree, true);

The tree view manages its own scrollbars automatically. Default weight is 100.

ListView

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 and resizable columns.

static const ListViewColT cols[] = {
    {"Name",     wgtChars(16), ListViewAlignLeftE},
    {"Size",     wgtChars(8),  ListViewAlignRightE},
    {"Type",     wgtChars(12), ListViewAlignLeftE},
    {"Modified", wgtChars(12), ListViewAlignLeftE}
};

static const char *data[] = {
    "AUTOEXEC.BAT", "412",     "Batch File",  "03/15/1994",
    "CONFIG.SYS",   "256",     "System File", "03/15/1994",
    "COMMAND.COM",  "54,645",  "Application", "09/30/1993",
};

WidgetT *lv = wgtListView(root);
wgtListViewSetColumns(lv, cols, 4);
wgtListViewSetData(lv, data, 3);
wgtListViewSetSelected(lv, 0);
lv->weight = 100;

Multi-select and sorting:

wgtListViewSetMultiSelect(lv, true);
wgtListViewSetSort(lv, 0, ListViewSortAscE);
wgtListViewSetReorderable(lv, true);

// Header click callback for custom sort logic:
wgtListViewSetHeaderClickCallback(lv, onHeaderClick);

Column widths support tagged sizes (wgtPixels, wgtChars, wgtPercent). Cell data is a flat array of strings in row-major order. Both the column and data arrays must remain valid for the widget's lifetime.

ScrollPane

WidgetT *wgtScrollPane(WidgetT *parent);

Scrollable container. Children are laid out vertically at their natural size. Scrollbars appear automatically when content exceeds the visible area.

WidgetT *sp = wgtScrollPane(root);
sp->weight  = 100;
sp->padding = wgtPixels(4);
sp->spacing = wgtPixels(4);

wgtLabel(sp, "Scrollable content:");
wgtHSeparator(sp);
for (int32_t i = 0; i < 8; i++) {
    wgtCheckbox(sp, items[i]);
}
wgtHSeparator(sp);
wgtButton(sp, "Scrolled Button");

Splitter

WidgetT *wgtSplitter(WidgetT *parent, bool vertical);
void     wgtSplitterSetPos(WidgetT *w, int32_t pos);
int32_t  wgtSplitterGetPos(const WidgetT *w);

Resizable divider between exactly two child widgets. vertical = true creates a left|right split; false creates a top/bottom split.

// Horizontal split: tree on left, list on right
WidgetT *vSplit = wgtSplitter(root, true);
vSplit->weight = 100;
wgtSplitterSetPos(vSplit, 150);

WidgetT *leftTree = wgtTreeView(vSplit);
// ... populate tree ...

WidgetT *rightList = wgtListBox(vSplit);
// ... populate list ...

Nested splitters for complex layouts:

// Outer: top/bottom split
WidgetT *hSplit = wgtSplitter(root, false);
hSplit->weight = 100;
wgtSplitterSetPos(hSplit, 120);

// Top pane: left/right split
WidgetT *vSplit = wgtSplitter(hSplit, true);
wgtSplitterSetPos(vSplit, 120);
wgtTreeView(vSplit);   // left
wgtListBox(vSplit);    // right

// Bottom pane: detail view
WidgetT *detail = wgtFrame(hSplit, "Preview");
wgtLabel(detail, "Select a file above.");

Image

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);
// From file (BMP, PNG, JPEG, GIF):
wgtImageFromFile(root, "sample.bmp");

// From pixel buffer (display format, takes ownership):
uint8_t *pixels = loadMyImage(&w, &h, &pitch);
wgtImage(root, pixels, w, h, pitch);

ImageButton

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 text. Behaves identically to wgtButton.

uint8_t *iconData = loadBmpPixels(&ctx, "new.bmp", &imgW, &imgH, &imgPitch);
WidgetT *btn = wgtImageButton(tb, iconData, imgW, imgH, imgPitch);
btn->onClick = onNewClick;

Takes ownership of the pixel buffer (freed on destroy).

Canvas

WidgetT *wgtCanvas(WidgetT *parent, int32_t w, int32_t h);

Drawable bitmap canvas with a sunken bevel border. Supports interactive freehand drawing (mouse strokes) and programmatic drawing.

const DisplayT *d = dvxGetDisplay(&ctx);
WidgetT *cv = wgtCanvas(root, 280, 100);

wgtCanvasClear(cv, packColor(d, 255, 255, 255));

wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0));
wgtCanvasDrawRect(cv, 5, 5, 50, 35);

wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 200));
wgtCanvasFillCircle(cv, 150, 50, 25);

wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0));
wgtCanvasSetPenSize(cv, 3);
wgtCanvasDrawLine(cv, 70, 5, 130, 90);

// Individual pixels:
wgtCanvasSetPixel(cv, 10, 10, packColor(d, 255, 0, 0));
uint32_t px = wgtCanvasGetPixel(cv, 10, 10);

// Save/load:
wgtCanvasSave(cv, "drawing.png");
wgtCanvasLoad(cv, "image.png");

Drawing primitives: wgtCanvasDrawLine, wgtCanvasDrawRect (outline), wgtCanvasFillRect (solid), wgtCanvasFillCircle. All use the current pen color and clip to the canvas bounds.

ANSI Terminal

WidgetT *wgtAnsiTerm(WidgetT *parent, int32_t cols, int32_t rows);

ANSI BBS terminal emulator. Displays a character grid (default 80x25 if cols/rows are 0) with full ANSI escape sequence support and a 16-color CGA palette.

WidgetT *term = wgtAnsiTerm(root, 80, 25);
term->weight = 100;
wgtAnsiTermSetScrollback(term, 500);

// Feed ANSI content directly:
static const uint8_t ansiData[] =
    "\x1B[2J"                          // clear screen
    "\x1B[1;34m=== Welcome ===\x1B[0m\r\n"
    "\x1B[1mBold\x1B[0m, "
    "\x1B[7mReverse\x1B[0m, "
    "\x1B[5mBlinking\x1B[0m\r\n"
    "\x1B[31m Red \x1B[32m Green \x1B[34m Blue \x1B[0m\r\n";

wgtAnsiTermWrite(term, ansiData, sizeof(ansiData) - 1);

Connect to a communications interface:

static int32_t myRead(void *ctx, uint8_t *buf, int32_t maxLen) {
    return serialRead((SerialT *)ctx, buf, maxLen);
}

static int32_t myWrite(void *ctx, const uint8_t *buf, int32_t len) {
    return serialWrite((SerialT *)ctx, buf, len);
}

wgtAnsiTermSetComm(term, &serial, myRead, myWrite);

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

Additional functions:

void wgtAnsiTermClear(WidgetT *w);                       // clear + reset cursor
int32_t wgtAnsiTermPoll(WidgetT *w);                     // poll comm for data
int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *y, int32_t *h); // fast dirty-row repaint
void wgtAnsiTermSetScrollback(WidgetT *w, int32_t maxLines);

Spacing and dividers

WidgetT *wgtSpacer(WidgetT *parent);     // flexible space (weight=100)
WidgetT *wgtHSeparator(WidgetT *parent); // horizontal line
WidgetT *wgtVSeparator(WidgetT *parent); // vertical line
WidgetT *row = wgtHBox(root);
wgtButton(row, "Left");
wgtSpacer(row);         // pushes next button to the right
wgtButton(row, "Right");

Tooltips

wgtSetTooltip(widget, "Tooltip text appears on hover");

Size specifications

Size fields (minW, minH, maxW, maxH, prefW, prefH, spacing, padding) accept tagged values created with:

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

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.

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.

// Disable an entire group of widgets:
wgtSetEnabled(btn, false);
wgtSetEnabled(slider, false);
wgtSetEnabled(textInput, false);
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.

void wgtInvalidatePaint(WidgetT *w);

Lightweight repaint without relayout. Use when only visual state changed (slider value, cursor blink, checkbox toggle) but widget sizes are stable.

WidgetT *wgtFind(WidgetT *root, const char *name);
void wgtSetName(WidgetT *w, const char *name);

Search the subtree for a widget by name (up to 31 chars). Uses djb2 hash for fast rejection.

wgtSetName(statusLabel, "status");
// ... later, from any callback:
WidgetT *lbl = wgtFind(root, "status");
wgtSetText(lbl, "Updated!");
wgtInvalidate(lbl);
void wgtDestroy(WidgetT *w);

Remove a widget and all its children from the tree and free memory.

void wgtSetDebugLayout(AppContextT *ctx, 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.

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.

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.

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.


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

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.

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

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 handling, etc.)
platformYield() Cooperative multitasking yield
platformVideoInit() Set up video mode, LFB, backbuffer
platformVideoShutdown() Restore text mode, free resources
platformVideoSetPalette() Set 8-bit palette entries
platformFlushRect() Copy rectangle from backbuffer to LFB
platformSpanFill8/16/32() Optimized pixel fill
platformSpanCopy8/16/32() Optimized pixel copy
platformMouseInit() Initialize mouse driver
platformMousePoll() Read mouse position and buttons
platformKeyboardGetModifiers() Read shift/ctrl/alt state
platformKeyboardRead() Read next key from buffer
platformAltScanToChar() Map Alt+scancode to ASCII
platformValidateFilename() Check filename for OS constraints

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.