// demo.c — DV/X GUI demonstration application #include "dvxApp.h" #include "dvxDialog.h" #include "dvxWidget.h" #include #include #include #include #include "thirdparty/stb_image.h" // ============================================================ // 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_EDIT_CUT 200 #define CMD_EDIT_COPY 201 #define CMD_EDIT_PASTE 202 #define CMD_VIEW_TERM 300 #define CMD_VIEW_CTRL 301 #define CMD_VIEW_ZOOM_IN 302 #define CMD_VIEW_ZOOM_OUT 303 #define CMD_VIEW_ZOOM_FIT 304 #define CMD_VIEW_TOOLBAR 305 #define CMD_VIEW_STATUSBAR 306 #define CMD_VIEW_SIZE_SMALL 307 #define CMD_VIEW_SIZE_MED 308 #define CMD_VIEW_SIZE_LARGE 309 #define CMD_WIN_CASCADE 350 #define CMD_WIN_TILE_H 351 #define CMD_WIN_TILE_V 352 #define CMD_WIN_TILE 353 #define CMD_HELP_ABOUT 400 #define CMD_CTX_CUT 500 #define CMD_CTX_COPY 501 #define CMD_CTX_PASTE 502 #define CMD_CTX_DELETE 503 #define CMD_CTX_SELALL 504 #define CMD_CTX_PROPS 505 // ============================================================ // Prototypes // ============================================================ static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch); static void onCloseCb(WindowT *win); static void onCloseMainCb(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 onToolbarClick(WidgetT *w); static void setupControlsWindow(AppContextT *ctx); static void setupMainWindow(AppContextT *ctx); static void setupTerminalWindow(AppContextT *ctx); static void setupWidgetDemo(AppContextT *ctx); // ============================================================ // Globals // ============================================================ static AppContextT *sCtx = NULL; // ============================================================ // loadBmpPixels — load a BMP/PNG file into display-format pixels // ============================================================ static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch) { int imgW; int imgH; int channels; uint8_t *rgb = stbi_load(path, &imgW, &imgH, &channels, 3); if (!rgb) { return NULL; } const DisplayT *d = dvxGetDisplay(ctx); int32_t bpp = d->format.bytesPerPixel; int32_t pitch = imgW * bpp; uint8_t *data = (uint8_t *)malloc(pitch * imgH); if (!data) { stbi_image_free(rgb); return NULL; } for (int32_t y = 0; y < imgH; y++) { for (int32_t x = 0; x < imgW; x++) { uint8_t *src = rgb + (y * imgW + x) * 3; uint32_t color = packColor(d, src[0], src[1], src[2]); uint8_t *dst = data + y * pitch + x * bpp; if (bpp == 1) { *dst = (uint8_t)color; } else if (bpp == 2) { *(uint16_t *)dst = (uint16_t)color; } else { *(uint32_t *)dst = color; } } } stbi_image_free(rgb); *outW = imgW; *outH = imgH; *outPitch = pitch; return data; } // ============================================================ // onCloseCb // ============================================================ static void onCloseCb(WindowT *win) { AppContextT *ctx = (AppContextT *)win->userData; if (ctx) { dvxDestroyWindow(ctx, win); } } // ============================================================ // onCloseMainCb // ============================================================ static void onCloseMainCb(WindowT *win) { AppContextT *ctx = (AppContextT *)win->userData; if (ctx) { int32_t result = dvxMessageBox(ctx, "Exit", "Are you sure you want to exit?", MB_YESNO | MB_ICONQUESTION); if (result == ID_YES) { dvxQuit(ctx); } } } // ============================================================ // onMenuCb // ============================================================ static const FileFilterT sFileFilters[] = { {"All Files (*.*)", "*.*"}, {"Text Files (*.txt)", "*.txt"}, {"Batch Files (*.bat)", "*.bat"}, {"Executables (*.exe)", "*.exe"}, {"Bitmap Files (*.bmp)", "*.bmp"} }; static void onMenuCb(WindowT *win, int32_t menuId) { AppContextT *ctx = (AppContextT *)win->userData; switch (menuId) { case CMD_FILE_OPEN: { char path[260]; if (dvxFileDialog(ctx, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) { char msg[300]; snprintf(msg, sizeof(msg), "Selected: %s", path); dvxMessageBox(ctx, "Open", msg, MB_OK | MB_ICONINFO); } break; } case CMD_FILE_SAVE: { char path[260]; if (dvxFileDialog(ctx, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) { char msg[300]; snprintf(msg, sizeof(msg), "Save to: %s", path); dvxMessageBox(ctx, "Save", msg, MB_OK | MB_ICONINFO); } break; } case CMD_FILE_EXIT: if (ctx) { int32_t result = dvxMessageBox(ctx, "Exit", "Are you sure you want to exit?", MB_YESNO | MB_ICONQUESTION); if (result == ID_YES) { dvxQuit(ctx); } } break; case CMD_VIEW_TERM: setupTerminalWindow(ctx); break; case CMD_VIEW_CTRL: setupControlsWindow(ctx); break; case CMD_WIN_CASCADE: dvxCascadeWindows(ctx); break; case CMD_WIN_TILE_H: dvxTileWindowsH(ctx); break; case CMD_WIN_TILE_V: dvxTileWindowsV(ctx); break; case CMD_WIN_TILE: dvxTileWindows(ctx); break; case CMD_HELP_ABOUT: dvxMessageBox(sCtx, "About DV/X Demo", "DV/X GUI Demonstration\n\n" "A DESQview/X-style windowing system for DOS.", MB_OK | MB_ICONINFO); break; case CMD_CTX_CUT: dvxMessageBox(ctx, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO); break; case CMD_CTX_COPY: dvxMessageBox(ctx, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO); break; case CMD_CTX_PASTE: dvxMessageBox(ctx, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO); break; case CMD_CTX_DELETE: dvxMessageBox(ctx, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO); break; case CMD_CTX_SELALL: dvxMessageBox(ctx, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO); break; case CMD_CTX_PROPS: dvxMessageBox(ctx, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO); break; } } // ============================================================ // onOkClick // ============================================================ static void onOkClick(WidgetT *w) { 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; 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; 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); } 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/resizable windows", " - Menu bars with accelerators", " - Scrollbars", " - Widget system", " - ANSI terminal emulator", 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; } } // ============================================================ // onToolbarClick // ============================================================ static void onToolbarClick(WidgetT *w) { WidgetT *root = w; while (root->parent) { root = root->parent; } WidgetT *status = wgtFind(root, "advStatus"); if (status) { wgtSetText(status, wgtGetText(w)); wgtInvalidate(status); } } // ============================================================ // setupControlsWindow — advanced widgets with tabs // ============================================================ static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"}; static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"}; static void setupControlsWindow(AppContextT *ctx) { WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 360, 440, 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, "Co&lor:"); WidgetT *dd = wgtDropdown(ddRow); wgtDropdownSetItems(dd, colorItems, 6); wgtDropdownSetSelected(dd, 0); wgtSetTooltip(dd, "Choose a color"); WidgetT *cbRow = wgtHBox(page1); wgtLabel(cbRow, "Si&ze:"); WidgetT *cb = wgtComboBox(cbRow, 32); wgtComboBoxSetItems(cb, sizeItems, 4); wgtComboBoxSetSelected(cb, 1); wgtSetTooltip(cb, "Type or select a size"); wgtHSeparator(page1); wgtLabel(page1, "&Progress:"); WidgetT *pbRow = wgtHBox(page1); WidgetT *pb = wgtProgressBar(pbRow); pb->weight = 100; wgtProgressBarSetValue(pb, 65); wgtSetTooltip(pb, "Task progress: 65%"); WidgetT *pbV = wgtProgressBarV(pbRow); wgtProgressBarSetValue(pbV, 75); wgtSetTooltip(pbV, "Vertical progress: 75%"); wgtLabel(page1, "&Volume:"); WidgetT *slider = wgtSlider(page1, 0, 100); wgtSetTooltip(slider, "Adjust volume level"); WidgetT *spinRow = wgtHBox(page1); spinRow->maxH = wgtPixels(30); wgtLabel(spinRow, "&Quantity:"); WidgetT *spin = wgtSpinner(spinRow, 0, 999, 1); wgtSpinnerSetValue(spin, 42); spin->weight = 50; wgtSetTooltip(spin, "Enter quantity (0-999)"); // --- Tab 2: Tree --- WidgetT *page2 = wgtTabPage(tabs, "&Tree"); WidgetT *tree = wgtTreeView(page2); wgtTreeViewSetReorderable(tree, true); 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: ListView (multi-column list) --- WidgetT *page3 = wgtTabPage(tabs, "&List"); static const ListViewColT lvCols[] = { {"Name", (int32_t)(WGT_SIZE_CHARS | 16), ListViewAlignLeftE}, {"Size", (int32_t)(WGT_SIZE_CHARS | 8), ListViewAlignRightE}, {"Type", (int32_t)(WGT_SIZE_CHARS | 12), ListViewAlignLeftE}, {"Modified", (int32_t)(WGT_SIZE_CHARS | 12), ListViewAlignLeftE} }; static const char *lvData[] = { "AUTOEXEC.BAT", "412", "Batch File", "03/15/1994", "CONFIG.SYS", "256", "System File", "03/15/1994", "COMMAND.COM", "54,645", "Application", "09/30/1993", "HIMEM.SYS", "29,136", "System Driver", "09/30/1993", "EMM386.EXE", "120,926", "Application", "09/30/1993", "MOUSE.COM", "56,408", "Application", "06/01/1994", "DOSKEY.COM", "5,883", "Application", "09/30/1993", "EDIT.COM", "413", "Application", "09/30/1993", "README.TXT", "8,192", "Text File", "01/10/1994", "DVXDEMO.EXE", "98,304", "Application", "03/15/2026" }; WidgetT *lv = wgtListView(page3); wgtListViewSetColumns(lv, lvCols, 4); wgtListViewSetData(lv, lvData, 10); wgtListViewSetSelected(lv, 0); wgtListViewSetMultiSelect(lv, true); wgtListViewSetReorderable(lv, true); lv->weight = 100; // --- Tab 4: ScrollPane --- WidgetT *page4sp = wgtTabPage(tabs, "&Scroll"); WidgetT *sp = wgtScrollPane(page4sp); sp->weight = 100; sp->padding = wgtPixels(4); sp->spacing = wgtPixels(4); wgtLabel(sp, "ScrollPane Demo:"); wgtHSeparator(sp); static const char *spItems[] = { "Item &1", "Item &2", "Item &3", "Item &4", "Item &5", "Item &6", "Item &7", "Item &8" }; for (int32_t i = 0; i < 8; i++) { wgtCheckbox(sp, spItems[i]); } wgtHSeparator(sp); wgtLabel(sp, "More widgets below:"); WidgetT *spRg = wgtRadioGroup(sp); static const char *spRadios[] = { "Radio 1", "Radio 2", "Radio 3", "Radio 4", "Radio 5" }; for (int32_t i = 0; i < 5; i++) { wgtRadio(spRg, spRadios[i]); } wgtHSeparator(sp); wgtLabel(sp, "Bottom of scroll area"); wgtButton(sp, "Scrolled &Button"); // --- Tab 5: Toolbar (ImageButtons + VSeparator) --- WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar"); WidgetT *tb = wgtToolbar(page5tb); int32_t imgW; int32_t imgH; int32_t imgPitch; uint8_t *newData = loadBmpPixels(ctx, "new.bmp", &imgW, &imgH, &imgPitch); if (newData) { WidgetT *btnNew = wgtImageButton(tb, newData, imgW, imgH, imgPitch); strncpy(btnNew->name, "New", MAX_WIDGET_NAME); btnNew->onClick = onToolbarClick; } uint8_t *openData = loadBmpPixels(ctx, "open.bmp", &imgW, &imgH, &imgPitch); if (openData) { WidgetT *btnOpen = wgtImageButton(tb, openData, imgW, imgH, imgPitch); strncpy(btnOpen->name, "Open", MAX_WIDGET_NAME); btnOpen->onClick = onToolbarClick; } uint8_t *saveData = loadBmpPixels(ctx, "save.bmp", &imgW, &imgH, &imgPitch); if (saveData) { WidgetT *btnSave = wgtImageButton(tb, saveData, imgW, imgH, imgPitch); strncpy(btnSave->name, "Save", MAX_WIDGET_NAME); btnSave->onClick = onToolbarClick; } wgtVSeparator(tb); WidgetT *btnHelp = wgtButton(tb, "&Help"); btnHelp->onClick = onToolbarClick; wgtLabel(page5tb, "ImageButtons with VSeparator."); // --- Tab 6: Media (Image, ImageFromFile) --- WidgetT *page6m = wgtTabPage(tabs, "&Media"); wgtLabel(page6m, "ImageFromFile (sample.bmp):"); wgtImageFromFile(page6m, "sample.bmp"); wgtHSeparator(page6m); wgtLabel(page6m, "Image (logo.bmp):"); WidgetT *imgRow = wgtHBox(page6m); uint8_t *logoData = loadBmpPixels(ctx, "logo.bmp", &imgW, &imgH, &imgPitch); if (logoData) { wgtImage(imgRow, logoData, imgW, imgH, imgPitch); } wgtVSeparator(imgRow); wgtLabel(imgRow, "32x32 DV/X logo"); // --- Tab 7: Editor (TextArea, Canvas) --- WidgetT *page7e = wgtTabPage(tabs, "&Editor"); wgtLabel(page7e, "TextArea:"); WidgetT *ta = wgtTextArea(page7e, 512); ta->weight = 100; wgtSetText(ta, "Multi-line text editor.\n\nFeatures:\n- Word wrap\n- Selection\n- Copy/Paste\n- Undo (Ctrl+Z)"); wgtHSeparator(page7e); wgtLabel(page7e, "Canvas (draw with mouse):"); const DisplayT *d = dvxGetDisplay(ctx); WidgetT *cv = wgtCanvas(page7e, 280, 80); wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0)); wgtCanvasDrawRect(cv, 5, 5, 50, 35); wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 200)); wgtCanvasFillCircle(cv, 150, 40, 25); wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0)); wgtCanvasDrawLine(cv, 70, 5, 130, 70); wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0)); // --- Tab 8: Splitter --- WidgetT *page8s = wgtTabPage(tabs, "S&plit"); // Outer horizontal splitter: explorer on top, detail on bottom WidgetT *hSplit = wgtSplitter(page8s, false); hSplit->weight = 100; wgtSplitterSetPos(hSplit, 120); // Top pane: vertical splitter (tree | list) WidgetT *vSplit = wgtSplitter(hSplit, true); wgtSplitterSetPos(vSplit, 120); // Left pane: multi-select tree view WidgetT *leftTree = wgtTreeView(vSplit); wgtTreeViewSetMultiSelect(leftTree, true); WidgetT *tiFolders = wgtTreeItem(leftTree, "Folders"); wgtTreeItemSetExpanded(tiFolders, true); wgtTreeItem(tiFolders, "Documents"); wgtTreeItem(tiFolders, "Pictures"); wgtTreeItem(tiFolders, "Music"); WidgetT *tiSystem = wgtTreeItem(leftTree, "System"); wgtTreeItemSetExpanded(tiSystem, true); wgtTreeItem(tiSystem, "Config"); wgtTreeItem(tiSystem, "Drivers"); // Right pane: list box WidgetT *rightList = wgtListBox(vSplit); static const char *explorerItems[] = { "README.TXT", "SETUP.EXE", "CONFIG.SYS", "AUTOEXEC.BAT", "COMMAND.COM", "HIMEM.SYS", "EMM386.EXE", "MOUSE.COM" }; wgtListBoxSetItems(rightList, explorerItems, 8); // Bottom pane: detail/preview area WidgetT *detailFrame = wgtFrame(hSplit, "Preview"); wgtLabel(detailFrame, "Select a file above to preview."); // --- Tab 9: Disabled --- WidgetT *page9d = wgtTabPage(tabs, "&Disabled"); // Left column: enabled widgets Right column: disabled widgets WidgetT *disRow = wgtHBox(page9d); disRow->weight = 100; // Enabled column WidgetT *enCol = wgtVBox(disRow); enCol->weight = 100; wgtLabel(enCol, "Enabled:"); wgtHSeparator(enCol); wgtLabel(enCol, "A &label"); wgtButton(enCol, "A &button"); WidgetT *enChk = wgtCheckbox(enCol, "&Check me"); enChk->as.checkbox.checked = true; WidgetT *enRg = wgtRadioGroup(enCol); wgtRadio(enRg, "&Radio 1"); WidgetT *enR2 = wgtRadio(enRg, "R&adio 2"); enRg->as.radioGroup.selectedIdx = enR2->as.radio.index; static const char *disItems[] = {"Alpha", "Beta", "Gamma"}; WidgetT *enDd = wgtDropdown(enCol); wgtDropdownSetItems(enDd, disItems, 3); wgtDropdownSetSelected(enDd, 0); WidgetT *enSlider = wgtSlider(enCol, 0, 100); wgtSliderSetValue(enSlider, 40); WidgetT *enProg = wgtProgressBar(enCol); wgtProgressBarSetValue(enProg, 60); WidgetT *enSpin = wgtSpinner(enCol, 0, 100, 1); wgtSpinnerSetValue(enSpin, 42); WidgetT *enText = wgtTextInput(enCol, 64); wgtSetText(enText, "Editable"); // Disabled column WidgetT *disCol = wgtVBox(disRow); disCol->weight = 100; wgtLabel(disCol, "Disabled:"); wgtHSeparator(disCol); WidgetT *dLbl = wgtLabel(disCol, "A &label"); wgtSetEnabled(dLbl, false); WidgetT *dBtn = wgtButton(disCol, "A &button"); wgtSetEnabled(dBtn, false); WidgetT *dChk = wgtCheckbox(disCol, "&Check me"); dChk->as.checkbox.checked = true; wgtSetEnabled(dChk, false); WidgetT *dRg = wgtRadioGroup(disCol); WidgetT *dR1 = wgtRadio(dRg, "&Radio 1"); WidgetT *dR2 = wgtRadio(dRg, "R&adio 2"); dRg->as.radioGroup.selectedIdx = dR2->as.radio.index; wgtSetEnabled(dR1, false); wgtSetEnabled(dR2, false); WidgetT *dDd = wgtDropdown(disCol); wgtDropdownSetItems(dDd, disItems, 3); wgtDropdownSetSelected(dDd, 0); wgtSetEnabled(dDd, false); WidgetT *dSlider = wgtSlider(disCol, 0, 100); wgtSliderSetValue(dSlider, 40); wgtSetEnabled(dSlider, false); WidgetT *dProg = wgtProgressBar(disCol); wgtProgressBarSetValue(dProg, 60); wgtSetEnabled(dProg, false); WidgetT *dSpin = wgtSpinner(disCol, 0, 100, 1); wgtSpinnerSetValue(dSpin, 42); wgtSetEnabled(dSpin, false); WidgetT *dText = wgtTextInput(disCol, 64); wgtSetText(dText, "Read-only"); wgtSetEnabled(dText, false); // Status bar at bottom (outside tabs) WidgetT *sb = wgtStatusBar(root); WidgetT *sbLabel = wgtLabel(sb, "Ready"); sbLabel->weight = 100; strncpy(sbLabel->name, "advStatus", MAX_WIDGET_NAME); wgtLabel(sb, "Line 1, Col 1"); wgtInvalidate(root); } // ============================================================ // setupMainWindow — info window + paint demos // ============================================================ static void setupMainWindow(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 = onCloseMainCb; 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, "E&xit", CMD_FILE_EXIT); } MenuT *editMenu = wmAddMenu(bar, "&Edit"); if (editMenu) { wmAddMenuItem(editMenu, "Cu&t", CMD_EDIT_CUT); wmAddMenuItem(editMenu, "&Copy", CMD_EDIT_COPY); wmAddMenuItem(editMenu, "&Paste", CMD_EDIT_PASTE); } MenuT *viewMenu = wmAddMenu(bar, "&View"); if (viewMenu) { wmAddMenuItem(viewMenu, "&Terminal", CMD_VIEW_TERM); wmAddMenuItem(viewMenu, "&Controls", CMD_VIEW_CTRL); wmAddMenuSeparator(viewMenu); 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); wmAddMenuSeparator(viewMenu); MenuT *zoomMenu = wmAddSubMenu(viewMenu, "&Zoom"); if (zoomMenu) { wmAddMenuItem(zoomMenu, "Zoom &In", CMD_VIEW_ZOOM_IN); wmAddMenuItem(zoomMenu, "Zoom &Out", CMD_VIEW_ZOOM_OUT); wmAddMenuSeparator(zoomMenu); wmAddMenuItem(zoomMenu, "&Fit to Window", CMD_VIEW_ZOOM_FIT); } } MenuT *winMenu = wmAddMenu(bar, "&Window"); if (winMenu) { wmAddMenuItem(winMenu, "&Cascade", CMD_WIN_CASCADE); wmAddMenuItem(winMenu, "Tile &Grid", CMD_WIN_TILE); wmAddMenuItem(winMenu, "Tile &Horizontal", CMD_WIN_TILE_H); wmAddMenuItem(winMenu, "Tile &Vertical", CMD_WIN_TILE_V); } MenuT *helpMenu = wmAddMenu(bar, "&Help"); if (helpMenu) { wmAddMenuItem(helpMenu, "&About...", CMD_HELP_ABOUT); } } // Accelerator table (global hotkeys) 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); win1->accelTable = accel; // Window-level context menu MenuT *winCtx = wmCreateMenu(); wmAddMenuItem(winCtx, "Cu&t", CMD_CTX_CUT); wmAddMenuItem(winCtx, "&Copy", CMD_CTX_COPY); wmAddMenuItem(winCtx, "&Paste", CMD_CTX_PASTE); wmAddMenuSeparator(winCtx); wmAddMenuItem(winCtx, "&Properties...", CMD_CTX_PROPS); win1->contextMenu = winCtx; wmUpdateContentRect(win1); wmReallocContentBuf(win1, &ctx->display); 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); } } // ============================================================ // setupTerminalWindow — ANSI terminal widget demo // ============================================================ static void setupTerminalWindow(AppContextT *ctx) { WindowT *win = dvxCreateWindow(ctx, "ANSI Terminal", 60, 60, 660, 420, true); if (!win) { return; } win->userData = ctx; win->onClose = onCloseCb; WidgetT *root = wgtInitWindow(ctx, win); WidgetT *term = wgtAnsiTerm(root, 80, 25); term->weight = 100; wgtAnsiTermSetScrollback(term, 500); // Feed some ANSI content to demonstrate the terminal static const uint8_t ansiDemo[] = "\x1B[2J" // clear screen "\x1B[1;34m========================================\r\n" " DV/X ANSI Terminal Emulator\r\n" "========================================\x1B[0m\r\n" "\r\n" "\x1B[1mBold text\x1B[0m, " "\x1B[7mReverse\x1B[0m, " "\x1B[5mBlinking\x1B[0m\r\n" "\r\n" "Standard colors:\r\n" " \x1B[30m\x1B[47m Black \x1B[0m" " \x1B[31m Red \x1B[0m" " \x1B[32m Green \x1B[0m" " \x1B[33m Yellow \x1B[0m" " \x1B[34m Blue \x1B[0m" " \x1B[35m Magenta \x1B[0m" " \x1B[36m Cyan \x1B[0m" " \x1B[37m White \x1B[0m\r\n" "\r\n" "Bright colors:\r\n" " \x1B[1;30m\x1B[47m DkGray \x1B[0m" " \x1B[1;31m LtRed \x1B[0m" " \x1B[1;32m LtGreen \x1B[0m" " \x1B[1;33m LtYellow \x1B[0m" " \x1B[1;34m LtBlue \x1B[0m" " \x1B[1;35m LtMagenta \x1B[0m" " \x1B[1;36m LtCyan \x1B[0m" " \x1B[1;37m BrWhite \x1B[0m\r\n" "\r\n" "Background colors:\r\n" " \x1B[40m\x1B[37m Black \x1B[0m" " \x1B[41m\x1B[37m Red \x1B[0m" " \x1B[42m\x1B[30m Green \x1B[0m" " \x1B[43m\x1B[30m Yellow \x1B[0m" " \x1B[44m\x1B[37m Blue \x1B[0m" " \x1B[45m\x1B[37m Magenta \x1B[0m" " \x1B[46m\x1B[30m Cyan \x1B[0m" " \x1B[47m\x1B[30m White \x1B[0m\r\n" "\r\n" "CP437 graphics: " "\x01\x02\x03\x04\x05\x06" // smileys, hearts, diamonds, clubs, spades " \xB0\xB1\xB2\xDB" // shade blocks " \xC4\xC5\xB3\xDA\xBF\xC0\xD9" // box drawing "\r\n" "\r\n" "\x1B[1;32mTerminal ready.\x1B[0m " "\x1B[36m(Not connected to any host)\x1B[0m\r\n"; wgtAnsiTermWrite(term, ansiDemo, (int32_t)(sizeof(ansiDemo) - 1)); WidgetT *sb = wgtStatusBar(root); wgtLabel(sb, "80x25 [Local]"); dvxFitWindow(ctx, win); wgtInvalidate(root); dvxMinimizeWindow(ctx, win); } // ============================================================ // setupWidgetDemo — form with accelerators // ============================================================ static void setupWidgetDemo(AppContextT *ctx) { WindowT *win = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 360, true); if (!win) { return; } win->userData = ctx; win->onClose = onCloseCb; win->onMenu = onMenuCb; WidgetT *root = wgtInitWindow(ctx, win); // 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); WidgetT *row2 = wgtHBox(frame); wgtLabel(row2, "&Password:"); wgtPasswordInput(row2, 32); WidgetT *row3 = wgtHBox(frame); wgtLabel(row3, "P&hone:"); wgtMaskedInput(row3, "(###) ###-####"); 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); // List boxes static const char *listItems[] = {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"}; static const char *multiItems[] = {"Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"}; WidgetT *listRow = wgtHBox(root); wgtLabel(listRow, "&Items:"); WidgetT *lb = wgtListBox(listRow); wgtListBoxSetItems(lb, listItems, 5); wgtListBoxSetReorderable(lb, true); lb->weight = 100; // Context menu on the list box MenuT *lbCtx = wmCreateMenu(); wmAddMenuItem(lbCtx, "Cu&t", CMD_CTX_CUT); wmAddMenuItem(lbCtx, "&Copy", CMD_CTX_COPY); wmAddMenuItem(lbCtx, "&Paste", CMD_CTX_PASTE); wmAddMenuSeparator(lbCtx); wmAddMenuItem(lbCtx, "&Delete", CMD_CTX_DELETE); wmAddMenuSeparator(lbCtx); wmAddMenuItem(lbCtx, "Select &All", CMD_CTX_SELALL); lb->contextMenu = lbCtx; wgtLabel(listRow, "&Multi:"); WidgetT *mlb = wgtListBox(listRow); wgtListBoxSetMultiSelect(mlb, true); wgtListBoxSetItems(mlb, multiItems, 7); mlb->weight = 100; wgtHSeparator(root); // Button row at bottom WidgetT *btnRow = wgtHBox(root); btnRow->align = AlignEndE; WidgetT *okBtn = wgtButton(btnRow, "&OK"); okBtn->onClick = onOkClick; wgtButton(btnRow, "&Cancel"); wgtInvalidate(root); } // ============================================================ // main // ============================================================ int main(int argc, char **argv) { (void)argc; // Change to executable's directory so relative BMP paths work char exeDir[260]; strncpy(exeDir, argv[0], sizeof(exeDir) - 1); exeDir[sizeof(exeDir) - 1] = '\0'; char *lastSep = strrchr(exeDir, '/'); char *lastBs = strrchr(exeDir, '\\'); if (lastBs > lastSep) { lastSep = lastBs; } if (lastSep) { *lastSep = '\0'; chdir(exeDir); } 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; } sCtx = &ctx; setupMainWindow(&ctx); setupWidgetDemo(&ctx); setupControlsWindow(&ctx); setupTerminalWindow(&ctx); dvxRun(&ctx); dvxShutdown(&ctx); printf("DV/X GUI Demo ended.\n"); return 0; }