// demo.c — DV/X GUI demonstration application #include "dvxApp.h" #include "dvxWidget.h" #include #include // ============================================================ // Menu command IDs // ============================================================ #define CMD_FILE_NEW 100 #define CMD_FILE_OPEN 101 #define CMD_FILE_SAVE 102 #define CMD_FILE_EXIT 103 #define CMD_HELP_ABOUT 200 // ============================================================ // Prototypes // ============================================================ static void onCloseCb(WindowT *win); static void onMenuCb(WindowT *win, int32_t menuId); static void onOkClick(WidgetT *w); static void onPaintColor(WindowT *win, RectT *dirtyArea); static void onPaintPattern(WindowT *win, RectT *dirtyArea); static void onPaintText(WindowT *win, RectT *dirtyArea); static void setupWidgetDemo2(AppContextT *ctx); static void setupWindows(AppContextT *ctx); // ============================================================ // onCloseCb // ============================================================ static void onCloseCb(WindowT *win) { AppContextT *ctx = (AppContextT *)win->userData; if (ctx) { dvxDestroyWindow(ctx, win); } } // ============================================================ // onMenuCb // ============================================================ static void onMenuCb(WindowT *win, int32_t menuId) { AppContextT *ctx = (AppContextT *)win->userData; switch (menuId) { case CMD_FILE_EXIT: if (ctx) { dvxQuit(ctx); } break; case CMD_HELP_ABOUT: // Could create an about dialog window here break; } } // ============================================================ // onOkClick // ============================================================ static void onOkClick(WidgetT *w) { // Find the status label and update it WidgetT *root = w; while (root->parent) { root = root->parent; } WidgetT *status = wgtFind(root, "status"); if (status) { wgtSetText(status, "Button clicked!"); wgtInvalidate(status); } } // ============================================================ // onPaintColor // ============================================================ static void onPaintColor(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; AppContextT *ctx = (AppContextT *)win->userData; if (!win->contentBuf || !ctx) { return; } const DisplayT *d = dvxGetDisplay(ctx); const BlitOpsT *ops = dvxGetBlitOps(ctx); for (int32_t y = 0; y < win->contentH; y++) { uint8_t r = (uint8_t)((y * 255) / (win->contentH > 1 ? win->contentH - 1 : 1)); uint8_t g = (uint8_t)(100); uint8_t b = (uint8_t)(200 - (y * 150) / (win->contentH > 1 ? win->contentH - 1 : 1)); uint32_t color = packColor(d, r, g, b); uint8_t *row = win->contentBuf + y * win->contentPitch; ops->spanFill(row, color, win->contentW); } } // ============================================================ // onPaintPattern // ============================================================ static void onPaintPattern(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; AppContextT *ctx = (AppContextT *)win->userData; if (!win->contentBuf || !ctx) { return; } const DisplayT *d = dvxGetDisplay(ctx); int32_t bpp = d->format.bytesPerPixel; int32_t sq = 16; // square size uint32_t c1 = packColor(d, 255, 255, 255); uint32_t c2 = packColor(d, 0, 0, 180); for (int32_t y = 0; y < win->contentH; y++) { for (int32_t x = 0; x < win->contentW; x++) { uint32_t color = ((x / sq) + (y / sq)) & 1 ? c2 : c1; uint8_t *px = win->contentBuf + y * win->contentPitch + x * bpp; if (bpp == 1) { *px = (uint8_t)color; } else if (bpp == 2) { *(uint16_t *)px = (uint16_t)color; } else { *(uint32_t *)px = color; } } } } // ============================================================ // onPaintText // ============================================================ static void onPaintText(WindowT *win, RectT *dirtyArea) { (void)dirtyArea; AppContextT *ctx = (AppContextT *)win->userData; if (!win->contentBuf || !ctx) { return; } const DisplayT *d = dvxGetDisplay(ctx); const BlitOpsT *ops = dvxGetBlitOps(ctx); const BitmapFontT *font = dvxGetFont(ctx); int32_t bpp = d->format.bytesPerPixel; // Fill white background uint32_t bg = packColor(d, 255, 255, 255); uint32_t fg = packColor(d, 0, 0, 0); for (int32_t y = 0; y < win->contentH; y++) { ops->spanFill(win->contentBuf + y * win->contentPitch, bg, win->contentW); } // Draw text lines directly into the content buffer static const char *lines[] = { "DV/X GUI Compositor", "", "A DESQview/X-style windowed GUI", "compositor for DOS, targeting", "DJGPP/DPMI.", "", "Features:", " - VESA VBE 2.0+ LFB", " - Dirty-rect compositing", " - Beveled Motif-style chrome", " - Draggable windows", " - Resizable windows", " - Menu bars", " - Scrollbars", " - Widget system", "", "Press ESC to exit.", NULL }; int32_t textY = 4; for (int32_t i = 0; lines[i] != NULL; i++) { const char *line = lines[i]; int32_t textX = 4; for (int32_t j = 0; line[j] != '\0'; j++) { int32_t idx = (uint8_t)line[j] - font->firstChar; if (idx < 0 || idx >= font->numChars) { textX += font->charWidth; continue; } const uint8_t *glyph = font->glyphData + idx * font->charHeight; for (int32_t row = 0; row < font->charHeight; row++) { int32_t py = textY + row; if (py >= win->contentH) { break; } uint8_t bits = glyph[row]; for (int32_t col = 0; col < font->charWidth; col++) { int32_t px = textX + col; if (px >= win->contentW) { break; } if (bits & (0x80 >> col)) { uint8_t *dst = win->contentBuf + py * win->contentPitch + px * bpp; if (bpp == 1) { *dst = (uint8_t)fg; } else if (bpp == 2) { *(uint16_t *)dst = (uint16_t)fg; } else { *(uint32_t *)dst = fg; } } } } textX += font->charWidth; } textY += font->charHeight + 2; } } // ============================================================ // setupWidgetDemo2 // ============================================================ static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"}; static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"}; static void setupWidgetDemo2(AppContextT *ctx) { WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 320, 400, true); if (!win) { return; } win->userData = ctx; win->onClose = onCloseCb; WidgetT *root = wgtInitWindow(ctx, win); // TabControl at top WidgetT *tabs = wgtTabControl(root); // --- Tab 1: Controls --- WidgetT *page1 = wgtTabPage(tabs, "Controls"); WidgetT *ddRow = wgtHBox(page1); wgtLabel(ddRow, "Color:"); WidgetT *dd = wgtDropdown(ddRow); wgtDropdownSetItems(dd, colorItems, 6); wgtDropdownSetSelected(dd, 0); WidgetT *cbRow = wgtHBox(page1); wgtLabel(cbRow, "Size:"); WidgetT *cb = wgtComboBox(cbRow, 32); wgtComboBoxSetItems(cb, sizeItems, 4); wgtComboBoxSetSelected(cb, 1); wgtHSeparator(page1); wgtLabel(page1, "Progress:"); WidgetT *pb = wgtProgressBar(page1); wgtProgressBarSetValue(pb, 65); wgtLabel(page1, "Volume:"); wgtSlider(page1, 0, 100); // --- Tab 2: Tree --- WidgetT *page2 = wgtTabPage(tabs, "Tree"); WidgetT *tree = wgtTreeView(page2); 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"); wgtTreeItem(src, "render.c"); WidgetT *images = wgtTreeItem(tree, "Images"); wgtTreeItem(images, "logo.png"); wgtTreeItem(images, "icon.bmp"); WidgetT *config = wgtTreeItem(tree, "Config"); wgtTreeItem(config, "settings.ini"); wgtTreeItem(config, "palette.dat"); // --- Tab 3: Toolbar --- WidgetT *page3 = wgtTabPage(tabs, "Toolbar"); WidgetT *tb = wgtToolbar(page3); wgtButton(tb, "New"); wgtButton(tb, "Open"); wgtButton(tb, "Save"); wgtLabel(page3, "Toolbar with buttons above."); // Status bar at bottom (outside tabs) WidgetT *sb = wgtStatusBar(root); WidgetT *sbLabel = wgtLabel(sb, "Ready"); sbLabel->weight = 100; wgtLabel(sb, "Line 1, Col 1"); wgtInvalidate(root); } // ============================================================ // setupWindows // ============================================================ static void setupWindows(AppContextT *ctx) { // Window 1: Text information window with menu bar WindowT *win1 = dvxCreateWindow(ctx, "DV/X Information", 50, 40, 340, 350, true); if (win1) { win1->userData = ctx; win1->onPaint = onPaintText; win1->onClose = onCloseCb; win1->onMenu = onMenuCb; MenuBarT *bar = wmAddMenuBar(win1); if (bar) { MenuT *fileMenu = wmAddMenu(bar, "File"); if (fileMenu) { wmAddMenuItem(fileMenu, "New", CMD_FILE_NEW); wmAddMenuItem(fileMenu, "Open...", CMD_FILE_OPEN); wmAddMenuItem(fileMenu, "Save", CMD_FILE_SAVE); wmAddMenuSeparator(fileMenu); wmAddMenuItem(fileMenu, "Exit", CMD_FILE_EXIT); } MenuT *helpMenu = wmAddMenu(bar, "Help"); if (helpMenu) { wmAddMenuItem(helpMenu, "About...", CMD_HELP_ABOUT); } } wmUpdateContentRect(win1); wmReallocContentBuf(win1, &ctx->display); // Paint initial content RectT fullRect = {0, 0, win1->contentW, win1->contentH}; win1->onPaint(win1, &fullRect); } // Window 2: Color gradient WindowT *win2 = dvxCreateWindow(ctx, "Color Gradient", 200, 100, 280, 250, true); if (win2) { win2->userData = ctx; win2->onPaint = onPaintColor; win2->onClose = onCloseCb; RectT fullRect = {0, 0, win2->contentW, win2->contentH}; win2->onPaint(win2, &fullRect); } // Window 3: Checkerboard pattern with scrollbars WindowT *win3 = dvxCreateWindow(ctx, "Pattern", 400, 150, 250, 220, true); if (win3) { win3->userData = ctx; win3->onPaint = onPaintPattern; win3->onClose = onCloseCb; wmAddVScrollbar(win3, 0, 100, 25); wmAddHScrollbar(win3, 0, 100, 25); wmUpdateContentRect(win3); wmReallocContentBuf(win3, &ctx->display); RectT fullRect = {0, 0, win3->contentW, win3->contentH}; win3->onPaint(win3, &fullRect); } // Window 4: Widget demo WindowT *win4 = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 320, true); if (win4) { win4->userData = ctx; win4->onClose = onCloseCb; WidgetT *root = wgtInitWindow(ctx, win4); // Status label at top WidgetT *status = wgtLabel(root, "Ready."); strncpy(status->name, "status", MAX_WIDGET_NAME); wgtHSeparator(root); // Frame with text input WidgetT *frame = wgtFrame(root, "User Input"); WidgetT *row1 = wgtHBox(frame); wgtLabel(row1, "Name:"); wgtTextInput(row1, 64); wgtHSeparator(root); // Checkboxes wgtCheckbox(root, "Enable feature A"); wgtCheckbox(root, "Enable feature B"); wgtHSeparator(root); // Radio buttons WidgetT *rg = wgtRadioGroup(root); wgtRadio(rg, "Option 1"); wgtRadio(rg, "Option 2"); wgtRadio(rg, "Option 3"); wgtHSeparator(root); // Button row at bottom WidgetT *btnRow = wgtHBox(root); btnRow->align = AlignEndE; WidgetT *okBtn = wgtButton(btnRow, "OK"); okBtn->onClick = onOkClick; wgtButton(btnRow, "Cancel"); // Layout and paint now that widget tree is complete wgtInvalidate(root); } } // ============================================================ // main // ============================================================ int main(void) { AppContextT ctx; printf("DV/X GUI Demo\n"); printf("Initializing VESA video...\n"); if (dvxInit(&ctx, 1024, 768, 16) != 0) { fprintf(stderr, "Failed to initialize DV/X GUI\n"); return 1; } setupWindows(&ctx); setupWidgetDemo2(&ctx); dvxRun(&ctx); dvxShutdown(&ctx); printf("DV/X GUI Demo ended.\n"); return 0; }