diff --git a/apps/dvxdemo/dvxdemo.c b/apps/dvxdemo/dvxdemo.c index 596442b..38e380e 100644 --- a/apps/dvxdemo/dvxdemo.c +++ b/apps/dvxdemo/dvxdemo.c @@ -186,7 +186,7 @@ static void onMenuCb(WindowT *win, int32_t menuId) { case CMD_HELP_ABOUT: dvxMessageBox(sAc, "About DVX Demo", - "DVX GUI Demonstration\n\n" + "DVX GUI Demonstration\n" "A DOS Visual eXecutive windowing system for DOS.", MB_OK | MB_ICONINFO); break; diff --git a/apps/progman/progman.c b/apps/progman/progman.c index 976ad6c..925ef36 100644 --- a/apps/progman/progman.c +++ b/apps/progman/progman.c @@ -17,6 +17,7 @@ #include #include #include +#include // ============================================================ // Constants @@ -68,12 +69,13 @@ static void onAppButtonClick(WidgetT *w); static void onPmClose(WindowT *win); static void onPmMenu(WindowT *win, int32_t menuId); static void scanAppsDir(void); +static void scanAppsDirRecurse(const char *dirPath); static void showAboutDialog(void); static void updateStatusText(void); // Task Manager static WindowT *sTmWindow = NULL; -static WidgetT *sTmListBox = NULL; +static WidgetT *sTmListView = NULL; static void buildTaskManager(void); static void onTmClose(WindowT *win); static void onTmEndTask(WidgetT *w); @@ -206,10 +208,22 @@ static void buildTaskManager(void) { WidgetT *root = wgtInitWindow(sAc, sTmWindow); - // List box of running apps - sTmListBox = wgtListBox(root); - sTmListBox->weight = 100; - sTmListBox->prefH = wgtPixels(160); + // List view of running apps + ListViewColT tmCols[3]; + tmCols[0].title = "Name"; + tmCols[0].width = wgtPercent(50); + tmCols[0].align = ListViewAlignLeftE; + tmCols[1].title = "Type"; + tmCols[1].width = wgtPercent(25); + tmCols[1].align = ListViewAlignLeftE; + tmCols[2].title = "Status"; + tmCols[2].width = wgtPercent(25); + tmCols[2].align = ListViewAlignLeftE; + + sTmListView = wgtListView(root); + sTmListView->weight = 100; + sTmListView->prefH = wgtPixels(160); + wgtListViewSetColumns(sTmListView, tmCols, 3); // Button row WidgetT *btnRow = wgtHBox(root); @@ -324,8 +338,8 @@ static void onPmMenu(WindowT *win, int32_t menuId) { static void onTmClose(WindowT *win) { - sTmListBox = NULL; - sTmWindow = NULL; + sTmListView = NULL; + sTmWindow = NULL; dvxDestroyWindow(sAc, win); } @@ -333,11 +347,11 @@ static void onTmClose(WindowT *win) { static void onTmEndTask(WidgetT *w) { (void)w; - if (!sTmListBox) { + if (!sTmListView) { return; } - int32_t sel = wgtListBoxGetSelected(sTmListBox); + int32_t sel = wgtListViewGetSelected(sTmListView); if (sel < 0) { return; @@ -370,11 +384,11 @@ static void onTmEndTask(WidgetT *w) { static void onTmSwitchTo(WidgetT *w) { (void)w; - if (!sTmListBox) { + if (!sTmListView) { return; } - int32_t sel = wgtListBoxGetSelected(sTmListBox); + int32_t sel = wgtListViewGetSelected(sTmListView); if (sel < 0) { return; @@ -417,13 +431,14 @@ static void onTmSwitchTo(WidgetT *w) { static void refreshTaskList(void) { - if (!sTmListBox) { + if (!sTmListView) { return; } - static const char *items[SHELL_MAX_APPS]; - static char names[SHELL_MAX_APPS][SHELL_APP_NAME_MAX + 16]; - int32_t count = 0; + // 3 columns per row: Name, Type, Status + static const char *cells[SHELL_MAX_APPS * 3]; + static char typeStrs[SHELL_MAX_APPS][12]; + int32_t rowCount = 0; for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { if (i == sMyAppId) { @@ -433,29 +448,56 @@ static void refreshTaskList(void) { ShellAppT *app = shellGetApp(i); if (app && app->state == AppStateRunningE) { - const char *state = app->hasMainLoop ? "task" : "callback"; - snprintf(names[count], sizeof(names[count]), "%s [%s]", app->name, state); - items[count] = names[count]; - count++; + int32_t base = rowCount * 3; + cells[base] = app->name; + snprintf(typeStrs[rowCount], sizeof(typeStrs[rowCount]), "%s", app->hasMainLoop ? "Task" : "Callback"); + cells[base + 1] = typeStrs[rowCount]; + cells[base + 2] = "Running"; + rowCount++; } } - wgtListBoxSetItems(sTmListBox, items, count); + wgtListViewSetData(sTmListView, cells, rowCount); } static void scanAppsDir(void) { - DIR *dir = opendir("apps"); + sAppCount = 0; + scanAppsDirRecurse("apps"); + shellLog("Progman: found %ld app(s)", (long)sAppCount); +} + + +static void scanAppsDirRecurse(const char *dirPath) { + DIR *dir = opendir(dirPath); if (!dir) { - shellLog("Progman: apps/ directory not found"); + if (sAppCount == 0) { + shellLog("Progman: %s directory not found", dirPath); + } + return; } - sAppCount = 0; struct dirent *ent; while ((ent = readdir(dir)) != NULL && sAppCount < MAX_APP_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); if (len < 5) { @@ -491,12 +533,11 @@ static void scanAppsDir(void) { entry->name[0] -= 32; } - snprintf(entry->path, sizeof(entry->path), "apps/%s", ent->d_name); + snprintf(entry->path, sizeof(entry->path), "%s", fullPath); sAppCount++; } closedir(dir); - shellLog("Progman: found %ld app(s)", (long)sAppCount); } diff --git a/dvx/platform/dvxPlatform.h b/dvx/platform/dvxPlatform.h index b0c1276..7470ea8 100644 --- a/dvx/platform/dvxPlatform.h +++ b/dvx/platform/dvxPlatform.h @@ -38,6 +38,10 @@ int32_t platformVideoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, i // Shut down video — restore text mode, unmap framebuffer, free backbuffer. void platformVideoShutdown(DisplayT *d); +// Enumerate available graphics modes. Calls cb(w, h, bpp, userData) for +// each LFB-capable graphics mode found. +void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData); + // Set palette entries (8-bit mode). pal is R,G,B triplets. void platformVideoSetPalette(const uint8_t *pal, int32_t firstEntry, int32_t count); diff --git a/dvx/platform/dvxPlatformDos.c b/dvx/platform/dvxPlatformDos.c index 66fc5a7..e475d3b 100644 --- a/dvx/platform/dvxPlatformDos.c +++ b/dvx/platform/dvxPlatformDos.c @@ -25,6 +25,7 @@ static int32_t findBestMode(int32_t requestedW, int32_t requestedH, int32_t preferredBpp, uint16_t *outMode, DisplayT *d); static void getModeInfo(uint16_t mode, DisplayT *d, int32_t *score, int32_t requestedW, int32_t requestedH, int32_t preferredBpp); static int32_t mapLfb(DisplayT *d, uint32_t physAddr); +void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData); static int32_t setVesaMode(uint16_t mode); // Alt+key scan code to ASCII lookup table (indexed by BIOS scan code). @@ -135,6 +136,68 @@ static int32_t findBestMode(int32_t requestedW, int32_t requestedH, int32_t pref } +// ============================================================ +// platformVideoEnumModes +// ============================================================ + +void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData) { + __dpmi_regs r; + + memset(&r, 0, sizeof(r)); + r.x.ax = 0x4F00; + r.x.es = __tb >> 4; + r.x.di = __tb & 0x0F; + + // Write "VBE2" signature to request VBE 2.0+ info + _farpokeb(_dos_ds, __tb + 0, 'V'); + _farpokeb(_dos_ds, __tb + 1, 'B'); + _farpokeb(_dos_ds, __tb + 2, 'E'); + _farpokeb(_dos_ds, __tb + 3, '2'); + + __dpmi_int(0x10, &r); + + if (r.x.ax != 0x004F) { + return; + } + + uint16_t modeListOff = _farpeekw(_dos_ds, __tb + 14); + uint16_t modeListSeg = _farpeekw(_dos_ds, __tb + 16); + uint32_t modeListAddr = ((uint32_t)modeListSeg << 4) + modeListOff; + + for (int32_t i = 0; i < 256; i++) { + uint16_t mode = _farpeekw(_dos_ds, modeListAddr + i * 2); + + if (mode == 0xFFFF) { + break; + } + + memset(&r, 0, sizeof(r)); + r.x.ax = 0x4F01; + r.x.cx = mode; + r.x.es = __tb >> 4; + r.x.di = __tb & 0x0F; + __dpmi_int(0x10, &r); + + if (r.x.ax != 0x004F) { + continue; + } + + uint16_t attr = _farpeekw(_dos_ds, __tb + 0); + + // Only report LFB-capable graphics modes + if (!(attr & 0x0080) || !(attr & 0x0010)) { + continue; + } + + int32_t w = _farpeekw(_dos_ds, __tb + 18); + int32_t h = _farpeekw(_dos_ds, __tb + 20); + int32_t bpp = _farpeekb(_dos_ds, __tb + 25); + + cb(w, h, bpp, userData); + } +} + + // ============================================================ // getModeInfo // ============================================================ diff --git a/dvxshell/shellApp.c b/dvxshell/shellApp.c index b6fca70..ad13bb6 100644 --- a/dvxshell/shellApp.c +++ b/dvxshell/shellApp.c @@ -30,7 +30,7 @@ void shellForceKillApp(AppContextT *ctx, ShellAppT *app); ShellAppT *shellGetApp(int32_t appId); int32_t shellLoadApp(AppContextT *ctx, const char *path); void shellReapApp(AppContextT *ctx, ShellAppT *app); -void shellReapApps(AppContextT *ctx); +bool shellReapApps(AppContextT *ctx); int32_t shellRunningAppCount(void); void shellTerminateAllApps(AppContextT *ctx); @@ -267,12 +267,17 @@ void shellReapApp(AppContextT *ctx, ShellAppT *app) { } -void shellReapApps(AppContextT *ctx) { +bool shellReapApps(AppContextT *ctx) { + bool reaped = false; + for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { if (sApps[i].state == AppStateTerminatingE) { shellReapApp(ctx, &sApps[i]); + reaped = true; } } + + return reaped; } diff --git a/dvxshell/shellApp.h b/dvxshell/shellApp.h index 7f204e9..63a5347 100644 --- a/dvxshell/shellApp.h +++ b/dvxshell/shellApp.h @@ -75,7 +75,7 @@ void shellAppInit(void); int32_t shellLoadApp(AppContextT *ctx, const char *path); // Reap finished callback-only apps (call each frame from main loop) -void shellReapApps(AppContextT *ctx); +bool shellReapApps(AppContextT *ctx); // Gracefully shut down a single app void shellReapApp(AppContextT *ctx, ShellAppT *app); @@ -111,7 +111,7 @@ void shellExportInit(void); // ============================================================ // Default desktop app path -#define SHELL_DESKTOP_APP "apps/progman.app" +#define SHELL_DESKTOP_APP "apps/progman/progman.app" // Register a callback for app state changes (load, reap, crash). // The desktop app calls this during appMain to receive notifications. diff --git a/dvxshell/shellDesktop.c b/dvxshell/shellDesktop.c deleted file mode 100644 index 86c8e85..0000000 --- a/dvxshell/shellDesktop.c +++ /dev/null @@ -1,526 +0,0 @@ -// shellDesktop.c — Program Manager window for DVX Shell -// -// Displays a grid of available DXE apps from the apps/ directory. -// Double-click or Enter launches an app. Includes Task Manager (Ctrl+Esc). - -#include "shellApp.h" -#include "dvxDialog.h" - -#include -#include -#include -#include -#include - -// ============================================================ -// Constants -// ============================================================ - -#define MAX_DXE_FILES 64 -#define MAX_PATH_LEN 260 -#define PM_GRID_COLS 4 -#define PM_BTN_W 100 -#define PM_BTN_H 24 - -// Menu command IDs -#define CMD_RUN 100 -#define CMD_EXIT 101 -#define CMD_CASCADE 200 -#define CMD_TILE 201 -#define CMD_TILE_H 202 -#define CMD_TILE_V 203 -#define CMD_ABOUT 300 -#define CMD_TASK_MGR 301 - -// ============================================================ -// Module state -// ============================================================ - -typedef struct { - char name[SHELL_APP_NAME_MAX]; // display name (filename without .app) - char path[MAX_PATH_LEN]; // full path -} DxeEntryT; - -static AppContextT *sCtx = NULL; -static WindowT *sPmWindow = NULL; -static WidgetT *sStatusLabel = NULL; -static DxeEntryT sDxeFiles[MAX_DXE_FILES]; -static int32_t sDxeCount = 0; - -// ============================================================ -// Prototypes -// ============================================================ - -static void buildPmWindow(void); -static void onAppButtonClick(WidgetT *w); -static void onPmClose(WindowT *win); -static void onPmMenu(WindowT *win, int32_t menuId); -static void scanAppsDir(void); -static void scanAppsDirRecurse(const char *dirPath); -static void showAboutDialog(void); -static void updateStatusText(void); - -// Task Manager -static WindowT *sTmWindow = NULL; -static WidgetT *sTmListView = NULL; -static void buildTaskManager(void); -static void onTmClose(WindowT *win); -static void onTmEndTask(WidgetT *w); -static void onTmSwitchTo(WidgetT *w); -static void refreshTaskList(void); - -// ============================================================ -// Static functions (alphabetical) -// ============================================================ - -static void buildPmWindow(void) { - int32_t screenW = sCtx->display.width; - int32_t screenH = sCtx->display.height; - int32_t winW = 440; - int32_t winH = 340; - int32_t winX = (screenW - winW) / 2; - int32_t winY = (screenH - winH) / 4; - - sPmWindow = dvxCreateWindow(sCtx, "Program Manager", winX, winY, winW, winH, true); - - if (!sPmWindow) { - return; - } - - sPmWindow->appId = 0; - sPmWindow->onClose = onPmClose; - sPmWindow->onMenu = onPmMenu; - - // Menu bar - MenuBarT *menuBar = wmAddMenuBar(sPmWindow); - MenuT *fileMenu = wmAddMenu(menuBar, "&File"); - wmAddMenuItem(fileMenu, "&Run...", CMD_RUN); - wmAddMenuSeparator(fileMenu); - wmAddMenuItem(fileMenu, "E&xit Shell", CMD_EXIT); - - MenuT *windowMenu = wmAddMenu(menuBar, "&Window"); - wmAddMenuItem(windowMenu, "&Cascade", CMD_CASCADE); - wmAddMenuItem(windowMenu, "&Tile", CMD_TILE); - wmAddMenuItem(windowMenu, "Tile &Horizontally", CMD_TILE_H); - wmAddMenuItem(windowMenu, "Tile &Vertically", CMD_TILE_V); - - MenuT *helpMenu = wmAddMenu(menuBar, "&Help"); - wmAddMenuItem(helpMenu, "&About DVX Shell...", CMD_ABOUT); - wmAddMenuSeparator(helpMenu); - wmAddMenuItem(helpMenu, "&Task Manager\tCtrl+Esc", CMD_TASK_MGR); - - // Widget tree - WidgetT *root = wgtInitWindow(sCtx, sPmWindow); - - // App button grid in a frame - WidgetT *appFrame = wgtFrame(root, "Applications"); - appFrame->weight = 100; - - if (sDxeCount == 0) { - WidgetT *lbl = wgtLabel(appFrame, "(No applications found in apps/ directory)"); - (void)lbl; - } else { - // Build rows of buttons - int32_t row = 0; - WidgetT *hbox = NULL; - - for (int32_t i = 0; i < sDxeCount; i++) { - if (i % PM_GRID_COLS == 0) { - hbox = wgtHBox(appFrame); - hbox->spacing = wgtPixels(8); - row++; - } - - WidgetT *btn = wgtButton(hbox, sDxeFiles[i].name); - btn->prefW = wgtPixels(PM_BTN_W); - btn->prefH = wgtPixels(PM_BTN_H); - btn->userData = &sDxeFiles[i]; - btn->onDblClick = onAppButtonClick; - btn->onClick = onAppButtonClick; - } - - (void)row; - } - - // Status bar - WidgetT *statusBar = wgtStatusBar(root); - sStatusLabel = wgtLabel(statusBar, ""); - sStatusLabel->weight = 100; - updateStatusText(); - - dvxFitWindow(sCtx, sPmWindow); -} - - -static void buildTaskManager(void) { - if (sTmWindow) { - // Already open — just raise it - for (int32_t i = 0; i < sCtx->stack.count; i++) { - if (sCtx->stack.windows[i] == sTmWindow) { - wmRaiseWindow(&sCtx->stack, &sCtx->dirty, i); - wmSetFocus(&sCtx->stack, &sCtx->dirty, sCtx->stack.count - 1); - break; - } - } - return; - } - - int32_t screenW = sCtx->display.width; - int32_t screenH = sCtx->display.height; - int32_t winW = 300; - int32_t winH = 280; - int32_t winX = (screenW - winW) / 2; - int32_t winY = (screenH - winH) / 3; - - sTmWindow = dvxCreateWindow(sCtx, "Task Manager", winX, winY, winW, winH, true); - - if (!sTmWindow) { - return; - } - - sTmWindow->appId = 0; - sTmWindow->onClose = onTmClose; - - WidgetT *root = wgtInitWindow(sCtx, sTmWindow); - - // List view of running apps - static const ListViewColT tmCols[] = { - { "Name", wgtPercent(50), ListViewAlignLeftE }, - { "Type", wgtPercent(25), ListViewAlignLeftE }, - { "Status", wgtPercent(25), ListViewAlignLeftE }, - }; - - sTmListView = wgtListView(root); - sTmListView->weight = 100; - sTmListView->prefH = wgtPixels(160); - wgtListViewSetColumns(sTmListView, tmCols, 3); - - // Button row - WidgetT *btnRow = wgtHBox(root); - btnRow->align = AlignEndE; - btnRow->spacing = wgtPixels(8); - - WidgetT *switchBtn = wgtButton(btnRow, "Switch To"); - switchBtn->onClick = onTmSwitchTo; - switchBtn->prefW = wgtPixels(90); - - WidgetT *endBtn = wgtButton(btnRow, "End Task"); - endBtn->onClick = onTmEndTask; - endBtn->prefW = wgtPixels(90); - - refreshTaskList(); - dvxFitWindow(sCtx, sTmWindow); -} - - -static void onAppButtonClick(WidgetT *w) { - DxeEntryT *entry = (DxeEntryT *)w->userData; - - if (!entry) { - return; - } - - shellLoadApp(sCtx, entry->path); - updateStatusText(); -} - - -static void onPmClose(WindowT *win) { - (void)win; - // Confirm exit - int32_t result = dvxMessageBox(sCtx, "Exit Shell", "Are you sure you want to exit DVX Shell?", MB_YESNO | MB_ICONQUESTION); - - if (result == ID_YES) { - sCtx->running = false; - } -} - - -static void onPmMenu(WindowT *win, int32_t menuId) { - (void)win; - - switch (menuId) { - case CMD_RUN: - { - FileFilterT filters[] = { - { "Applications (*.app)", "*.app" }, - { "All Files (*.*)", "*.*" } - }; - char path[MAX_PATH_LEN]; - - if (dvxFileDialog(sCtx, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) { - shellLoadApp(sCtx, path); - updateStatusText(); - } - } - break; - - case CMD_EXIT: - onPmClose(sPmWindow); - break; - - case CMD_CASCADE: - dvxCascadeWindows(sCtx); - break; - - case CMD_TILE: - dvxTileWindows(sCtx); - break; - - case CMD_TILE_H: - dvxTileWindowsH(sCtx); - break; - - case CMD_TILE_V: - dvxTileWindowsV(sCtx); - break; - - case CMD_ABOUT: - showAboutDialog(); - break; - - case CMD_TASK_MGR: - buildTaskManager(); - break; - } -} - - -static void onTmClose(WindowT *win) { - sTmListView = NULL; - sTmWindow = NULL; - dvxDestroyWindow(sCtx, win); -} - - -static void onTmEndTask(WidgetT *w) { - (void)w; - - if (!sTmListView) { - return; - } - - int32_t sel = wgtListViewGetSelected(sTmListView); - - if (sel < 0) { - return; - } - - // Map list index to app ID - int32_t idx = 0; - - for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { - ShellAppT *app = shellGetApp(i); - - if (app && app->state == AppStateRunningE) { - if (idx == sel) { - shellForceKillApp(sCtx, app); - refreshTaskList(); - updateStatusText(); - return; - } - - idx++; - } - } -} - - -static void onTmSwitchTo(WidgetT *w) { - (void)w; - - if (!sTmListView) { - return; - } - - int32_t sel = wgtListViewGetSelected(sTmListView); - - if (sel < 0) { - return; - } - - // Map list index to app ID, find topmost window for that app - int32_t idx = 0; - - for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { - ShellAppT *app = shellGetApp(i); - - if (app && app->state == AppStateRunningE) { - if (idx == sel) { - // Find the topmost window for this app - for (int32_t j = sCtx->stack.count - 1; j >= 0; j--) { - WindowT *win = sCtx->stack.windows[j]; - - if (win->appId == i) { - if (win->minimized) { - wmRestoreMinimized(&sCtx->stack, &sCtx->dirty, win); - } - - wmRaiseWindow(&sCtx->stack, &sCtx->dirty, j); - wmSetFocus(&sCtx->stack, &sCtx->dirty, sCtx->stack.count - 1); - return; - } - } - - return; - } - - idx++; - } - } -} - - -static void refreshTaskList(void) { - if (!sTmListView) { - return; - } - - // 3 columns per row: Name, Type, Status - static const char *cells[SHELL_MAX_APPS * 3]; - static char typeStrs[SHELL_MAX_APPS][12]; - int32_t rowCount = 0; - - for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { - ShellAppT *app = shellGetApp(i); - - if (app && app->state == AppStateRunningE) { - int32_t base = rowCount * 3; - cells[base] = app->name; - snprintf(typeStrs[rowCount], sizeof(typeStrs[rowCount]), "%s", app->hasMainLoop ? "Task" : "Callback"); - cells[base + 1] = typeStrs[rowCount]; - cells[base + 2] = "Running"; - rowCount++; - } - } - - wgtListViewSetData(sTmListView, cells, rowCount); -} - - -static void scanAppsDir(void) { - 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 (sDxeCount == 0) { - shellLog("Shell: %s directory not found", dirPath); - } - - return; - } - - struct dirent *ent; - - 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); - - if (len < 5) { - continue; - } - - // Check for .app extension (case-insensitive) - const char *ext = ent->d_name + len - 4; - - if (strcmp(ext, ".app") != 0 && strcmp(ext, ".APP") != 0) { - continue; - } - - DxeEntryT *entry = &sDxeFiles[sDxeCount]; - - // Name = filename without extension - int32_t nameLen = len - 4; - - if (nameLen >= SHELL_APP_NAME_MAX) { - nameLen = SHELL_APP_NAME_MAX - 1; - } - - memcpy(entry->name, ent->d_name, nameLen); - entry->name[nameLen] = '\0'; - - // Capitalize first letter for display - if (entry->name[0] >= 'a' && entry->name[0] <= 'z') { - entry->name[0] -= 32; - } - - snprintf(entry->path, sizeof(entry->path), "%s", fullPath); - sDxeCount++; - } - - closedir(dir); -} - - -static void showAboutDialog(void) { - dvxMessageBox(sCtx, "About DVX Shell", - "DVX Shell 1.0\n\nA DOS Visual eXecutive desktop shell for DJGPP/DPMI. Using DXE3 dynamic loading for application modules.", - MB_OK | MB_ICONINFO); -} - - -static void updateStatusText(void) { - if (!sStatusLabel) { - return; - } - - static char buf[64]; - int32_t count = shellRunningAppCount(); - - if (count == 0) { - snprintf(buf, sizeof(buf), "No applications running"); - } else if (count == 1) { - snprintf(buf, sizeof(buf), "1 application running"); - } else { - snprintf(buf, sizeof(buf), "%ld applications running", (long)count); - } - - wgtSetText(sStatusLabel, buf); -} - -// ============================================================ -// Public API -// ============================================================ - -void shellDesktopInit(AppContextT *ctx) { - sCtx = ctx; - scanAppsDir(); - buildPmWindow(); -} - - -void shellDesktopUpdateStatus(void) { - updateStatusText(); - - // Also refresh task manager if open - if (sTmWindow) { - refreshTaskList(); - } -} - - -void shellTaskManager(AppContextT *ctx) { - sCtx = ctx; - buildTaskManager(); -} diff --git a/dvxshell/shellExport.c b/dvxshell/shellExport.c index 5e4344e..b3d677c 100644 --- a/dvxshell/shellExport.c +++ b/dvxshell/shellExport.c @@ -13,6 +13,7 @@ #include "taskswitch.h" #include +#include #include #include #include @@ -402,6 +403,9 @@ DXE_EXPORT_TABLE(shellExportTable) DXE_EXPORT(readdir) DXE_EXPORT(closedir) + // libc — filesystem + DXE_EXPORT(stat) + // libc — misc DXE_EXPORT(qsort) DXE_EXPORT(rand) diff --git a/dvxshell/shellMain.c b/dvxshell/shellMain.c index 792b4ec..1f73fe8 100644 --- a/dvxshell/shellMain.c +++ b/dvxshell/shellMain.c @@ -6,6 +6,7 @@ #include "shellApp.h" #include "dvxDialog.h" +#include "platform/dvxPlatform.h" #include #include @@ -33,6 +34,7 @@ static void desktopUpdate(void); static void idleYield(void *ctx); static void installCrashHandler(void); static void logCrash(int sig); +static void logVideoMode(int32_t w, int32_t h, int32_t bpp, void *userData); // ============================================================ // crashHandler — catch page faults and other fatal signals @@ -130,6 +132,12 @@ static void logCrash(int sig) { } +static void logVideoMode(int32_t w, int32_t h, int32_t bpp, void *userData) { + (void)userData; + shellLog(" %ldx%ld %ldbpp", (long)w, (long)h, (long)bpp); +} + + // ============================================================ // shellLog — append a line to DVX.LOG // ============================================================ @@ -179,6 +187,10 @@ int main(void) { return 1; } + shellLog("Available video modes:"); + platformVideoEnumModes(logVideoMode, NULL); + shellLog("Selected: %ldx%ld %ldbpp (pitch %ld)", (long)sCtx.display.width, (long)sCtx.display.height, (long)sCtx.display.format.bitsPerPixel, (long)sCtx.display.pitch); + // Initialize task system if (tsInit() != TS_OK) { shellLog("Failed to initialize task system"); @@ -259,11 +271,10 @@ int main(void) { tsYield(); } - // Reap terminated apps - shellReapApps(&sCtx); - - // Notify desktop of any state changes - desktopUpdate(); + // Reap terminated apps and notify desktop if anything changed + if (shellReapApps(&sCtx)) { + desktopUpdate(); + } } shellLog("DVX Shell shutting down...");