More code cleanup.

This commit is contained in:
Scott Duensing 2026-03-17 18:22:16 -05:00
parent 78302c26d2
commit 1556ba889c
28 changed files with 459 additions and 449 deletions

27
Makefile Normal file
View file

@ -0,0 +1,27 @@
# DVX GUI — Top-level Makefile
#
# Builds the full DVX stack: library, task switcher, shell, and apps.
.PHONY: all clean dvx tasks dvxshell apps
all: dvx tasks dvxshell apps
dvx:
$(MAKE) -C dvx
tasks:
$(MAKE) -C tasks
dvxshell: dvx tasks
$(MAKE) -C dvxshell
apps: dvx tasks dvxshell
$(MAKE) -C apps
clean:
$(MAKE) -C dvx clean
$(MAKE) -C tasks clean
$(MAKE) -C dvxshell clean
$(MAKE) -C apps clean
-rmdir obj/dvx/widgets obj/dvx/platform obj/dvx obj/tasks obj/dvxshell obj/apps obj 2>/dev/null
-rmdir bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin lib 2>/dev/null

View file

@ -10,25 +10,32 @@ OBJDIR = ../obj/apps
BINDIR = ../bin/apps BINDIR = ../bin/apps
# App definitions: each is a subdir with a single .c file # App definitions: each is a subdir with a single .c file
APPS = progman notepad clock APPS = progman notepad clock dvxdemo
.PHONY: all clean $(APPS) .PHONY: all clean $(APPS)
all: $(APPS) all: $(APPS)
progman: $(BINDIR)/progman.app progman: $(BINDIR)/progman/progman.app
notepad: $(BINDIR)/notepad.app notepad: $(BINDIR)/notepad/notepad.app
clock: $(BINDIR)/clock.app clock: $(BINDIR)/clock/clock.app
dvxdemo: $(BINDIR)/dvxdemo/dvxdemo.app
$(BINDIR)/progman.app: $(OBJDIR)/progman.o | $(BINDIR) $(BINDIR)/progman/progman.app: $(OBJDIR)/progman.o | $(BINDIR)/progman
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $< $(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
$(BINDIR)/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR) $(BINDIR)/notepad/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)/notepad
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $< $(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
$(BINDIR)/clock.app: $(OBJDIR)/clock.o | $(BINDIR) $(BINDIR)/clock/clock.app: $(OBJDIR)/clock.o | $(BINDIR)/clock
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $< $(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
DVXDEMO_BMPS = logo.bmp new.bmp open.bmp sample.bmp save.bmp
$(BINDIR)/dvxdemo/dvxdemo.app: $(OBJDIR)/dvxdemo.o $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) | $(BINDIR)/dvxdemo
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
cp $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) $(BINDIR)/dvxdemo/
$(OBJDIR)/progman.o: progman/progman.c | $(OBJDIR) $(OBJDIR)/progman.o: progman/progman.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
@ -38,16 +45,33 @@ $(OBJDIR)/notepad.o: notepad/notepad.c | $(OBJDIR)
$(OBJDIR)/clock.o: clock/clock.c | $(OBJDIR) $(OBJDIR)/clock.o: clock/clock.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR): $(OBJDIR):
mkdir -p $(OBJDIR) mkdir -p $(OBJDIR)
$(BINDIR): $(BINDIR)/progman:
mkdir -p $(BINDIR) mkdir -p $(BINDIR)/progman
$(BINDIR)/notepad:
mkdir -p $(BINDIR)/notepad
$(BINDIR)/clock:
mkdir -p $(BINDIR)/clock
$(BINDIR)/dvxdemo:
mkdir -p $(BINDIR)/dvxdemo
# Dependencies # Dependencies
$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h $(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
$(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h $(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
$(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h $(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
clean: clean:
rm -rf $(OBJDIR) $(BINDIR) rm -f $(OBJDIR)/progman.o $(OBJDIR)/notepad.o $(OBJDIR)/clock.o $(OBJDIR)/dvxdemo.o
rm -f $(BINDIR)/progman/progman.app
rm -f $(BINDIR)/notepad/notepad.app
rm -f $(BINDIR)/clock/clock.app
rm -f $(BINDIR)/dvxdemo/dvxdemo.app $(addprefix $(BINDIR)/dvxdemo/,$(DVXDEMO_BMPS))

View file

@ -1,15 +1,19 @@
// demo.c — DVX GUI demonstration application // dvxdemo.c — DVX GUI demonstration app (DXE version)
//
// Callback-only app that opens several windows showcasing the DVX
// widget system, paint callbacks, menus, accelerators, etc.
#include "dvxApp.h" #include "dvxApp.h"
#include "dvxDialog.h" #include "dvxDialog.h"
#include "dvxWidget.h" #include "dvxWidget.h"
#include "dvxWm.h"
#include "shellApp.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "thirdparty/stb_image.h"
// ============================================================ // ============================================================
// Menu command IDs // Menu command IDs
@ -49,7 +53,8 @@
// Prototypes // Prototypes
// ============================================================ // ============================================================
static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch); int32_t appMain(DxeAppContextT *ctx);
static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag);
static void onCloseCb(WindowT *win); static void onCloseCb(WindowT *win);
static void onCloseMainCb(WindowT *win); static void onCloseMainCb(WindowT *win);
static void onMenuCb(WindowT *win, int32_t menuId); static void onMenuCb(WindowT *win, int32_t menuId);
@ -58,102 +63,58 @@ static void onPaintColor(WindowT *win, RectT *dirtyArea);
static void onPaintPattern(WindowT *win, RectT *dirtyArea); static void onPaintPattern(WindowT *win, RectT *dirtyArea);
static void onPaintText(WindowT *win, RectT *dirtyArea); static void onPaintText(WindowT *win, RectT *dirtyArea);
static void onToolbarClick(WidgetT *w); static void onToolbarClick(WidgetT *w);
static void setupControlsWindow(AppContextT *ctx); static void setupControlsWindow(void);
static void setupMainWindow(AppContextT *ctx); static void setupMainWindow(void);
static void setupTerminalWindow(AppContextT *ctx); static void setupTerminalWindow(void);
static void setupWidgetDemo(AppContextT *ctx); static void setupWidgetDemo(void);
// ============================================================ // ============================================================
// Globals // Module state
// ============================================================ // ============================================================
static AppContextT *sCtx = NULL; static AppContextT *sAc = NULL;
static DxeAppContextT *sDxeCtx = NULL;
// ============================================================ // ============================================================
// loadBmpPixels — load a BMP/PNG file into display-format pixels // App descriptor
// ============================================================ // ============================================================
static uint8_t *loadBmpPixels(AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch) { AppDescriptorT appDescriptor = {
int imgW; .name = "DVX Demo",
int imgH; .hasMainLoop = false,
int channels; .stackSize = 0,
uint8_t *rgb = stbi_load(path, &imgW, &imgH, &channels, 3); .priority = TS_PRIORITY_NORMAL
};
if (!rgb) { // ============================================================
return NULL; // Callbacks
// ============================================================
static void onCanvasDraw(WidgetT *w, int32_t cx, int32_t cy, bool drag) {
if (drag && w->as.canvas.lastX >= 0) {
wgtCanvasDrawLine(w, w->as.canvas.lastX, w->as.canvas.lastY, cx, cy);
} else {
wgtCanvasDrawLine(w, cx, cy, cx, cy);
} }
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) { static void onCloseCb(WindowT *win) {
AppContextT *ctx = (AppContextT *)win->userData; dvxDestroyWindow(sAc, win);
if (ctx) {
dvxDestroyWindow(ctx, win);
}
} }
// ============================================================
// onCloseMainCb
// ============================================================
static void onCloseMainCb(WindowT *win) { static void onCloseMainCb(WindowT *win) {
AppContextT *ctx = (AppContextT *)win->userData; int32_t result = dvxMessageBox(sAc, "Close",
"Close this window?",
MB_YESNO | MB_ICONQUESTION);
if (ctx) { if (result == ID_YES) {
int32_t result = dvxMessageBox(ctx, "Exit", dvxDestroyWindow(sAc, win);
"Are you sure you want to exit?",
MB_YESNO | MB_ICONQUESTION);
if (result == ID_YES) {
dvxQuit(ctx);
}
} }
} }
// ============================================================
// onMenuCb
// ============================================================
static const FileFilterT sFileFilters[] = { static const FileFilterT sFileFilters[] = {
{"All Files (*.*)", "*.*"}, {"All Files (*.*)", "*.*"},
{"Text Files (*.txt)", "*.txt"}, {"Text Files (*.txt)", "*.txt"},
@ -163,16 +124,14 @@ static const FileFilterT sFileFilters[] = {
}; };
static void onMenuCb(WindowT *win, int32_t menuId) { static void onMenuCb(WindowT *win, int32_t menuId) {
AppContextT *ctx = (AppContextT *)win->userData;
switch (menuId) { switch (menuId) {
case CMD_FILE_OPEN: { case CMD_FILE_OPEN: {
char path[260]; char path[260];
if (dvxFileDialog(ctx, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) { if (dvxFileDialog(sAc, "Open File", FD_OPEN, NULL, sFileFilters, 5, path, sizeof(path))) {
char msg[300]; char msg[300];
snprintf(msg, sizeof(msg), "Selected: %s", path); snprintf(msg, sizeof(msg), "Selected: %s", path);
dvxMessageBox(ctx, "Open", msg, MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Open", msg, MB_OK | MB_ICONINFO);
} }
break; break;
@ -181,96 +140,84 @@ static void onMenuCb(WindowT *win, int32_t menuId) {
case CMD_FILE_SAVE: { case CMD_FILE_SAVE: {
char path[260]; char path[260];
if (dvxFileDialog(ctx, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) { if (dvxFileDialog(sAc, "Save As", FD_SAVE, NULL, sFileFilters, 5, path, sizeof(path))) {
char msg[300]; char msg[300];
snprintf(msg, sizeof(msg), "Save to: %s", path); snprintf(msg, sizeof(msg), "Save to: %s", path);
dvxMessageBox(ctx, "Save", msg, MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Save", msg, MB_OK | MB_ICONINFO);
} }
break; break;
} }
case CMD_FILE_EXIT: case CMD_FILE_EXIT:
if (ctx) { onCloseMainCb(win);
int32_t result = dvxMessageBox(ctx, "Exit",
"Are you sure you want to exit?",
MB_YESNO | MB_ICONQUESTION);
if (result == ID_YES) {
dvxQuit(ctx);
}
}
break; break;
case CMD_VIEW_TERM: case CMD_VIEW_TERM:
setupTerminalWindow(ctx); setupTerminalWindow();
break; break;
case CMD_VIEW_CTRL: case CMD_VIEW_CTRL:
setupControlsWindow(ctx); setupControlsWindow();
break; break;
case CMD_VIEW_DEBUG_LYT: { case CMD_VIEW_DEBUG_LYT: {
static bool sDebugLyt = false; static bool sDebugLyt = false;
sDebugLyt = !sDebugLyt; sDebugLyt = !sDebugLyt;
wgtSetDebugLayout(ctx, sDebugLyt); wgtSetDebugLayout(sAc, sDebugLyt);
break; break;
} }
case CMD_WIN_CASCADE: case CMD_WIN_CASCADE:
dvxCascadeWindows(ctx); dvxCascadeWindows(sAc);
break; break;
case CMD_WIN_TILE_H: case CMD_WIN_TILE_H:
dvxTileWindowsH(ctx); dvxTileWindowsH(sAc);
break; break;
case CMD_WIN_TILE_V: case CMD_WIN_TILE_V:
dvxTileWindowsV(ctx); dvxTileWindowsV(sAc);
break; break;
case CMD_WIN_TILE: case CMD_WIN_TILE:
dvxTileWindows(ctx); dvxTileWindows(sAc);
break; break;
case CMD_HELP_ABOUT: case CMD_HELP_ABOUT:
dvxMessageBox(sCtx, "About DVX Demo", dvxMessageBox(sAc, "About DVX Demo",
"DVX GUI Demonstration\n\n" "DVX GUI Demonstration\n\n"
"A DOS Visual eXecutive windowing system for DOS.", "A DOS Visual eXecutive windowing system for DOS.",
MB_OK | MB_ICONINFO); MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_CUT: case CMD_CTX_CUT:
dvxMessageBox(ctx, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Context Menu", "Cut selected.", MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_COPY: case CMD_CTX_COPY:
dvxMessageBox(ctx, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Context Menu", "Copied to clipboard.", MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_PASTE: case CMD_CTX_PASTE:
dvxMessageBox(ctx, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Context Menu", "Pasted from clipboard.", MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_DELETE: case CMD_CTX_DELETE:
dvxMessageBox(ctx, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Context Menu", "Deleted.", MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_SELALL: case CMD_CTX_SELALL:
dvxMessageBox(ctx, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Context Menu", "Selected all.", MB_OK | MB_ICONINFO);
break; break;
case CMD_CTX_PROPS: case CMD_CTX_PROPS:
dvxMessageBox(ctx, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO); dvxMessageBox(sAc, "Properties", "Window properties dialog would appear here.", MB_OK | MB_ICONINFO);
break; break;
} }
} }
// ============================================================
// onOkClick
// ============================================================
static void onOkClick(WidgetT *w) { static void onOkClick(WidgetT *w) {
WidgetT *root = w; WidgetT *root = w;
@ -286,20 +233,15 @@ static void onOkClick(WidgetT *w) {
} }
// ============================================================
// onPaintColor
// ============================================================
static void onPaintColor(WindowT *win, RectT *dirtyArea) { static void onPaintColor(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea; (void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) { if (!win->contentBuf || !sAc) {
return; return;
} }
const DisplayT *d = dvxGetDisplay(ctx); const DisplayT *d = dvxGetDisplay(sAc);
const BlitOpsT *ops = dvxGetBlitOps(ctx); const BlitOpsT *ops = dvxGetBlitOps(sAc);
for (int32_t y = 0; y < win->contentH; y++) { 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 r = (uint8_t)((y * 255) / (win->contentH > 1 ? win->contentH - 1 : 1));
@ -314,19 +256,14 @@ static void onPaintColor(WindowT *win, RectT *dirtyArea) {
} }
// ============================================================
// onPaintPattern
// ============================================================
static void onPaintPattern(WindowT *win, RectT *dirtyArea) { static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea; (void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) { if (!win->contentBuf || !sAc) {
return; return;
} }
const DisplayT *d = dvxGetDisplay(ctx); const DisplayT *d = dvxGetDisplay(sAc);
int32_t bpp = d->format.bytesPerPixel; int32_t bpp = d->format.bytesPerPixel;
int32_t sq = 16; int32_t sq = 16;
@ -350,21 +287,16 @@ static void onPaintPattern(WindowT *win, RectT *dirtyArea) {
} }
// ============================================================
// onPaintText
// ============================================================
static void onPaintText(WindowT *win, RectT *dirtyArea) { static void onPaintText(WindowT *win, RectT *dirtyArea) {
(void)dirtyArea; (void)dirtyArea;
AppContextT *ctx = (AppContextT *)win->userData;
if (!win->contentBuf || !ctx) { if (!win->contentBuf || !sAc) {
return; return;
} }
const DisplayT *d = dvxGetDisplay(ctx); const DisplayT *d = dvxGetDisplay(sAc);
const BlitOpsT *ops = dvxGetBlitOps(ctx); const BlitOpsT *ops = dvxGetBlitOps(sAc);
const BitmapFontT *font = dvxGetFont(ctx); const BitmapFontT *font = dvxGetFont(sAc);
int32_t bpp = d->format.bytesPerPixel; int32_t bpp = d->format.bytesPerPixel;
uint32_t bg = packColor(d, 255, 255, 255); uint32_t bg = packColor(d, 255, 255, 255);
@ -447,10 +379,6 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
} }
// ============================================================
// onToolbarClick
// ============================================================
static void onToolbarClick(WidgetT *w) { static void onToolbarClick(WidgetT *w) {
WidgetT *root = w; WidgetT *root = w;
@ -473,17 +401,16 @@ static void onToolbarClick(WidgetT *w) {
static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"}; static const char *colorItems[] = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"}; static const char *sizeItems[] = {"Small", "Medium", "Large", "Extra Large"};
static void setupControlsWindow(AppContextT *ctx) { static void setupControlsWindow(void) {
WindowT *win = dvxCreateWindow(ctx, "Advanced Widgets", 380, 50, 360, 440, true); WindowT *win = dvxCreateWindow(sAc, "Advanced Widgets", 380, 50, 360, 440, true);
if (!win) { if (!win) {
return; return;
} }
win->userData = ctx; win->onClose = onCloseCb;
win->onClose = onCloseCb;
WidgetT *root = wgtInitWindow(ctx, win); WidgetT *root = wgtInitWindow(sAc, win);
// TabControl at top // TabControl at top
WidgetT *tabs = wgtTabControl(root); WidgetT *tabs = wgtTabControl(root);
@ -617,58 +544,35 @@ static void setupControlsWindow(AppContextT *ctx) {
wgtLabel(sp, "Bottom of scroll area"); wgtLabel(sp, "Bottom of scroll area");
wgtButton(sp, "Scrolled &Button"); wgtButton(sp, "Scrolled &Button");
// --- Tab 5: Toolbar (ImageButtons + VSeparator) --- // --- Tab 5: Toolbar (text buttons + VSeparator) ---
WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar"); WidgetT *page5tb = wgtTabPage(tabs, "Tool&bar");
WidgetT *tb = wgtToolbar(page5tb); WidgetT *tb = wgtToolbar(page5tb);
int32_t imgW; WidgetT *btnNew = wgtButton(tb, "&New");
int32_t imgH; btnNew->onClick = onToolbarClick;
int32_t imgPitch; WidgetT *btnOpen = wgtButton(tb, "&Open");
btnOpen->onClick = onToolbarClick;
uint8_t *newData = loadBmpPixels(ctx, "new.bmp", &imgW, &imgH, &imgPitch); WidgetT *btnSave = wgtButton(tb, "&Save");
if (newData) { btnSave->onClick = onToolbarClick;
WidgetT *btnNew = wgtImageButton(tb, newData, imgW, imgH, imgPitch);
wgtSetName(btnNew, "New");
btnNew->onClick = onToolbarClick;
}
uint8_t *openData = loadBmpPixels(ctx, "open.bmp", &imgW, &imgH, &imgPitch);
if (openData) {
WidgetT *btnOpen = wgtImageButton(tb, openData, imgW, imgH, imgPitch);
wgtSetName(btnOpen, "Open");
btnOpen->onClick = onToolbarClick;
}
uint8_t *saveData = loadBmpPixels(ctx, "save.bmp", &imgW, &imgH, &imgPitch);
if (saveData) {
WidgetT *btnSave = wgtImageButton(tb, saveData, imgW, imgH, imgPitch);
wgtSetName(btnSave, "Save");
btnSave->onClick = onToolbarClick;
}
wgtVSeparator(tb); wgtVSeparator(tb);
WidgetT *btnHelp = wgtButton(tb, "&Help"); WidgetT *btnHelp = wgtButton(tb, "&Help");
btnHelp->onClick = onToolbarClick; btnHelp->onClick = onToolbarClick;
wgtLabel(page5tb, "ImageButtons with VSeparator."); wgtLabel(page5tb, "Toolbar with text buttons and VSeparator.");
// --- Tab 6: Media (Image, ImageFromFile) --- // --- Tab 6: Media (Image from file) ---
WidgetT *page6m = wgtTabPage(tabs, "&Media"); WidgetT *page6m = wgtTabPage(tabs, "&Media");
char samplePath[272];
snprintf(samplePath, sizeof(samplePath), "%s/sample.bmp", sDxeCtx->appDir);
wgtLabel(page6m, "ImageFromFile (sample.bmp):"); wgtLabel(page6m, "ImageFromFile (sample.bmp):");
wgtImageFromFile(page6m, "sample.bmp"); WidgetT *img = wgtImageFromFile(page6m, samplePath);
wgtHSeparator(page6m); if (!img) {
wgtLabel(page6m, "(File not found)");
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 DVX logo");
// --- Tab 7: Editor (TextArea, Canvas) --- // --- Tab 7: Editor (TextArea, Canvas) ---
WidgetT *page7e = wgtTabPage(tabs, "&Editor"); WidgetT *page7e = wgtTabPage(tabs, "&Editor");
@ -681,7 +585,7 @@ static void setupControlsWindow(AppContextT *ctx) {
wgtHSeparator(page7e); wgtHSeparator(page7e);
wgtLabel(page7e, "Canvas (draw with mouse):"); wgtLabel(page7e, "Canvas (draw with mouse):");
const DisplayT *d = dvxGetDisplay(ctx); const DisplayT *d = dvxGetDisplay(sAc);
WidgetT *cv = wgtCanvas(page7e, 280, 80); WidgetT *cv = wgtCanvas(page7e, 280, 80);
wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0)); wgtCanvasSetPenColor(cv, packColor(d, 200, 0, 0));
wgtCanvasDrawRect(cv, 5, 5, 50, 35); wgtCanvasDrawRect(cv, 5, 5, 50, 35);
@ -690,6 +594,7 @@ static void setupControlsWindow(AppContextT *ctx) {
wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0)); wgtCanvasSetPenColor(cv, packColor(d, 0, 150, 0));
wgtCanvasDrawLine(cv, 70, 5, 130, 70); wgtCanvasDrawLine(cv, 70, 5, 130, 70);
wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0)); wgtCanvasSetPenColor(cv, packColor(d, 0, 0, 0));
wgtCanvasSetMouseCallback(cv, onCanvasDraw);
// --- Tab 8: Splitter --- // --- Tab 8: Splitter ---
WidgetT *page8s = wgtTabPage(tabs, "S&plit"); WidgetT *page8s = wgtTabPage(tabs, "S&plit");
@ -831,12 +736,11 @@ static void setupControlsWindow(AppContextT *ctx) {
// setupMainWindow — info window + paint demos // setupMainWindow — info window + paint demos
// ============================================================ // ============================================================
static void setupMainWindow(AppContextT *ctx) { static void setupMainWindow(void) {
// Window 1: Text information window with menu bar // Window 1: Text information window with menu bar
WindowT *win1 = dvxCreateWindow(ctx, "DVX Information", 50, 40, 340, 350, true); WindowT *win1 = dvxCreateWindow(sAc, "DVX Information", 50, 40, 340, 350, true);
if (win1) { if (win1) {
win1->userData = ctx;
win1->onPaint = onPaintText; win1->onPaint = onPaintText;
win1->onClose = onCloseMainCb; win1->onClose = onCloseMainCb;
win1->onMenu = onMenuCb; win1->onMenu = onMenuCb;
@ -924,36 +828,34 @@ static void setupMainWindow(AppContextT *ctx) {
win1->contextMenu = winCtx; win1->contextMenu = winCtx;
wmUpdateContentRect(win1); wmUpdateContentRect(win1);
wmReallocContentBuf(win1, &ctx->display); wmReallocContentBuf(win1, &sAc->display);
RectT fullRect = {0, 0, win1->contentW, win1->contentH}; RectT fullRect = {0, 0, win1->contentW, win1->contentH};
win1->onPaint(win1, &fullRect); win1->onPaint(win1, &fullRect);
} }
// Window 2: Color gradient // Window 2: Color gradient
WindowT *win2 = dvxCreateWindow(ctx, "Color Gradient", 200, 100, 280, 250, true); WindowT *win2 = dvxCreateWindow(sAc, "Color Gradient", 200, 100, 280, 250, true);
if (win2) { if (win2) {
win2->userData = ctx; win2->onPaint = onPaintColor;
win2->onPaint = onPaintColor; win2->onClose = onCloseCb;
win2->onClose = onCloseCb;
RectT fullRect = {0, 0, win2->contentW, win2->contentH}; RectT fullRect = {0, 0, win2->contentW, win2->contentH};
win2->onPaint(win2, &fullRect); win2->onPaint(win2, &fullRect);
} }
// Window 3: Checkerboard pattern with scrollbars // Window 3: Checkerboard pattern with scrollbars
WindowT *win3 = dvxCreateWindow(ctx, "Pattern", 400, 150, 250, 220, true); WindowT *win3 = dvxCreateWindow(sAc, "Pattern", 400, 150, 250, 220, true);
if (win3) { if (win3) {
win3->userData = ctx; win3->onPaint = onPaintPattern;
win3->onPaint = onPaintPattern; win3->onClose = onCloseCb;
win3->onClose = onCloseCb;
wmAddVScrollbar(win3, 0, 100, 25); wmAddVScrollbar(win3, 0, 100, 25);
wmAddHScrollbar(win3, 0, 100, 25); wmAddHScrollbar(win3, 0, 100, 25);
wmUpdateContentRect(win3); wmUpdateContentRect(win3);
wmReallocContentBuf(win3, &ctx->display); wmReallocContentBuf(win3, &sAc->display);
RectT fullRect = {0, 0, win3->contentW, win3->contentH}; RectT fullRect = {0, 0, win3->contentW, win3->contentH};
win3->onPaint(win3, &fullRect); win3->onPaint(win3, &fullRect);
@ -965,17 +867,16 @@ static void setupMainWindow(AppContextT *ctx) {
// setupTerminalWindow — ANSI terminal widget demo // setupTerminalWindow — ANSI terminal widget demo
// ============================================================ // ============================================================
static void setupTerminalWindow(AppContextT *ctx) { static void setupTerminalWindow(void) {
WindowT *win = dvxCreateWindow(ctx, "ANSI Terminal", 60, 60, 660, 420, true); WindowT *win = dvxCreateWindow(sAc, "ANSI Terminal", 60, 60, 660, 420, true);
if (!win) { if (!win) {
return; return;
} }
win->userData = ctx; win->onClose = onCloseCb;
win->onClose = onCloseCb;
WidgetT *root = wgtInitWindow(ctx, win); WidgetT *root = wgtInitWindow(sAc, win);
WidgetT *term = wgtAnsiTerm(root, 80, 25); WidgetT *term = wgtAnsiTerm(root, 80, 25);
term->weight = 100; term->weight = 100;
@ -1036,8 +937,8 @@ static void setupTerminalWindow(AppContextT *ctx) {
WidgetT *sb = wgtStatusBar(root); WidgetT *sb = wgtStatusBar(root);
wgtLabel(sb, "80x25 [Local]"); wgtLabel(sb, "80x25 [Local]");
dvxFitWindow(ctx, win); dvxFitWindow(sAc, win);
dvxMinimizeWindow(ctx, win); dvxMinimizeWindow(sAc, win);
} }
@ -1045,18 +946,17 @@ static void setupTerminalWindow(AppContextT *ctx) {
// setupWidgetDemo — form with accelerators // setupWidgetDemo — form with accelerators
// ============================================================ // ============================================================
static void setupWidgetDemo(AppContextT *ctx) { static void setupWidgetDemo(void) {
WindowT *win = dvxCreateWindow(ctx, "Widget Demo", 80, 200, 280, 360, true); WindowT *win = dvxCreateWindow(sAc, "Widget Demo", 80, 200, 280, 360, true);
if (!win) { if (!win) {
return; return;
} }
win->userData = ctx; win->onClose = onCloseCb;
win->onClose = onCloseCb; win->onMenu = onMenuCb;
win->onMenu = onMenuCb;
WidgetT *root = wgtInitWindow(ctx, win); WidgetT *root = wgtInitWindow(sAc, win);
// Status label at top // Status label at top
WidgetT *status = wgtLabel(root, "Ready."); WidgetT *status = wgtLabel(root, "Ready.");
@ -1134,48 +1034,17 @@ static void setupWidgetDemo(AppContextT *ctx) {
// ============================================================ // ============================================================
// main // Entry point
// ============================================================ // ============================================================
int main(int argc, char **argv) { int32_t appMain(DxeAppContextT *ctx) {
(void)argc; sDxeCtx = ctx;
sAc = ctx->shellCtx;
// Change to executable's directory so relative BMP paths work setupMainWindow();
char exeDir[260]; setupWidgetDemo();
strncpy(exeDir, argv[0], sizeof(exeDir) - 1); setupControlsWindow();
exeDir[sizeof(exeDir) - 1] = '\0'; setupTerminalWindow();
char *lastSep = strrchr(exeDir, '/');
char *lastBs = strrchr(exeDir, '\\');
if (lastBs > lastSep) {
lastSep = lastBs;
}
if (lastSep) {
*lastSep = '\0';
chdir(exeDir);
}
AppContextT ctx;
printf("DVX GUI Demo\n");
printf("Initializing VESA video...\n");
if (dvxInit(&ctx, 1024, 768, 16) != 0) {
fprintf(stderr, "Failed to initialize DVX GUI\n");
return 1;
}
sCtx = &ctx;
setupMainWindow(&ctx);
setupWidgetDemo(&ctx);
setupControlsWindow(&ctx);
setupTerminalWindow(&ctx);
dvxRun(&ctx);
dvxShutdown(&ctx);
printf("DVX GUI Demo ended.\n");
return 0; return 0;
} }

View file

@ -35,6 +35,7 @@
#define CMD_TILE 201 #define CMD_TILE 201
#define CMD_TILE_H 202 #define CMD_TILE_H 202
#define CMD_TILE_V 203 #define CMD_TILE_V 203
#define CMD_MIN_ON_RUN 104
#define CMD_ABOUT 300 #define CMD_ABOUT 300
#define CMD_TASK_MGR 301 #define CMD_TASK_MGR 301
@ -52,6 +53,7 @@ static AppContextT *sAc = NULL;
static int32_t sMyAppId = 0; static int32_t sMyAppId = 0;
static WindowT *sPmWindow = NULL; static WindowT *sPmWindow = NULL;
static WidgetT *sStatusLabel = NULL; static WidgetT *sStatusLabel = NULL;
static bool sMinOnRun = false;
static AppEntryT sAppFiles[MAX_APP_FILES]; static AppEntryT sAppFiles[MAX_APP_FILES];
static int32_t sAppCount = 0; static int32_t sAppCount = 0;
@ -112,11 +114,14 @@ static void buildPmWindow(void) {
// Menu bar // Menu bar
MenuBarT *menuBar = wmAddMenuBar(sPmWindow); MenuBarT *menuBar = wmAddMenuBar(sPmWindow);
MenuT *fileMenu = wmAddMenu(menuBar, "&File"); MenuT *fileMenu = wmAddMenu(menuBar, "&File");
wmAddMenuItem(fileMenu, "&Run...", CMD_RUN); wmAddMenuItem(fileMenu, "&Run...", CMD_RUN);
wmAddMenuSeparator(fileMenu); wmAddMenuSeparator(fileMenu);
wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT); wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT);
MenuT *optMenu = wmAddMenu(menuBar, "&Options");
wmAddMenuCheckItem(optMenu, "&Minimize on Run", CMD_MIN_ON_RUN, false);
MenuT *windowMenu = wmAddMenu(menuBar, "&Window"); MenuT *windowMenu = wmAddMenu(menuBar, "&Window");
wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE); wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE);
wmAddMenuItem(windowMenu, "&Tile", CMD_TILE); wmAddMenuItem(windowMenu, "&Tile", CMD_TILE);
@ -242,6 +247,10 @@ static void onAppButtonClick(WidgetT *w) {
shellLoadApp(sAc, entry->path); shellLoadApp(sAc, entry->path);
updateStatusText(); updateStatusText();
if (sMinOnRun && sPmWindow) {
dvxMinimizeWindow(sAc, sPmWindow);
}
} }
@ -271,6 +280,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
if (dvxFileDialog(sAc, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) { if (dvxFileDialog(sAc, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) {
shellLoadApp(sAc, path); shellLoadApp(sAc, path);
updateStatusText(); updateStatusText();
if (sMinOnRun && sPmWindow) {
dvxMinimizeWindow(sAc, sPmWindow);
}
} }
} }
break; break;
@ -295,6 +308,10 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
dvxTileWindowsV(sAc); dvxTileWindowsV(sAc);
break; break;
case CMD_MIN_ON_RUN:
sMinOnRun = !sMinOnRun;
break;
case CMD_ABOUT: case CMD_ABOUT:
showAboutDialog(); showAboutDialog();
break; break;
@ -485,7 +502,7 @@ static void scanAppsDir(void) {
static void showAboutDialog(void) { static void showAboutDialog(void) {
dvxMessageBox(sAc, "About DVX Shell", dvxMessageBox(sAc, "About DVX Shell",
"DVX Shell 1.0\n\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.", "DVX Shell 1.0\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.",
MB_OK | MB_ICONINFO); MB_OK | MB_ICONINFO);
} }

View file

@ -130,4 +130,4 @@ $(WOBJDIR)/widgetScrollbar.o: widgets/widgetScrollbar.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS) $(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS)
clean: clean:
rm -rf $(OBJDIR) $(LIBDIR) rm -f $(OBJS) $(POBJS) $(WOBJS) $(TARGET)

View file

@ -19,8 +19,12 @@
#define KB_MOVE_STEP 8 #define KB_MOVE_STEP 8
#define MENU_CHECK_WIDTH 14 #define MENU_CHECK_WIDTH 14
#define SUBMENU_ARROW_WIDTH 12 #define SUBMENU_ARROW_WIDTH 12
#define SUBMENU_ARROW_HALF 3 // half-size of submenu arrow glyph
#define TOOLTIP_DELAY_MS 500 #define TOOLTIP_DELAY_MS 500
#define TOOLTIP_PAD 3 #define TOOLTIP_PAD 3
#define POPUP_BEVEL_WIDTH 2 // popup menu border bevel thickness
#define POPUP_ITEM_PAD_H 8 // extra horizontal padding in popup items
#define MENU_TAB_GAP_CHARS 3 // char-widths gap between label and shortcut
// ============================================================ // ============================================================
// Prototypes // Prototypes
@ -133,7 +137,7 @@ static void calcPopupSize(const AppContextT *ctx, const MenuT *menu, int32_t *pw
memcpy(leftBuf, label, leftLen); memcpy(leftBuf, label, leftLen);
leftBuf[leftLen] = '\0'; leftBuf[leftLen] = '\0';
itemW = textWidthAccel(&ctx->font, leftBuf) + ctx->font.charWidth * 3 + textWidth(&ctx->font, tab + 1); itemW = textWidthAccel(&ctx->font, leftBuf) + ctx->font.charWidth * MENU_TAB_GAP_CHARS + textWidth(&ctx->font, tab + 1);
} else { } else {
itemW = textWidthAccel(&ctx->font, label); itemW = textWidthAccel(&ctx->font, label);
} }
@ -151,8 +155,8 @@ static void calcPopupSize(const AppContextT *ctx, const MenuT *menu, int32_t *pw
} }
} }
*pw = maxW + CHROME_TITLE_PAD * 2 + 8 + (hasSub ? SUBMENU_ARROW_WIDTH : 0) + (hasCheck ? MENU_CHECK_WIDTH : 0); *pw = maxW + CHROME_TITLE_PAD * 2 + POPUP_ITEM_PAD_H + (hasSub ? SUBMENU_ARROW_WIDTH : 0) + (hasCheck ? MENU_CHECK_WIDTH : 0);
*ph = menu->itemCount * ctx->font.charHeight + 4; *ph = menu->itemCount * ctx->font.charHeight + POPUP_BEVEL_WIDTH * 2;
} }
@ -380,16 +384,16 @@ static void compositeAndFlush(AppContextT *ctx) {
smBevel.highlight = ctx->colors.windowHighlight; smBevel.highlight = ctx->colors.windowHighlight;
smBevel.shadow = ctx->colors.windowShadow; smBevel.shadow = ctx->colors.windowShadow;
smBevel.face = ctx->colors.menuBg; smBevel.face = ctx->colors.menuBg;
smBevel.width = 2; smBevel.width = POPUP_BEVEL_WIDTH;
drawBevel(d, ops, ctx->sysMenu.popupX, ctx->sysMenu.popupY, ctx->sysMenu.popupW, ctx->sysMenu.popupH, &smBevel); drawBevel(d, ops, ctx->sysMenu.popupX, ctx->sysMenu.popupY, ctx->sysMenu.popupW, ctx->sysMenu.popupH, &smBevel);
int32_t itemY = ctx->sysMenu.popupY + 2; int32_t itemY = ctx->sysMenu.popupY + POPUP_BEVEL_WIDTH;
for (int32_t k = 0; k < ctx->sysMenu.itemCount; k++) { for (int32_t k = 0; k < ctx->sysMenu.itemCount; k++) {
SysMenuItemT *item = &ctx->sysMenu.items[k]; SysMenuItemT *item = &ctx->sysMenu.items[k];
if (item->separator) { if (item->separator) {
drawHLine(d, ops, ctx->sysMenu.popupX + 2, itemY + ctx->font.charHeight / 2, ctx->sysMenu.popupW - 4, ctx->colors.windowShadow); drawHLine(d, ops, ctx->sysMenu.popupX + POPUP_BEVEL_WIDTH, itemY + ctx->font.charHeight / 2, ctx->sysMenu.popupW - POPUP_BEVEL_WIDTH * 2, ctx->colors.windowShadow);
itemY += ctx->font.charHeight; itemY += ctx->font.charHeight;
continue; continue;
} }
@ -402,8 +406,8 @@ static void compositeAndFlush(AppContextT *ctx) {
fg = ctx->colors.menuHighlightFg; fg = ctx->colors.menuHighlightFg;
} }
rectFill(d, ops, ctx->sysMenu.popupX + 2, itemY, ctx->sysMenu.popupW - 4, ctx->font.charHeight, bg); rectFill(d, ops, ctx->sysMenu.popupX + POPUP_BEVEL_WIDTH, itemY, ctx->sysMenu.popupW - POPUP_BEVEL_WIDTH * 2, ctx->font.charHeight, bg);
drawTextAccel(d, ops, &ctx->font, ctx->sysMenu.popupX + CHROME_TITLE_PAD + 2, itemY, item->label, fg, bg, true); drawTextAccel(d, ops, &ctx->font, ctx->sysMenu.popupX + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH, itemY, item->label, fg, bg, true);
itemY += ctx->font.charHeight; itemY += ctx->font.charHeight;
} }
@ -661,7 +665,7 @@ static void dispatchEvents(AppContextT *ctx) {
my >= ctx->sysMenu.popupY && my < ctx->sysMenu.popupY + ctx->sysMenu.popupH) { my >= ctx->sysMenu.popupY && my < ctx->sysMenu.popupY + ctx->sysMenu.popupH) {
// Hover tracking // Hover tracking
int32_t relY = my - ctx->sysMenu.popupY - 2; int32_t relY = my - ctx->sysMenu.popupY - POPUP_BEVEL_WIDTH;
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0; int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
if (itemIdx >= 0 && itemIdx < ctx->sysMenu.itemCount && ctx->sysMenu.items[itemIdx].separator) { if (itemIdx >= 0 && itemIdx < ctx->sysMenu.itemCount && ctx->sysMenu.items[itemIdx].separator) {
@ -701,7 +705,7 @@ static void dispatchEvents(AppContextT *ctx) {
if (inCurrent) { if (inCurrent) {
// Hover tracking in current level // Hover tracking in current level
int32_t relY = my - ctx->popup.popupY - 2; int32_t relY = my - ctx->popup.popupY - POPUP_BEVEL_WIDTH;
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0; int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
if (itemIdx < 0) { if (itemIdx < 0) {
@ -781,7 +785,7 @@ static void dispatchEvents(AppContextT *ctx) {
closePopupLevel(ctx); closePopupLevel(ctx);
// Now current level IS this parent — update hover // Now current level IS this parent — update hover
int32_t relY = my - ctx->popup.popupY - 2; int32_t relY = my - ctx->popup.popupY - POPUP_BEVEL_WIDTH;
int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0; int32_t itemIdx = (relY >= 0) ? (int32_t)((uint32_t)relY * ctx->charHeightRecip >> 16) : 0;
if (itemIdx < 0) { if (itemIdx < 0) {
@ -970,17 +974,17 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
popBevel.highlight = ctx->colors.windowHighlight; popBevel.highlight = ctx->colors.windowHighlight;
popBevel.shadow = ctx->colors.windowShadow; popBevel.shadow = ctx->colors.windowShadow;
popBevel.face = ctx->colors.menuBg; popBevel.face = ctx->colors.menuBg;
popBevel.width = 2; popBevel.width = POPUP_BEVEL_WIDTH;
drawBevel(d, ops, px, py, pw, ph, &popBevel); drawBevel(d, ops, px, py, pw, ph, &popBevel);
// Draw menu items // Draw menu items
int32_t itemY = py + 2; int32_t itemY = py + POPUP_BEVEL_WIDTH;
for (int32_t k = 0; k < menu->itemCount; k++) { for (int32_t k = 0; k < menu->itemCount; k++) {
const MenuItemT *item = &menu->items[k]; const MenuItemT *item = &menu->items[k];
if (item->separator) { if (item->separator) {
drawHLine(d, ops, px + 2, itemY + ctx->font.charHeight / 2, pw - 4, ctx->colors.windowShadow); drawHLine(d, ops, px + POPUP_BEVEL_WIDTH, itemY + ctx->font.charHeight / 2, pw - POPUP_BEVEL_WIDTH * 2, ctx->colors.windowShadow);
itemY += ctx->font.charHeight; itemY += ctx->font.charHeight;
continue; continue;
} }
@ -993,7 +997,7 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
fg = ctx->colors.menuHighlightFg; fg = ctx->colors.menuHighlightFg;
} }
rectFill(d, ops, px + 2, itemY, pw - 4, ctx->font.charHeight, bg); rectFill(d, ops, px + POPUP_BEVEL_WIDTH, itemY, pw - POPUP_BEVEL_WIDTH * 2, ctx->font.charHeight, bg);
// Split label at tab: left part is the menu text, right part is the shortcut // Split label at tab: left part is the menu text, right part is the shortcut
const char *tab = strchr(item->label, '\t'); const char *tab = strchr(item->label, '\t');
@ -1009,11 +1013,11 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
memcpy(leftBuf, item->label, leftLen); memcpy(leftBuf, item->label, leftLen);
leftBuf[leftLen] = '\0'; leftBuf[leftLen] = '\0';
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + 2 + checkMargin, itemY, leftBuf, fg, bg, true); drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH + checkMargin, itemY, leftBuf, fg, bg, true);
const char *right = tab + 1; const char *right = tab + 1;
int32_t rightW = textWidth(&ctx->font, right); int32_t rightW = textWidth(&ctx->font, right);
int32_t rightX = px + pw - rightW - CHROME_TITLE_PAD - 4; int32_t rightX = px + pw - rightW - CHROME_TITLE_PAD - POPUP_BEVEL_WIDTH * 2;
if (item->subMenu) { if (item->subMenu) {
rightX -= SUBMENU_ARROW_WIDTH; rightX -= SUBMENU_ARROW_WIDTH;
@ -1021,13 +1025,13 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
drawText(d, ops, &ctx->font, rightX, itemY, right, fg, bg, true); drawText(d, ops, &ctx->font, rightX, itemY, right, fg, bg, true);
} else { } else {
drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + 2 + checkMargin, itemY, item->label, fg, bg, true); drawTextAccel(d, ops, &ctx->font, px + CHROME_TITLE_PAD + POPUP_BEVEL_WIDTH + checkMargin, itemY, item->label, fg, bg, true);
} }
// Draw check/radio indicator // Draw check/radio indicator
if (item->checked) { if (item->checked) {
int32_t cy = itemY + ctx->font.charHeight / 2; int32_t cy = itemY + ctx->font.charHeight / 2;
int32_t cx = px + 2 + MENU_CHECK_WIDTH / 2; int32_t cx = px + POPUP_BEVEL_WIDTH + MENU_CHECK_WIDTH / 2;
if (item->type == MenuItemCheckE) { if (item->type == MenuItemCheckE) {
// Checkmark: small tick shape // Checkmark: small tick shape
@ -1049,14 +1053,14 @@ static void drawPopupLevel(AppContextT *ctx, DisplayT *d, const BlitOpsT *ops, c
// Draw submenu arrow indicator // Draw submenu arrow indicator
if (item->subMenu) { if (item->subMenu) {
int32_t arrowX = px + pw - SUBMENU_ARROW_WIDTH - 2; int32_t arrowX = px + pw - SUBMENU_ARROW_WIDTH - POPUP_BEVEL_WIDTH;
int32_t arrowY = itemY + ctx->font.charHeight / 2; int32_t arrowY = itemY + ctx->font.charHeight / 2;
for (int32_t row = -3; row <= 3; row++) { for (int32_t row = -SUBMENU_ARROW_HALF; row <= SUBMENU_ARROW_HALF; row++) {
int32_t len = 4 - (row < 0 ? -row : row); int32_t len = SUBMENU_ARROW_HALF + 1 - (row < 0 ? -row : row);
if (len > 0) { if (len > 0) {
drawHLine(d, ops, arrowX + (row < 0 ? 3 + row : 3 - row), arrowY + row, len, fg); drawHLine(d, ops, arrowX + (row < 0 ? SUBMENU_ARROW_HALF + row : SUBMENU_ARROW_HALF - row), arrowY + row, len, fg);
} }
} }
} }
@ -2078,8 +2082,8 @@ static void openSubMenu(AppContextT *ctx) {
// Open submenu at right edge of current popup, aligned with hovered item // Open submenu at right edge of current popup, aligned with hovered item
ctx->popup.menu = item->subMenu; ctx->popup.menu = item->subMenu;
ctx->popup.popupX = pl->popupX + pl->popupW - 2; ctx->popup.popupX = pl->popupX + pl->popupW - POPUP_BEVEL_WIDTH;
ctx->popup.popupY = pl->popupY + 2 + idx * ctx->font.charHeight; ctx->popup.popupY = pl->popupY + POPUP_BEVEL_WIDTH + idx * ctx->font.charHeight;
ctx->popup.hoverItem = -1; ctx->popup.hoverItem = -1;
calcPopupSize(ctx, ctx->popup.menu, &ctx->popup.popupW, &ctx->popup.popupH); calcPopupSize(ctx, ctx->popup.menu, &ctx->popup.popupW, &ctx->popup.popupH);
@ -2178,8 +2182,8 @@ static void openSysMenu(AppContextT *ctx, WindowT *win) {
} }
} }
ctx->sysMenu.popupW = maxW + CHROME_TITLE_PAD * 2 + 8; ctx->sysMenu.popupW = maxW + CHROME_TITLE_PAD * 2 + POPUP_ITEM_PAD_H;
ctx->sysMenu.popupH = ctx->sysMenu.itemCount * ctx->font.charHeight + 4; ctx->sysMenu.popupH = ctx->sysMenu.itemCount * ctx->font.charHeight + POPUP_BEVEL_WIDTH * 2;
ctx->sysMenu.hoverItem = -1; ctx->sysMenu.hoverItem = -1;
ctx->sysMenu.active = true; ctx->sysMenu.active = true;

View file

@ -17,12 +17,18 @@
// Constants // Constants
// ============================================================ // ============================================================
#define MSG_MAX_WIDTH 320 #define MSG_MAX_WIDTH 320
#define MSG_PADDING 8 #define MSG_PADDING 8
#define ICON_AREA_WIDTH 40 #define ICON_AREA_WIDTH 40
#define BUTTON_WIDTH 80 #define BUTTON_WIDTH 80
#define BUTTON_HEIGHT 24 #define BUTTON_HEIGHT 24
#define BUTTON_GAP 8 #define BUTTON_GAP 8
#define ICON_GLYPH_SIZE 24 // icon glyph drawing area (pixels)
#define ICON_GLYPH_CENTER 12 // center of icon glyph (ICON_GLYPH_SIZE / 2)
#define ICON_OUTER_R2 144 // outer circle radius squared (12*12)
#define ICON_INNER_R2 100 // inner circle radius squared (10*10)
#define FD_DIALOG_WIDTH 360 // file dialog window width
#define FD_DIALOG_HEIGHT 340 // file dialog window height
// ============================================================ // ============================================================
// Prototypes // Prototypes
@ -75,13 +81,13 @@ static MsgBoxStateT sMsgBox;
static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t iconType, uint32_t color) { static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t iconType, uint32_t color) {
if (iconType == MB_ICONINFO) { if (iconType == MB_ICONINFO) {
// Circle outline with 'i' // Circle outline with 'i'
for (int32_t row = 0; row < 24; row++) { for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
for (int32_t col = 0; col < 24; col++) { for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
int32_t dx = col - 12; int32_t dx = col - ICON_GLYPH_CENTER;
int32_t dy = row - 12; int32_t dy = row - ICON_GLYPH_CENTER;
int32_t d2 = dx * dx + dy * dy; int32_t d2 = dx * dx + dy * dy;
if (d2 <= 144 && d2 >= 100) { if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
rectFill(d, ops, x + col, y + row, 1, 1, color); rectFill(d, ops, x + col, y + row, 1, 1, color);
} }
} }
@ -91,15 +97,15 @@ static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y
rectFill(d, ops, x + 11, y + 10, 2, 8, color); rectFill(d, ops, x + 11, y + 10, 2, 8, color);
} else if (iconType == MB_ICONWARNING) { } else if (iconType == MB_ICONWARNING) {
// Triangle outline with '!' // Triangle outline with '!'
for (int32_t row = 0; row < 24; row++) { for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
int32_t halfW = row / 2; int32_t halfW = row / 2;
int32_t lx = 12 - halfW; int32_t lx = ICON_GLYPH_CENTER - halfW;
int32_t rx = 12 + halfW; int32_t rx = ICON_GLYPH_CENTER + halfW;
rectFill(d, ops, x + lx, y + row, 1, 1, color); rectFill(d, ops, x + lx, y + row, 1, 1, color);
rectFill(d, ops, x + rx, y + row, 1, 1, color); rectFill(d, ops, x + rx, y + row, 1, 1, color);
if (row == 23) { if (row == ICON_GLYPH_SIZE - 1) {
drawHLine(d, ops, x + lx, y + row, rx - lx + 1, color); drawHLine(d, ops, x + lx, y + row, rx - lx + 1, color);
} }
} }
@ -108,31 +114,31 @@ static void drawIconGlyph(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y
rectFill(d, ops, x + 11, y + 19, 2, 2, color); rectFill(d, ops, x + 11, y + 19, 2, 2, color);
} else if (iconType == MB_ICONERROR) { } else if (iconType == MB_ICONERROR) {
// Circle outline with X // Circle outline with X
for (int32_t row = 0; row < 24; row++) { for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
for (int32_t col = 0; col < 24; col++) { for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
int32_t dx = col - 12; int32_t dx = col - ICON_GLYPH_CENTER;
int32_t dy = row - 12; int32_t dy = row - ICON_GLYPH_CENTER;
int32_t d2 = dx * dx + dy * dy; int32_t d2 = dx * dx + dy * dy;
if (d2 <= 144 && d2 >= 100) { if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
rectFill(d, ops, x + col, y + row, 1, 1, color); rectFill(d, ops, x + col, y + row, 1, 1, color);
} }
} }
} }
for (int32_t i = 0; i < 12; i++) { for (int32_t i = 0; i < ICON_GLYPH_CENTER; i++) {
rectFill(d, ops, x + 6 + i, y + 6 + i, 2, 2, color); rectFill(d, ops, x + 6 + i, y + 6 + i, 2, 2, color);
rectFill(d, ops, x + 18 - i, y + 6 + i, 2, 2, color); rectFill(d, ops, x + 18 - i, y + 6 + i, 2, 2, color);
} }
} else if (iconType == MB_ICONQUESTION) { } else if (iconType == MB_ICONQUESTION) {
// Circle outline with '?' // Circle outline with '?'
for (int32_t row = 0; row < 24; row++) { for (int32_t row = 0; row < ICON_GLYPH_SIZE; row++) {
for (int32_t col = 0; col < 24; col++) { for (int32_t col = 0; col < ICON_GLYPH_SIZE; col++) {
int32_t dx = col - 12; int32_t dx = col - ICON_GLYPH_CENTER;
int32_t dy = row - 12; int32_t dy = row - ICON_GLYPH_CENTER;
int32_t d2 = dx * dx + dy * dy; int32_t d2 = dx * dx + dy * dy;
if (d2 <= 144 && d2 >= 100) { if (d2 <= ICON_OUTER_R2 && d2 >= ICON_INNER_R2) {
rectFill(d, ops, x + col, y + row, 1, 1, color); rectFill(d, ops, x + col, y + row, 1, 1, color);
} }
} }
@ -214,7 +220,7 @@ int32_t dvxMessageBox(AppContextT *ctx, const char *title, const char *message,
// Calculate content area sizes // Calculate content area sizes
int32_t msgAreaH = textH + MSG_PADDING * 2; int32_t msgAreaH = textH + MSG_PADDING * 2;
int32_t iconAreaH = hasIcon ? (24 + MSG_PADDING * 2) : 0; int32_t iconAreaH = hasIcon ? (ICON_GLYPH_SIZE + MSG_PADDING * 2) : 0;
if (msgAreaH < iconAreaH) { if (msgAreaH < iconAreaH) {
msgAreaH = iconAreaH; msgAreaH = iconAreaH;
@ -982,8 +988,8 @@ bool dvxFileDialog(AppContextT *ctx, const char *title, int32_t flags, const cha
sFd.curDir[FD_MAX_PATH - 1] = '\0'; sFd.curDir[FD_MAX_PATH - 1] = '\0';
// Create dialog window // Create dialog window
int32_t dlgW = 360; int32_t dlgW = FD_DIALOG_WIDTH;
int32_t dlgH = 340; int32_t dlgH = FD_DIALOG_HEIGHT;
int32_t winX = (ctx->display.width - dlgW) / 2; int32_t winX = (ctx->display.width - dlgW) / 2;
int32_t winY = (ctx->display.height - dlgH) / 2; int32_t winY = (ctx->display.height - dlgH) / 2;

View file

@ -86,6 +86,8 @@ typedef struct {
// Bitmap font // Bitmap font
// ============================================================ // ============================================================
#define FONT_CHAR_WIDTH 8 // fixed glyph width in pixels
typedef struct { typedef struct {
int32_t charWidth; // fixed width per glyph (e.g. 8) int32_t charWidth; // fixed width per glyph (e.g. 8)
int32_t charHeight; // e.g. 14 or 16 int32_t charHeight; // e.g. 14 or 16

View file

@ -448,6 +448,7 @@ typedef struct WidgetT {
int32_t penSize; int32_t penSize;
int32_t lastX; int32_t lastX;
int32_t lastY; int32_t lastY;
void (*onMouse)(struct WidgetT *w, int32_t cx, int32_t cy, bool drag);
} canvas; } canvas;
AnsiTermDataT *ansiTerm; AnsiTermDataT *ansiTerm;
@ -680,6 +681,10 @@ void wgtCanvasSetPenColor(WidgetT *w, uint32_t color);
// Set the pen size in pixels (diameter). // Set the pen size in pixels (diameter).
void wgtCanvasSetPenSize(WidgetT *w, int32_t size); void wgtCanvasSetPenSize(WidgetT *w, int32_t size);
// Set the mouse callback. Called on click (drag=false) and drag (drag=true)
// with canvas-relative coordinates. If NULL (default), mouse events are ignored.
void wgtCanvasSetMouseCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag));
// Save the canvas to a PNG file. Returns 0 on success, -1 on failure. // Save the canvas to a PNG file. Returns 0 on success, -1 on failure.
int32_t wgtCanvasSave(WidgetT *w, const char *path); int32_t wgtCanvasSave(WidgetT *w, const char *path);

View file

@ -15,8 +15,16 @@
// Constants // Constants
// ============================================================ // ============================================================
#define GADGET_PAD 2 #define GADGET_PAD 2
#define GADGET_INSET 2 // inset from title bar edges #define GADGET_INSET 2 // inset from title bar edges
#define MENU_BAR_GAP 8 // horizontal gap between menu bar labels
#define RESIZE_BREAK_INSET 16 // distance from corner to resize groove
#define CLOSE_ICON_INSET 3 // close bar inset from gadget edges
#define MAXIMIZE_ICON_INSET 2 // maximize box inset from gadget edges
#define RESTORE_ICON_INSET 4 // restore box inset from gadget edges
#define MINIMIZE_ICON_SIZE 4 // filled square size for minimize icon
#define SB_ARROW_ROWS 4 // number of rows in scrollbar arrow glyph
#define SB_ARROW_HALF 2 // half-width of scrollbar arrow tip
// ============================================================ // ============================================================
// Title bar gadget geometry // Title bar gadget geometry
@ -78,7 +86,7 @@ static void computeMenuBarPositions(WindowT *win, const BitmapFontT *font) {
menu->barX = x; menu->barX = x;
menu->barW = labelW; menu->barW = labelW;
x += labelW; x += labelW + MENU_BAR_GAP;
} }
win->menuBar->positionsDirty = false; win->menuBar->positionsDirty = false;
@ -201,6 +209,14 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
rectFill(d, ops, win->x + CHROME_BORDER_WIDTH, barY, rectFill(d, ops, win->x + CHROME_BORDER_WIDTH, barY,
win->w - CHROME_BORDER_WIDTH * 2, barH, colors->menuBg); win->w - CHROME_BORDER_WIDTH * 2, barH, colors->menuBg);
// Clip menu labels to the menu bar area
int32_t savedClipX = d->clipX;
int32_t savedClipY = d->clipY;
int32_t savedClipW = d->clipW;
int32_t savedClipH = d->clipH;
setClipRect(d, win->x + CHROME_BORDER_WIDTH, barY,
win->w - CHROME_BORDER_WIDTH * 2, barH);
// Draw each menu label // Draw each menu label
for (int32_t i = 0; i < win->menuBar->menuCount; i++) { for (int32_t i = 0; i < win->menuBar->menuCount; i++) {
MenuT *menu = &win->menuBar->menus[i]; MenuT *menu = &win->menuBar->menus[i];
@ -211,6 +227,8 @@ static void drawMenuBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fon
colors->menuFg, colors->menuBg, true); colors->menuFg, colors->menuBg, true);
} }
setClipRect(d, savedClipX, savedClipY, savedClipW, savedClipH);
// Draw bottom separator line // Draw bottom separator line
drawHLine(d, ops, win->x + CHROME_BORDER_WIDTH, barY + barH - 1, drawHLine(d, ops, win->x + CHROME_BORDER_WIDTH, barY + barH - 1,
win->w - CHROME_BORDER_WIDTH * 2, colors->windowShadow); win->w - CHROME_BORDER_WIDTH * 2, colors->windowShadow);
@ -234,7 +252,7 @@ static void drawResizeBreaks(DisplayT *d, const BlitOpsT *ops, const ColorScheme
uint32_t hi = colors->windowHighlight; uint32_t hi = colors->windowHighlight;
uint32_t sh = colors->windowShadow; uint32_t sh = colors->windowShadow;
int32_t bw = CHROME_BORDER_WIDTH; int32_t bw = CHROME_BORDER_WIDTH;
int32_t inset = 16; // distance from corner to groove int32_t inset = RESIZE_BREAK_INSET;
int32_t wx = win->x; int32_t wx = win->x;
int32_t wy = win->y; int32_t wy = win->y;
int32_t ww = win->w; int32_t ww = win->w;
@ -416,23 +434,23 @@ static void drawScrollbarArrow(DisplayT *d, const BlitOpsT *ops, const ColorSche
int32_t cy = y + size / 2; int32_t cy = y + size / 2;
uint32_t fg = colors->contentFg; uint32_t fg = colors->contentFg;
// Draw a small 5-row triangle // Draw a small triangle
for (int32_t i = 0; i < 4; i++) { for (int32_t i = 0; i < SB_ARROW_ROWS; i++) {
switch (dir) { switch (dir) {
case 0: // up case 0: // up
drawHLine(d, ops, cx - i, cy - 2 + i, 1 + i * 2, fg); drawHLine(d, ops, cx - i, cy - SB_ARROW_HALF + i, 1 + i * 2, fg);
break; break;
case 1: // down case 1: // down
drawHLine(d, ops, cx - i, cy + 2 - i, 1 + i * 2, fg); drawHLine(d, ops, cx - i, cy + SB_ARROW_HALF - i, 1 + i * 2, fg);
break; break;
case 2: // left case 2: // left
drawVLine(d, ops, cx - 2 + i, cy - i, 1 + i * 2, fg); drawVLine(d, ops, cx - SB_ARROW_HALF + i, cy - i, 1 + i * 2, fg);
break; break;
case 3: // right case 3: // right
drawVLine(d, ops, cx + 2 - i, cy - i, 1 + i * 2, fg); drawVLine(d, ops, cx + SB_ARROW_HALF - i, cy - i, 1 + i * 2, fg);
break; break;
} }
} }
@ -456,32 +474,34 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
// Close gadget on the LEFT (system menu / close) // Close gadget on the LEFT (system menu / close)
drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS); drawTitleGadget(d, ops, colors, g.closeX, g.gadgetY, g.gadgetS);
// Horizontal bar icon inside close gadget // Horizontal bar icon inside close gadget
drawHLine(d, ops, g.closeX + 3, g.gadgetY + g.gadgetS / 2, drawHLine(d, ops, g.closeX + CLOSE_ICON_INSET, g.gadgetY + g.gadgetS / 2,
g.gadgetS - 6, colors->contentFg); g.gadgetS - CLOSE_ICON_INSET * 2, colors->contentFg);
if (g.maxX >= 0) { if (g.maxX >= 0) {
// Maximize/restore gadget on the far RIGHT // Maximize/restore gadget on the far RIGHT
drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS); drawTitleGadget(d, ops, colors, g.maxX, g.gadgetY, g.gadgetS);
if (win->maximized) { if (win->maximized) {
// Restore icon: larger box outline filling more of the gadget // Restore icon: small box outline
int32_t bx = g.maxX + 2; int32_t bx = g.maxX + RESTORE_ICON_INSET;
int32_t by = g.gadgetY + 2; int32_t by = g.gadgetY + RESTORE_ICON_INSET;
int32_t bs = g.gadgetS - 4; int32_t bs = g.gadgetS - RESTORE_ICON_INSET * 2;
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
drawHLine(d, ops, bx, by, bs, colors->contentFg);
drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg);
drawVLine(d, ops, bx, by, bs, colors->contentFg);
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
} else {
// Maximize icon: larger box outline filling more of the gadget
int32_t bx = g.maxX + MAXIMIZE_ICON_INSET;
int32_t by = g.gadgetY + MAXIMIZE_ICON_INSET;
int32_t bs = g.gadgetS - MAXIMIZE_ICON_INSET * 2;
rectFill(d, ops, bx, by, bs, bs, colors->buttonFace); rectFill(d, ops, bx, by, bs, bs, colors->buttonFace);
drawHLine(d, ops, bx, by, bs, colors->contentFg); drawHLine(d, ops, bx, by, bs, colors->contentFg);
drawHLine(d, ops, bx, by + 1, bs, colors->contentFg); drawHLine(d, ops, bx, by + 1, bs, colors->contentFg);
drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg); drawHLine(d, ops, bx, by + bs - 1, bs, colors->contentFg);
drawVLine(d, ops, bx, by, bs, colors->contentFg); drawVLine(d, ops, bx, by, bs, colors->contentFg);
drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg); drawVLine(d, ops, bx + bs - 1, by, bs, colors->contentFg);
} else {
// Maximize icon: small box outline
rectFill(d, ops, g.maxX + 3, g.gadgetY + 3,
g.gadgetS - 6, g.gadgetS - 6, colors->buttonFace);
drawHLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawHLine(d, ops, g.maxX + 3, g.gadgetY + g.gadgetS - 4, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, g.maxX + 3, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
drawVLine(d, ops, g.maxX + g.gadgetS - 4, g.gadgetY + 3, g.gadgetS - 6, colors->contentFg);
} }
} }
@ -489,9 +509,8 @@ static void drawTitleBar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *fo
if (g.minX >= 0) { if (g.minX >= 0) {
drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS); drawTitleGadget(d, ops, colors, g.minX, g.gadgetY, g.gadgetS);
// Small square centered in minimize gadget // Small square centered in minimize gadget
int32_t miniSize = 4; rectFill(d, ops, g.minX + (g.gadgetS - MINIMIZE_ICON_SIZE) / 2, g.gadgetY + (g.gadgetS - MINIMIZE_ICON_SIZE) / 2,
rectFill(d, ops, g.minX + (g.gadgetS - miniSize) / 2, g.gadgetY + (g.gadgetS - miniSize) / 2, MINIMIZE_ICON_SIZE, MINIMIZE_ICON_SIZE, colors->contentFg);
miniSize, miniSize, colors->contentFg);
} }
// Title text — centered between close gadget and minimize, truncated to fit // Title text — centered between close gadget and minimize, truncated to fit
@ -1343,9 +1362,9 @@ int32_t wmMinimizedIconHit(const WindowStackT *stack, const DisplayT *d, int32_t
static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) { static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) {
// Title bar: close gadget + padding + 1 char of title + padding // Title bar: close gadget + padding + 1 char of title + padding
int32_t gadgetS = CHROME_TITLE_HEIGHT - 4; int32_t gadgetS = CHROME_TITLE_HEIGHT - GADGET_INSET * 2;
int32_t gadgetPad = 2; int32_t gadgetPad = GADGET_PAD;
int32_t charW = 8; // bitmap font char width int32_t charW = FONT_CHAR_WIDTH;
// Minimum title bar width: close gadget + 1 char + minimize gadget + padding // Minimum title bar width: close gadget + 1 char + minimize gadget + padding
int32_t titleMinW = gadgetPad + gadgetS + gadgetPad + charW + gadgetPad + gadgetS + gadgetPad; int32_t titleMinW = gadgetPad + gadgetS + gadgetPad + charW + gadgetPad + gadgetS + gadgetPad;
@ -1357,6 +1376,16 @@ static void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH) {
*minW = titleMinW + CHROME_BORDER_WIDTH * 2; *minW = titleMinW + CHROME_BORDER_WIDTH * 2;
// Menu bar width: ensure window is wide enough to show all menu labels
if (win->menuBar && win->menuBar->menuCount > 0) {
MenuT *last = &win->menuBar->menus[win->menuBar->menuCount - 1];
int32_t menuW = last->barX + last->barW + CHROME_TOTAL_SIDE;
if (menuW > *minW) {
*minW = menuW;
}
}
// Minimum height: border + title + inner border + some content + bottom chrome // Minimum height: border + title + inner border + some content + bottom chrome
*minH = CHROME_TOTAL_TOP + CHROME_TOTAL_BOTTOM + 1; *minH = CHROME_TOTAL_TOP + CHROME_TOTAL_BOTTOM + 1;

View file

@ -98,8 +98,8 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
int32_t textY = w->y + (w->h - font->charHeight) / 2; int32_t textY = w->y + (w->h - font->charHeight) / 2;
if (w->as.button.pressed) { if (w->as.button.pressed) {
textX++; textX += BUTTON_PRESS_OFFSET;
textY++; textY += BUTTON_PRESS_OFFSET;
} }
if (!w->enabled) { if (!w->enabled) {
@ -109,7 +109,7 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
} }
if (w->focused) { if (w->focused) {
int32_t off = w->as.button.pressed ? 1 : 0; int32_t off = w->as.button.pressed ? BUTTON_PRESS_OFFSET : 0;
drawFocusRect(d, ops, w->x + 3 + off, w->y + 3 + off, w->w - 6, w->h - 6, fg); drawFocusRect(d, ops, w->x + BUTTON_FOCUS_INSET + off, w->y + BUTTON_FOCUS_INSET + off, w->w - BUTTON_FOCUS_INSET * 2, w->h - BUTTON_FOCUS_INSET * 2, fg);
} }
} }

View file

@ -621,6 +621,17 @@ int32_t wgtCanvasSave(WidgetT *w, const char *path) {
} }
// ============================================================
// wgtCanvasSetMouseCallback
// ============================================================
void wgtCanvasSetMouseCallback(WidgetT *w, void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag)) {
if (w && w->type == WidgetCanvasE) {
w->as.canvas.onMouse = cb;
}
}
// ============================================================ // ============================================================
// wgtCanvasSetPenColor // wgtCanvasSetPenColor
// ============================================================ // ============================================================
@ -689,26 +700,25 @@ void widgetCanvasCalcMinSize(WidgetT *w, const BitmapFontT *font) {
void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) { void widgetCanvasOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
(void)root; (void)root;
if (!hit->as.canvas.onMouse) {
return;
}
// Convert widget coords to canvas coords // Convert widget coords to canvas coords
int32_t cx = vx - hit->x - CANVAS_BORDER; int32_t cx = vx - hit->x - CANVAS_BORDER;
int32_t cy = vy - hit->y - CANVAS_BORDER; int32_t cy = vy - hit->y - CANVAS_BORDER;
if (sDrawingCanvas == hit) { bool drag = (sDrawingCanvas == hit);
// Continuation of a drag stroke — draw line from last to current
if (hit->as.canvas.lastX >= 0) { if (!drag) {
canvasDrawLine(hit, hit->as.canvas.lastX, hit->as.canvas.lastY, cx, cy);
} else {
canvasDrawDot(hit, cx, cy);
}
} else {
// First click — start drawing, place a dot
sDrawingCanvas = hit; sDrawingCanvas = hit;
canvasDrawDot(hit, cx, cy);
} }
hit->as.canvas.lastX = cx; hit->as.canvas.lastX = cx;
hit->as.canvas.lastY = cy; hit->as.canvas.lastY = cy;
hit->as.canvas.onMouse(hit, cx, cy, drag);
wgtInvalidatePaint(hit); wgtInvalidatePaint(hit);
} }

View file

@ -262,8 +262,8 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
int32_t delta = x - sResizeStartX; int32_t delta = x - sResizeStartX;
int32_t newW = sResizeOrigW + delta; int32_t newW = sResizeOrigW + delta;
if (newW < 20) { if (newW < LISTVIEW_MIN_COL_W) {
newW = 20; newW = LISTVIEW_MIN_COL_W;
} }
if (newW != sResizeListView->as.listView->resolvedColW[sResizeCol]) { if (newW != sResizeListView->as.listView->resolvedColW[sResizeCol]) {

View file

@ -61,6 +61,8 @@ extern const WidgetClassT *widgetClassTable[];
#define SEPARATOR_THICKNESS 2 #define SEPARATOR_THICKNESS 2
#define BUTTON_PAD_H 8 #define BUTTON_PAD_H 8
#define BUTTON_PAD_V 4 #define BUTTON_PAD_V 4
#define BUTTON_FOCUS_INSET 3 // focus rect inset from button edge
#define BUTTON_PRESS_OFFSET 1 // text/focus shift when button is pressed
#define CHECKBOX_BOX_SIZE 12 #define CHECKBOX_BOX_SIZE 12
#define CHECKBOX_GAP 4 #define CHECKBOX_GAP 4
#define FRAME_BEVEL_BORDER 2 #define FRAME_BEVEL_BORDER 2
@ -75,6 +77,7 @@ extern const WidgetClassT *widgetClassTable[];
#define TAB_BORDER 2 #define TAB_BORDER 2
#define LISTBOX_BORDER 2 #define LISTBOX_BORDER 2
#define LISTVIEW_BORDER 2 #define LISTVIEW_BORDER 2
#define LISTVIEW_MIN_COL_W 20 // minimum column width during resize
#define TREE_INDENT 16 #define TREE_INDENT 16
#define TREE_EXPAND_SIZE 9 #define TREE_EXPAND_SIZE 9
#define TREE_ICON_GAP 4 #define TREE_ICON_GAP 4
@ -84,6 +87,8 @@ extern const WidgetClassT *widgetClassTable[];
#define SB_MIN_THUMB 14 #define SB_MIN_THUMB 14
#define SPLITTER_BAR_W 5 #define SPLITTER_BAR_W 5
#define SPLITTER_MIN_PANE 20 #define SPLITTER_MIN_PANE 20
#define TOOLBAR_PAD 2 // padding inside toolbar/statusbar
#define TOOLBAR_GAP 2 // gap between toolbar/statusbar children
// ============================================================ // ============================================================
// Inline helpers // Inline helpers

View file

@ -33,8 +33,8 @@ void widgetCalcMinSizeBox(WidgetT *w, const BitmapFontT *font) {
// Toolbar and StatusBar use tighter padding // Toolbar and StatusBar use tighter padding
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) { if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
pad = 2; pad = TOOLBAR_PAD;
gap = 2; gap = TOOLBAR_GAP;
} }
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) { for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
@ -145,8 +145,8 @@ void widgetLayoutBox(WidgetT *w, const BitmapFontT *font) {
// Toolbar and StatusBar use tighter padding // Toolbar and StatusBar use tighter padding
if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) { if (w->type == WidgetToolbarE || w->type == WidgetStatusBarE) {
pad = 2; pad = TOOLBAR_PAD;
gap = 2; gap = TOOLBAR_GAP;
} }
int32_t innerX = w->x + pad + fb; int32_t innerX = w->x + pad + fb;

View file

@ -1,48 +0,0 @@
# DVX GUI Demo Makefile for DJGPP cross-compilation
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx
LDFLAGS = -L../lib -ldvx -lm
OBJDIR = ../obj/dvxdemo
BINDIR = ../bin
LIBDIR = ../lib
SRCS = demo.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(BINDIR)/demo.exe
BMPS = $(wildcard *.bmp)
.PHONY: all clean lib
all: lib $(TARGET) $(addprefix $(BINDIR)/,$(BMPS))
lib:
$(MAKE) -C ../dvx
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a | $(BINDIR)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
$(EXE2COFF) $@
cat $(CWSDSTUB) $(BINDIR)/demo > $@
rm -f $(BINDIR)/demo
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(BINDIR):
mkdir -p $(BINDIR)
$(BINDIR)/%.bmp: %.bmp | $(BINDIR)
cp $< $@
# Dependencies
$(OBJDIR)/demo.o: demo.c ../dvx/dvxApp.h ../dvx/dvxWidget.h
clean:
rm -rf $(OBJDIR) $(BINDIR)

View file

@ -14,7 +14,7 @@ LIBDIR = ../lib
SRCS = shellMain.c shellApp.c shellExport.c SRCS = shellMain.c shellApp.c shellExport.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(BINDIR)/dvxshell.exe TARGET = $(BINDIR)/dvx.exe
.PHONY: all clean libs .PHONY: all clean libs
@ -25,10 +25,10 @@ libs:
$(MAKE) -C ../tasks $(MAKE) -C ../tasks
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR) $(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvxshell.map $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
$(EXE2COFF) $@ $(EXE2COFF) $@
cat $(CWSDSTUB) $(BINDIR)/dvxshell > $@ cat $(CWSDSTUB) $(BINDIR)/dvx > $@
rm -f $(BINDIR)/dvxshell rm -f $(BINDIR)/dvx
$(OBJDIR)/%.o: %.c | $(OBJDIR) $(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
@ -44,4 +44,4 @@ $(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialo
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h $(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h $(OBJDIR)/shellExport.o: shellExport.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h
clean: clean:
rm -rf $(OBJDIR) $(TARGET) rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log

View file

@ -182,6 +182,23 @@ int32_t shellLoadApp(AppContextT *ctx, const char *path) {
app->dxeCtx.shellCtx = ctx; app->dxeCtx.shellCtx = ctx;
app->dxeCtx.appId = id; app->dxeCtx.appId = id;
// Derive app directory from path (everything up to last '/' or '\')
snprintf(app->dxeCtx.appDir, sizeof(app->dxeCtx.appDir), "%s", path);
char *lastSlash = strrchr(app->dxeCtx.appDir, '/');
char *lastBack = strrchr(app->dxeCtx.appDir, '\\');
if (lastBack > lastSlash) {
lastSlash = lastBack;
}
if (lastSlash) {
*lastSlash = '\0';
} else {
app->dxeCtx.appDir[0] = '.';
app->dxeCtx.appDir[1] = '\0';
}
// Launch // Launch
sCurrentAppId = id; sCurrentAppId = id;

View file

@ -30,6 +30,7 @@ typedef struct {
typedef struct { typedef struct {
AppContextT *shellCtx; // the shell's GUI context AppContextT *shellCtx; // the shell's GUI context
int32_t appId; // this app's ID int32_t appId; // this app's ID
char appDir[260]; // directory containing the .app file
} DxeAppContextT; } DxeAppContextT;
// ============================================================ // ============================================================
@ -95,7 +96,7 @@ int32_t shellRunningAppCount(void);
// Logging // Logging
// ============================================================ // ============================================================
// Write a printf-style message to SHELL.LOG // Write a printf-style message to DVX.LOG
void shellLog(const char *fmt, ...); void shellLog(const char *fmt, ...);
// ============================================================ // ============================================================

View file

@ -10,6 +10,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h>
// ============================================================ // ============================================================
// Constants // Constants
@ -55,12 +56,13 @@ static void onAppButtonClick(WidgetT *w);
static void onPmClose(WindowT *win); static void onPmClose(WindowT *win);
static void onPmMenu(WindowT *win, int32_t menuId); static void onPmMenu(WindowT *win, int32_t menuId);
static void scanAppsDir(void); static void scanAppsDir(void);
static void scanAppsDirRecurse(const char *dirPath);
static void showAboutDialog(void); static void showAboutDialog(void);
static void updateStatusText(void); static void updateStatusText(void);
// Task Manager // Task Manager
static WindowT *sTmWindow = NULL; static WindowT *sTmWindow = NULL;
static WidgetT *sTmListBox = NULL; static WidgetT *sTmListView = NULL;
static void buildTaskManager(void); static void buildTaskManager(void);
static void onTmClose(WindowT *win); static void onTmClose(WindowT *win);
static void onTmEndTask(WidgetT *w); static void onTmEndTask(WidgetT *w);
@ -181,10 +183,17 @@ static void buildTaskManager(void) {
WidgetT *root = wgtInitWindow(sCtx, sTmWindow); WidgetT *root = wgtInitWindow(sCtx, sTmWindow);
// List box of running apps // List view of running apps
sTmListBox = wgtListBox(root); static const ListViewColT tmCols[] = {
sTmListBox->weight = 100; { "Name", wgtPercent(50), ListViewAlignLeftE },
sTmListBox->prefH = wgtPixels(160); { "Type", wgtPercent(25), ListViewAlignLeftE },
{ "Status", wgtPercent(25), ListViewAlignLeftE },
};
sTmListView = wgtListView(root);
sTmListView->weight = 100;
sTmListView->prefH = wgtPixels(160);
wgtListViewSetColumns(sTmListView, tmCols, 3);
// Button row // Button row
WidgetT *btnRow = wgtHBox(root); WidgetT *btnRow = wgtHBox(root);
@ -278,8 +287,8 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
static void onTmClose(WindowT *win) { static void onTmClose(WindowT *win) {
sTmListBox = NULL; sTmListView = NULL;
sTmWindow = NULL; sTmWindow = NULL;
dvxDestroyWindow(sCtx, win); dvxDestroyWindow(sCtx, win);
} }
@ -287,11 +296,11 @@ static void onTmClose(WindowT *win) {
static void onTmEndTask(WidgetT *w) { static void onTmEndTask(WidgetT *w) {
(void)w; (void)w;
if (!sTmListBox) { if (!sTmListView) {
return; return;
} }
int32_t sel = wgtListBoxGetSelected(sTmListBox); int32_t sel = wgtListViewGetSelected(sTmListView);
if (sel < 0) { if (sel < 0) {
return; return;
@ -320,11 +329,11 @@ static void onTmEndTask(WidgetT *w) {
static void onTmSwitchTo(WidgetT *w) { static void onTmSwitchTo(WidgetT *w) {
(void)w; (void)w;
if (!sTmListBox) { if (!sTmListView) {
return; return;
} }
int32_t sel = wgtListBoxGetSelected(sTmListBox); int32_t sel = wgtListViewGetSelected(sTmListView);
if (sel < 0) { if (sel < 0) {
return; return;
@ -363,41 +372,69 @@ static void onTmSwitchTo(WidgetT *w) {
static void refreshTaskList(void) { static void refreshTaskList(void) {
if (!sTmListBox) { if (!sTmListView) {
return; return;
} }
static const char *items[SHELL_MAX_APPS]; // 3 columns per row: Name, Type, Status
static char names[SHELL_MAX_APPS][SHELL_APP_NAME_MAX + 16]; static const char *cells[SHELL_MAX_APPS * 3];
int32_t count = 0; static char typeStrs[SHELL_MAX_APPS][12];
int32_t rowCount = 0;
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
ShellAppT *app = shellGetApp(i); ShellAppT *app = shellGetApp(i);
if (app && app->state == AppStateRunningE) { if (app && app->state == AppStateRunningE) {
const char *state = app->hasMainLoop ? "task" : "callback"; int32_t base = rowCount * 3;
snprintf(names[count], sizeof(names[count]), "%s [%s]", app->name, state); cells[base] = app->name;
items[count] = names[count]; snprintf(typeStrs[rowCount], sizeof(typeStrs[rowCount]), "%s", app->hasMainLoop ? "Task" : "Callback");
count++; cells[base + 1] = typeStrs[rowCount];
cells[base + 2] = "Running";
rowCount++;
} }
} }
wgtListBoxSetItems(sTmListBox, items, count); wgtListViewSetData(sTmListView, cells, rowCount);
} }
static void scanAppsDir(void) { static void scanAppsDir(void) {
DIR *dir = opendir("apps"); sDxeCount = 0;
scanAppsDirRecurse("apps");
shellLog("Shell: found %ld app(s)", (long)sDxeCount);
}
static void scanAppsDirRecurse(const char *dirPath) {
DIR *dir = opendir(dirPath);
if (!dir) { if (!dir) {
shellLog("Shell: apps/ directory not found"); if (sDxeCount == 0) {
shellLog("Shell: %s directory not found", dirPath);
}
return; return;
} }
sDxeCount = 0;
struct dirent *ent; struct dirent *ent;
while ((ent = readdir(dir)) != NULL && sDxeCount < MAX_DXE_FILES) { while ((ent = readdir(dir)) != NULL && sDxeCount < MAX_DXE_FILES) {
// Skip . and ..
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
}
char fullPath[MAX_PATH_LEN];
snprintf(fullPath, sizeof(fullPath), "%s/%s", dirPath, ent->d_name);
// Check if this is a directory — recurse into it
struct stat st;
if (stat(fullPath, &st) == 0 && S_ISDIR(st.st_mode)) {
scanAppsDirRecurse(fullPath);
continue;
}
int32_t len = strlen(ent->d_name); int32_t len = strlen(ent->d_name);
if (len < 5) { if (len < 5) {
@ -428,12 +465,11 @@ static void scanAppsDir(void) {
entry->name[0] -= 32; entry->name[0] -= 32;
} }
snprintf(entry->path, sizeof(entry->path), "apps/%s", ent->d_name); snprintf(entry->path, sizeof(entry->path), "%s", fullPath);
sDxeCount++; sDxeCount++;
} }
closedir(dir); closedir(dir);
shellLog("Shell: found %ld app(s)", (long)sDxeCount);
} }

View file

@ -167,6 +167,8 @@ DXE_EXPORT_TABLE(shellExportTable)
DXE_EXPORT(wmSetIcon) DXE_EXPORT(wmSetIcon)
DXE_EXPORT(wmCreateMenu) DXE_EXPORT(wmCreateMenu)
DXE_EXPORT(wmFreeMenu) DXE_EXPORT(wmFreeMenu)
DXE_EXPORT(wmUpdateContentRect)
DXE_EXPORT(wmReallocContentBuf)
// dvxWidget.h — window integration // dvxWidget.h — window integration
DXE_EXPORT(wgtInitWindow) DXE_EXPORT(wgtInitWindow)
@ -278,6 +280,7 @@ DXE_EXPORT_TABLE(shellExportTable)
// dvxWidget.h — canvas // dvxWidget.h — canvas
DXE_EXPORT(wgtCanvas) DXE_EXPORT(wgtCanvas)
DXE_EXPORT(wgtCanvasClear) DXE_EXPORT(wgtCanvasClear)
DXE_EXPORT(wgtCanvasSetMouseCallback)
DXE_EXPORT(wgtCanvasSetPenColor) DXE_EXPORT(wgtCanvasSetPenColor)
DXE_EXPORT(wgtCanvasSetPenSize) DXE_EXPORT(wgtCanvasSetPenSize)
DXE_EXPORT(wgtCanvasSave) DXE_EXPORT(wgtCanvasSave)
@ -301,6 +304,7 @@ DXE_EXPORT_TABLE(shellExportTable)
// dvxWidget.h — operations // dvxWidget.h — operations
DXE_EXPORT(wgtInvalidate) DXE_EXPORT(wgtInvalidate)
DXE_EXPORT(wgtInvalidatePaint) DXE_EXPORT(wgtInvalidatePaint)
DXE_EXPORT(wgtSetDebugLayout)
DXE_EXPORT(wgtSetText) DXE_EXPORT(wgtSetText)
DXE_EXPORT(wgtGetText) DXE_EXPORT(wgtGetText)
DXE_EXPORT(wgtSetEnabled) DXE_EXPORT(wgtSetEnabled)

View file

@ -131,7 +131,7 @@ static void logCrash(int sig) {
// ============================================================ // ============================================================
// shellLog — append a line to SHELL.LOG // shellLog — append a line to DVX.LOG
// ============================================================ // ============================================================
void shellLog(const char *fmt, ...) { void shellLog(const char *fmt, ...) {
@ -163,7 +163,7 @@ void shellRegisterDesktopUpdate(void (*updateFn)(void)) {
// ============================================================ // ============================================================
int main(void) { int main(void) {
sLogFile = fopen("shell.log", "w"); sLogFile = fopen("dvx.log", "w");
shellLog("DVX Shell starting..."); shellLog("DVX Shell starting...");
// Initialize GUI // Initialize GUI

View file

@ -23,7 +23,9 @@ DEMO_TARGET = $(BINDIR)/tsdemo.exe
.PHONY: all clean .PHONY: all clean
all: $(TARGET) $(DEMO_TARGET) all: $(TARGET)
demo: $(DEMO_TARGET)
$(TARGET): $(OBJS) | $(LIBDIR) $(TARGET): $(OBJS) | $(LIBDIR)
$(AR) rcs $@ $(OBJS) $(AR) rcs $@ $(OBJS)
@ -52,4 +54,4 @@ $(OBJDIR)/taskswitch.o: taskswitch.c taskswitch.h thirdparty/stb_ds.h
$(OBJDIR)/demo.o: demo.c taskswitch.h $(OBJDIR)/demo.o: demo.c taskswitch.h
clean: clean:
rm -rf $(OBJDIR) $(TARGET) $(DEMO_TARGET) rm -f $(OBJS) $(DEMO_OBJS) $(TARGET) $(DEMO_TARGET)