# DV/X GUI A DESQview/X-style windowed GUI compositor for DOS, targeting DJGPP/DPMI with VESA VBE 2.0+ linear framebuffer. Motif-style beveled chrome, dirty-rectangle compositing, draggable and resizable windows, dropdown menus, scrollbars, and a declarative widget/layout system. ## Building Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`). ``` make # builds bin/demo.exe make clean # removes obj/ and bin/ ``` Set `DJGPP_PREFIX` in the Makefile if your toolchain is installed somewhere other than `~/djgpp/djgpp`. ## Architecture The library is organized in five layers. Each layer is a `.h`/`.c` pair. Application code only needs to include `dvxApp.h` (which pulls in the rest) and optionally `dvxWidget.h`. ``` Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops) Layer 3 dvxComp Dirty rectangle list, merge, LFB flush Layer 4 dvxWm Window stack, chrome, drag, resize, focus, menus, scrollbars Layer 5 dvxApp Event loop, mouse/keyboard input, public API dvxWidget Widget/layout system (optional, standalone) ``` Supporting files: | File | Purpose | |------|---------| | `dvxTypes.h` | Shared type definitions used by all layers | | `dvxFont.h` | Built-in 8x14 bitmap font glyph data | | `dvxCursor.h` | Mouse cursor bitmask data (5 shapes) | | `dvxPalette.h` | Default VGA palette for 8-bit mode | | `dvxIcon.c` | stb_image implementation unit (BMP/TGA/PNG) | | `thirdparty/stb_image.h` | Third-party single-header image loader | ## Quick start ```c #include "dvxApp.h" static void onPaint(WindowT *win, RectT *dirty) { AppContextT *ctx = (AppContextT *)win->userData; const BlitOpsT *ops = dvxGetBlitOps(ctx); const DisplayT *d = dvxGetDisplay(ctx); uint32_t blue = packColor(d, 0, 0, 200); for (int32_t y = 0; y < win->contentH; y++) { ops->spanFill(win->contentBuf + y * win->contentPitch, blue, win->contentW); } } int main(void) { AppContextT ctx; if (dvxInit(&ctx, 1024, 768, 16) != 0) { return 1; } WindowT *win = dvxCreateWindow(&ctx, "Hello", 100, 100, 300, 200, true); win->userData = &ctx; win->onPaint = onPaint; RectT r = {0, 0, win->contentW, win->contentH}; win->onPaint(win, &r); dvxRun(&ctx); dvxShutdown(&ctx); return 0; } ``` ## Quick start (widgets) ```c #include "dvxApp.h" #include "dvxWidget.h" static void onButtonClick(WidgetT *w) { WidgetT *root = w; while (root->parent) root = root->parent; WidgetT *lbl = wgtFind(root, "status"); if (lbl) { wgtSetText(lbl, "Clicked!"); wgtInvalidate(lbl); } } int main(void) { AppContextT ctx; dvxInit(&ctx, 1024, 768, 16); WindowT *win = dvxCreateWindow(&ctx, "Widgets", 100, 100, 260, 200, true); win->userData = &ctx; WidgetT *root = wgtInitWindow(&ctx, win); WidgetT *lbl = wgtLabel(root, "Ready."); strncpy(lbl->name, "status", MAX_WIDGET_NAME); wgtHSeparator(root); WidgetT *row = wgtHBox(root); wgtLabel(row, "Name:"); wgtTextInput(row, 64); WidgetT *btn = wgtButton(root, "Go"); btn->onClick = onButtonClick; wgtInvalidate(root); // initial layout + paint dvxRun(&ctx); dvxShutdown(&ctx); return 0; } ``` --- ## Application API (`dvxApp.h`) ### Lifecycle ```c int32_t dvxInit(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_t preferredBpp); ``` Initialize VESA video, input, fonts, color scheme, and cursors. Finds a mode matching the requested resolution and bit depth (8, 15, 16, or 32). Returns 0 on success, -1 on failure. ```c void dvxRun(AppContextT *ctx); ``` Enter the main event loop. Handles mouse movement, button clicks, keyboard input, window management, dirty-rectangle compositing, and LFB flush. Returns when `dvxQuit()` is called or ESC is pressed. ```c void dvxShutdown(AppContextT *ctx); ``` Restore text mode, release LFB mapping, and free the backbuffer. ```c void dvxQuit(AppContextT *ctx); ``` Request exit from the main loop. `dvxRun()` returns on the next iteration. ### Windows ```c WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable); ``` Create a window at screen position (`x`, `y`) with outer dimensions `w` x `h`. If `resizable` is true, the window gets resize handles and a maximize button. The window is raised to the top and given focus. Returns NULL on failure. After creation, set `win->userData` and install callbacks: | Callback | Signature | When called | |----------|-----------|-------------| | `onPaint` | `void (WindowT *win, RectT *dirtyArea)` | Content needs redrawing | | `onKey` | `void (WindowT *win, int32_t key, int32_t mod)` | Key press (focused window) | | `onMouse` | `void (WindowT *win, int32_t x, int32_t y, int32_t buttons)` | Mouse event in content area | | `onResize` | `void (WindowT *win, int32_t newW, int32_t newH)` | Window resized | | `onClose` | `void (WindowT *win)` | Close button double-clicked | | `onMenu` | `void (WindowT *win, int32_t menuId)` | Menu item selected | | `onScroll` | `void (WindowT *win, ScrollbarOrientE orient, int32_t value)` | Scrollbar moved | Mouse/key coordinates are relative to the content area. `buttons` is a bitmask (bit 0 = left, bit 1 = right, bit 2 = middle). ```c void dvxDestroyWindow(AppContextT *ctx, WindowT *win); ``` Remove a window from the stack and free its resources. ```c void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title); ``` Change the title bar text. ```c int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path); ``` Load a BMP, TGA, or PNG image and assign it as the window's minimized icon. The image is converted to the display pixel format and scaled to 64x64. Returns 0 on success. ### Invalidation ```c void dvxInvalidateRect(AppContextT *ctx, WindowT *win, int32_t x, int32_t y, int32_t w, int32_t h); void dvxInvalidateWindow(AppContextT *ctx, WindowT *win); ``` Mark a region (or the entire content area) as needing repaint. The compositor flushes dirty rectangles to the LFB on the next frame. ### Accessors ```c DisplayT *dvxGetDisplay(AppContextT *ctx); const BlitOpsT *dvxGetBlitOps(const AppContextT *ctx); const BitmapFontT *dvxGetFont(const AppContextT *ctx); const ColorSchemeT *dvxGetColors(const AppContextT *ctx); ``` ### Window properties Set these directly on the `WindowT` struct after creation: | Field | Type | Default | Description | |-------|------|---------|-------------| | `maxW` | `int32_t` | -1 | Maximum width when maximized (-1 = screen width) | | `maxH` | `int32_t` | -1 | Maximum height when maximized (-1 = screen height) | | `userData` | `void *` | NULL | Application data pointer, passed through to callbacks | ### Content buffer Each window has a persistent content backbuffer: | Field | Description | |-------|-------------| | `contentBuf` | Pixel data in display format | | `contentPitch` | Bytes per scanline | | `contentW` | Width in pixels | | `contentH` | Height in pixels | Paint callbacks write directly into `contentBuf`. The compositor copies visible portions to the screen backbuffer, then flushes dirty rects to the LFB. --- ## Menu bars ```c MenuBarT *wmAddMenuBar(WindowT *win); ``` Add a menu bar to a window. Call `wmUpdateContentRect()` and `wmReallocContentBuf()` after adding the menu bar (the menu bar reduces the content area). ```c MenuT *wmAddMenu(MenuBarT *bar, const char *label); ``` Add a top-level menu to the bar. Returns a `MenuT *` to populate with items. ```c void wmAddMenuItem(MenuT *menu, const char *label, int32_t id); void wmAddMenuSeparator(MenuT *menu); ``` Add items to a menu. `id` is an application-defined command passed to `onMenu`. Up to 16 items per menu, 8 menus per bar. --- ## Scrollbars ```c ScrollbarT *wmAddVScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize); ScrollbarT *wmAddHScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize); ``` Add a scrollbar to a window. `pageSize` controls the thumb size relative to the range. Call `wmUpdateContentRect()` and `wmReallocContentBuf()` afterward. The `onScroll` callback fires when the user drags the thumb or clicks the arrow buttons / trough. Widget windows manage their own scrollbars automatically -- do not add them manually. --- ## Video and drawing (`dvxVideo.h`, `dvxDraw.h`) These are lower-level APIs. Application code typically only needs `packColor`. ```c uint32_t packColor(const DisplayT *d, uint8_t r, uint8_t g, uint8_t b); ``` Pack an RGB triple into the display's pixel format. ```c void rectFill(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); ``` Fill a rectangle with a solid color (clipped to the display clip rect). ```c void drawBevel(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const BevelStyleT *style); ``` Draw a beveled frame. `BevelStyleT` specifies highlight, shadow, face colors and border width. ```c void drawText(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, uint32_t fg, uint32_t bg, bool opaque); int32_t drawChar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, char ch, uint32_t fg, uint32_t bg, bool opaque); int32_t textWidth(const BitmapFontT *font, const char *text); ``` Draw text using the built-in 8-pixel-wide bitmap font. `opaque` controls whether background pixels are drawn. `drawChar` returns the advance width. ```c void drawHLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, uint32_t color); void drawVLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t h, uint32_t color); ``` ```c void rectCopy(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h); ``` Blit from a source buffer to the backbuffer with clipping. ```c void setClipRect(DisplayT *d, int32_t x, int32_t y, int32_t w, int32_t h); void resetClipRect(DisplayT *d); ``` --- ## Widget system (`dvxWidget.h`) An optional declarative layout system inspired by Amiga MUI and PC GEOS. Widgets are arranged in a tree of VBox/HBox containers. A two-pass layout engine (measure minimum sizes, then distribute space) handles geometry automatically. Scrollbars appear when the window is too small for the content. ### Initialization ```c WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win); ``` Attach the widget system to a window. Returns the root container (a VBox filling the content area). Installs `onPaint`, `onMouse`, `onKey`, and `onResize` handlers on the window. Build the widget tree by passing the returned root (or its children) as `parent` to the widget creation functions below. Call `wgtInvalidate(root)` after the tree is fully built to trigger the initial layout and paint. ### Containers ```c WidgetT *wgtVBox(WidgetT *parent); // vertical stack WidgetT *wgtHBox(WidgetT *parent); // horizontal stack WidgetT *wgtFrame(WidgetT *parent, const char *title); // titled border ``` Containers hold child widgets and control layout direction. `wgtFrame` draws a beveled border with a title label and lays out children vertically. Container properties (set directly on the returned `WidgetT *`): | Field | Type | Default | Description | |-------|------|---------|-------------| | `align` | `WidgetAlignE` | `AlignStartE` | Main-axis alignment when no children have weight | | `spacing` | `int32_t` | 0 (4px) | Tagged size: gap between children | | `padding` | `int32_t` | 0 (4px) | Tagged size: internal padding | Alignment values: | Value | HBox meaning | VBox meaning | |-------|-------------|-------------| | `AlignStartE` | left | top | | `AlignCenterE` | center | center | | `AlignEndE` | right | bottom | ### Widgets ```c WidgetT *wgtLabel(WidgetT *parent, const char *text); ``` Static text label. Sized to fit its text. ```c WidgetT *wgtButton(WidgetT *parent, const char *text); ``` Beveled push button. Set `onClick` to handle clicks. ```c WidgetT *wgtCheckbox(WidgetT *parent, const char *text); ``` Toggle checkbox. Read/write `w->as.checkbox.checked`. Set `onChange` to be notified. ```c WidgetT *wgtRadioGroup(WidgetT *parent); WidgetT *wgtRadio(WidgetT *parent, const char *text); ``` Radio buttons. Create a group, then add options as children. The group tracks the selected index in `w->as.radioGroup.selectedIdx`. Set `onChange` on the group to be notified. Radio indices are auto-assigned. ```c WidgetT *wgtTextInput(WidgetT *parent, int32_t maxLen); ``` Single-line text input field. Supports typing, cursor movement (arrows, Home, End), backspace, and delete. `maxLen` is the buffer capacity. Default weight is 100 (stretches to fill). Set `onChange` to be notified on edits. ```c WidgetT *wgtListBox(WidgetT *parent); ``` List box (basic -- set items with `wgtListBoxSetItems()`). ```c WidgetT *wgtTextArea(WidgetT *parent, int32_t maxLen); ``` Multi-line text area (basic). ### Spacing and dividers ```c WidgetT *wgtSpacer(WidgetT *parent); ``` Invisible spacer. Default weight is 100 -- pushes siblings apart. ```c WidgetT *wgtHSeparator(WidgetT *parent); // horizontal line WidgetT *wgtVSeparator(WidgetT *parent); // vertical line ``` Visual divider lines (shadow + highlight, 2px). ### Size specifications Size fields (`minW`, `minH`, `maxW`, `maxH`, `prefW`, `prefH`, `spacing`, `padding`) accept tagged values created with: ```c wgtPixels(120) // 120 pixels wgtChars(15) // 15 character widths wgtPercent(50) // 50% of parent ``` A raw `0` means auto (use the widget's natural/computed size). ### Weight The `weight` field controls how extra space is distributed among siblings. When a container is larger than its children's combined minimum size, the surplus is divided proportionally by weight. - `weight = 0` -- fixed size, does not stretch (default for most widgets) - `weight = 100` -- normal stretch (default for spacers and text inputs) - Relative values work: weights of 100, 200, 100 give a 1:2:1 split When all children have weight 0, the container's `align` property determines where children are placed within the extra space. ### Operations ```c void wgtSetText(WidgetT *w, const char *text); const char *wgtGetText(const WidgetT *w); ``` Get/set text for labels, buttons, checkboxes, radios, and text inputs. ```c void wgtSetEnabled(WidgetT *w, bool enabled); void wgtSetVisible(WidgetT *w, bool visible); ``` Disabled widgets are drawn grayed out and ignore input. Hidden widgets are excluded from layout. ```c void wgtInvalidate(WidgetT *w); ``` Trigger a full relayout and repaint of the widget tree. Call after modifying widget properties, adding/removing widgets, or changing text. ```c WidgetT *wgtFind(WidgetT *root, const char *name); ``` Search the subtree for a widget by name. Set `w->name` (up to 31 chars) to make a widget findable. ```c void wgtDestroy(WidgetT *w); ``` Remove a widget and all its children from the tree and free memory. ### List box operations ```c void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count); int32_t wgtListBoxGetSelected(const WidgetT *w); void wgtListBoxSetSelected(WidgetT *w, int32_t idx); ``` --- ## Types reference (`dvxTypes.h`) ### WindowT Core window structure. Key fields: | Field | Type | Description | |-------|------|-------------| | `id` | `int32_t` | Unique window identifier | | `x`, `y`, `w`, `h` | `int32_t` | Outer frame position and size | | `contentX`, `contentY` | `int32_t` | Content area offset within frame | | `contentW`, `contentH` | `int32_t` | Content area dimensions | | `contentBuf` | `uint8_t *` | Pixel buffer (display format) | | `contentPitch` | `int32_t` | Bytes per scanline in content buffer | | `title` | `char[128]` | Title bar text | | `visible` | `bool` | Window is shown | | `focused` | `bool` | Window has input focus | | `minimized` | `bool` | Window is minimized to icon | | `maximized` | `bool` | Window is maximized | | `resizable` | `bool` | Window has resize handles | | `maxW`, `maxH` | `int32_t` | Max dimensions for maximize (-1 = screen) | | `userData` | `void *` | Application data pointer | ### ColorSchemeT All colors are pre-packed via `packColor()`: | Field | Usage | |-------|-------| | `desktop` | Desktop background | | `windowFace` | Window frame fill | | `windowHighlight` | Bevel light edge | | `windowShadow` | Bevel dark edge | | `activeTitleBg/Fg` | Focused title bar | | `inactiveTitleBg/Fg` | Unfocused title bar | | `contentBg/Fg` | Window content area | | `menuBg/Fg` | Menu bar and popups | | `menuHighlightBg/Fg` | Highlighted menu item | | `buttonFace` | Button interior | | `scrollbarBg/Fg/Trough` | Scrollbar elements | ### BitmapFontT Fixed-width bitmap font (8px wide, 14 or 16px tall). Glyphs are packed 1bpp, `charHeight` bytes per glyph, MSB-first. ### BevelStyleT ```c typedef struct { uint32_t highlight; // top/left edge color uint32_t shadow; // bottom/right edge color uint32_t face; // interior fill (0 = no fill) int32_t width; // border thickness in pixels } BevelStyleT; ``` --- ## Window chrome Windows use a Motif/GEOS-style frame: - 4px beveled outer border with perpendicular groove breaks - 20px title bar (dark charcoal background when focused) - Close button on the left edge (requires double-click) - Minimize button on the right edge (always present) - Maximize button to the left of minimize (resizable windows only) - Optional menu bar below the title bar (20px) - Optional scrollbars along the right and bottom edges (16px) Minimized windows appear as 64x64 beveled icons along the bottom of the screen. If a window has an icon image set via `dvxSetWindowIcon()`, that image is shown; otherwise a nearest-neighbor-scaled snapshot of the window content is used. Double-click an icon to restore. --- ## Hardware requirements - 486 or later CPU - VESA VBE 2.0+ compatible video card with linear framebuffer support - PS/2 mouse (or compatible mouse driver) - DPMI host (CWSDPMI, Windows DOS box, DOSBox, 86Box) Tested on 86Box with PCI video cards. DOSBox is a trusted reference platform.