ProgMan is now it's own app. About app moved into ProgMan. Minor API improvements.
This commit is contained in:
parent
76030270f9
commit
78302c26d2
31 changed files with 716 additions and 164 deletions
|
|
@ -10,17 +10,17 @@ OBJDIR = ../obj/apps
|
|||
BINDIR = ../bin/apps
|
||||
|
||||
# App definitions: each is a subdir with a single .c file
|
||||
APPS = about notepad clock
|
||||
APPS = progman notepad clock
|
||||
|
||||
.PHONY: all clean $(APPS)
|
||||
|
||||
all: $(APPS)
|
||||
|
||||
about: $(BINDIR)/about.app
|
||||
progman: $(BINDIR)/progman.app
|
||||
notepad: $(BINDIR)/notepad.app
|
||||
clock: $(BINDIR)/clock.app
|
||||
|
||||
$(BINDIR)/about.app: $(OBJDIR)/about.o | $(BINDIR)
|
||||
$(BINDIR)/progman.app: $(OBJDIR)/progman.o | $(BINDIR)
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
|
||||
$(BINDIR)/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)
|
||||
|
|
@ -29,7 +29,7 @@ $(BINDIR)/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)
|
|||
$(BINDIR)/clock.app: $(OBJDIR)/clock.o | $(BINDIR)
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
|
||||
|
||||
$(OBJDIR)/about.o: about/about.c | $(OBJDIR)
|
||||
$(OBJDIR)/progman.o: progman/progman.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/notepad.o: notepad/notepad.c | $(OBJDIR)
|
||||
|
|
@ -45,7 +45,7 @@ $(BINDIR):
|
|||
mkdir -p $(BINDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/about.o: about/about.c ../dvx/dvxApp.h ../dvx/dvxWidget.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)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
|
||||
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
// about.c — "About DVX Shell" sample DXE application (callback-only)
|
||||
//
|
||||
// Demonstrates a simple callback-only app: creates a window with widgets,
|
||||
// registers callbacks, and returns. The shell event loop handles the rest.
|
||||
|
||||
#include "dvxApp.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "shellApp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
int32_t appMain(DxeAppContextT *ctx);
|
||||
static void onClose(WindowT *win);
|
||||
static void onOkClick(WidgetT *w);
|
||||
|
||||
// ============================================================
|
||||
// App state
|
||||
// ============================================================
|
||||
|
||||
static DxeAppContextT *sCtx = NULL;
|
||||
static WindowT *sWin = NULL;
|
||||
|
||||
// ============================================================
|
||||
// App descriptor (required DXE export)
|
||||
// ============================================================
|
||||
|
||||
AppDescriptorT appDescriptor = {
|
||||
.name = "About",
|
||||
.hasMainLoop = false,
|
||||
.stackSize = 0,
|
||||
.priority = TS_PRIORITY_NORMAL
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Callbacks
|
||||
// ============================================================
|
||||
|
||||
static void onClose(WindowT *win) {
|
||||
dvxDestroyWindow(sCtx->shellCtx, win);
|
||||
sWin = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void onOkClick(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
if (sWin) {
|
||||
dvxDestroyWindow(sCtx->shellCtx, sWin);
|
||||
sWin = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Entry point
|
||||
// ============================================================
|
||||
|
||||
int32_t appMain(DxeAppContextT *ctx) {
|
||||
sCtx = ctx;
|
||||
|
||||
AppContextT *ac = ctx->shellCtx;
|
||||
int32_t screenW = ac->display.width;
|
||||
int32_t screenH = ac->display.height;
|
||||
int32_t winW = 280;
|
||||
int32_t winH = 200;
|
||||
int32_t winX = (screenW - winW) / 2;
|
||||
int32_t winY = (screenH - winH) / 3;
|
||||
|
||||
sWin = dvxCreateWindow(ac, "About DVX Shell", winX, winY, winW, winH, false);
|
||||
|
||||
if (!sWin) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sWin->onClose = onClose;
|
||||
|
||||
WidgetT *root = wgtInitWindow(ac, sWin);
|
||||
|
||||
wgtLabel(root, "DVX Shell 1.0");
|
||||
wgtHSeparator(root);
|
||||
wgtLabel(root, "A DOS Visual eXecutive desktop shell for DJGPP/DPMI.");
|
||||
wgtLabel(root, "Using DXE3 dynamic loading for application modules.");
|
||||
wgtSpacer(root);
|
||||
|
||||
WidgetT *btnRow = wgtHBox(root);
|
||||
btnRow->align = AlignCenterE;
|
||||
|
||||
WidgetT *okBtn = wgtButton(btnRow, "OK");
|
||||
okBtn->onClick = onOkClick;
|
||||
okBtn->prefW = wgtPixels(80);
|
||||
|
||||
dvxFitWindow(ac, sWin);
|
||||
wgtInvalidate(root);
|
||||
return 0;
|
||||
}
|
||||
534
apps/progman/progman.c
Normal file
534
apps/progman/progman.c
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
// progman.c — Program Manager application for DVX Shell
|
||||
//
|
||||
// Displays a grid of available apps from the apps/ directory.
|
||||
// Double-click or Enter launches an app. Includes Task Manager (Ctrl+Esc).
|
||||
// This is a callback-only DXE app: creates windows, registers callbacks,
|
||||
// and returns.
|
||||
|
||||
#include "dvxApp.h"
|
||||
#include "dvxDialog.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "shellApp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
// ============================================================
|
||||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define MAX_APP_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
|
||||
} AppEntryT;
|
||||
|
||||
static DxeAppContextT *sCtx = NULL;
|
||||
static AppContextT *sAc = NULL;
|
||||
static int32_t sMyAppId = 0;
|
||||
static WindowT *sPmWindow = NULL;
|
||||
static WidgetT *sStatusLabel = NULL;
|
||||
static AppEntryT sAppFiles[MAX_APP_FILES];
|
||||
static int32_t sAppCount = 0;
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
int32_t appMain(DxeAppContextT *ctx);
|
||||
static void buildPmWindow(void);
|
||||
static void desktopUpdate(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 showAboutDialog(void);
|
||||
static void updateStatusText(void);
|
||||
|
||||
// Task Manager
|
||||
static WindowT *sTmWindow = NULL;
|
||||
static WidgetT *sTmListBox = NULL;
|
||||
static void buildTaskManager(void);
|
||||
static void onTmClose(WindowT *win);
|
||||
static void onTmEndTask(WidgetT *w);
|
||||
static void onTmSwitchTo(WidgetT *w);
|
||||
static void refreshTaskList(void);
|
||||
|
||||
// ============================================================
|
||||
// App descriptor
|
||||
// ============================================================
|
||||
|
||||
AppDescriptorT appDescriptor = {
|
||||
.name = "Program Manager",
|
||||
.hasMainLoop = false,
|
||||
.stackSize = 0,
|
||||
.priority = TS_PRIORITY_NORMAL
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Static functions (alphabetical)
|
||||
// ============================================================
|
||||
|
||||
static void buildPmWindow(void) {
|
||||
int32_t screenW = sAc->display.width;
|
||||
int32_t screenH = sAc->display.height;
|
||||
int32_t winW = 440;
|
||||
int32_t winH = 340;
|
||||
int32_t winX = (screenW - winW) / 2;
|
||||
int32_t winY = (screenH - winH) / 4;
|
||||
|
||||
sPmWindow = dvxCreateWindow(sAc, "Program Manager", winX, winY, winW, winH, true);
|
||||
|
||||
if (!sPmWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(sAc, sPmWindow);
|
||||
|
||||
// App button grid in a frame
|
||||
WidgetT *appFrame = wgtFrame(root, "Applications");
|
||||
appFrame->weight = 100;
|
||||
|
||||
if (sAppCount == 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 < sAppCount; i++) {
|
||||
if (i % PM_GRID_COLS == 0) {
|
||||
hbox = wgtHBox(appFrame);
|
||||
hbox->spacing = wgtPixels(8);
|
||||
row++;
|
||||
}
|
||||
|
||||
WidgetT *btn = wgtButton(hbox, sAppFiles[i].name);
|
||||
btn->prefW = wgtPixels(PM_BTN_W);
|
||||
btn->prefH = wgtPixels(PM_BTN_H);
|
||||
btn->userData = &sAppFiles[i];
|
||||
btn->onDblClick = onAppButtonClick;
|
||||
btn->onClick = onAppButtonClick;
|
||||
}
|
||||
|
||||
(void)row;
|
||||
}
|
||||
|
||||
// Status bar
|
||||
WidgetT *statusBar = wgtStatusBar(root);
|
||||
sStatusLabel = wgtLabel(statusBar, "");
|
||||
sStatusLabel->weight = 100;
|
||||
updateStatusText();
|
||||
|
||||
dvxFitWindow(sAc, sPmWindow);
|
||||
}
|
||||
|
||||
|
||||
static void buildTaskManager(void) {
|
||||
if (sTmWindow) {
|
||||
// Already open — just raise it
|
||||
for (int32_t i = 0; i < sAc->stack.count; i++) {
|
||||
if (sAc->stack.windows[i] == sTmWindow) {
|
||||
wmRaiseWindow(&sAc->stack, &sAc->dirty, i);
|
||||
wmSetFocus(&sAc->stack, &sAc->dirty, sAc->stack.count - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t screenW = sAc->display.width;
|
||||
int32_t screenH = sAc->display.height;
|
||||
int32_t winW = 300;
|
||||
int32_t winH = 280;
|
||||
int32_t winX = (screenW - winW) / 2;
|
||||
int32_t winY = (screenH - winH) / 3;
|
||||
|
||||
sTmWindow = dvxCreateWindow(sAc, "Task Manager", winX, winY, winW, winH, true);
|
||||
|
||||
if (!sTmWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
sTmWindow->onClose = onTmClose;
|
||||
|
||||
WidgetT *root = wgtInitWindow(sAc, sTmWindow);
|
||||
|
||||
// List box of running apps
|
||||
sTmListBox = wgtListBox(root);
|
||||
sTmListBox->weight = 100;
|
||||
sTmListBox->prefH = wgtPixels(160);
|
||||
|
||||
// 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(sAc, sTmWindow);
|
||||
}
|
||||
|
||||
|
||||
static void desktopUpdate(void) {
|
||||
updateStatusText();
|
||||
|
||||
if (sTmWindow) {
|
||||
refreshTaskList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onAppButtonClick(WidgetT *w) {
|
||||
AppEntryT *entry = (AppEntryT *)w->userData;
|
||||
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
shellLoadApp(sAc, entry->path);
|
||||
updateStatusText();
|
||||
}
|
||||
|
||||
|
||||
static void onPmClose(WindowT *win) {
|
||||
(void)win;
|
||||
// Confirm exit
|
||||
int32_t result = dvxMessageBox(sAc, "Exit Shell", "Are you sure you want to exit DVX Shell?", MB_YESNO | MB_ICONQUESTION);
|
||||
|
||||
if (result == ID_YES) {
|
||||
dvxQuit(sAc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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(sAc, "Run Application", FD_OPEN, "apps", filters, 2, path, sizeof(path))) {
|
||||
shellLoadApp(sAc, path);
|
||||
updateStatusText();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_EXIT:
|
||||
onPmClose(sPmWindow);
|
||||
break;
|
||||
|
||||
case CMD_CASCADE:
|
||||
dvxCascadeWindows(sAc);
|
||||
break;
|
||||
|
||||
case CMD_TILE:
|
||||
dvxTileWindows(sAc);
|
||||
break;
|
||||
|
||||
case CMD_TILE_H:
|
||||
dvxTileWindowsH(sAc);
|
||||
break;
|
||||
|
||||
case CMD_TILE_V:
|
||||
dvxTileWindowsV(sAc);
|
||||
break;
|
||||
|
||||
case CMD_ABOUT:
|
||||
showAboutDialog();
|
||||
break;
|
||||
|
||||
case CMD_TASK_MGR:
|
||||
buildTaskManager();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onTmClose(WindowT *win) {
|
||||
sTmListBox = NULL;
|
||||
sTmWindow = NULL;
|
||||
dvxDestroyWindow(sAc, win);
|
||||
}
|
||||
|
||||
|
||||
static void onTmEndTask(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
if (!sTmListBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t sel = wgtListBoxGetSelected(sTmListBox);
|
||||
|
||||
if (sel < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map list index to app ID (skip our own appId)
|
||||
int32_t idx = 0;
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
if (i == sMyAppId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ShellAppT *app = shellGetApp(i);
|
||||
|
||||
if (app && app->state == AppStateRunningE) {
|
||||
if (idx == sel) {
|
||||
shellForceKillApp(sAc, app);
|
||||
refreshTaskList();
|
||||
updateStatusText();
|
||||
return;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onTmSwitchTo(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
if (!sTmListBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t sel = wgtListBoxGetSelected(sTmListBox);
|
||||
|
||||
if (sel < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Map list index to app ID (skip our own appId), find topmost window
|
||||
int32_t idx = 0;
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
if (i == sMyAppId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ShellAppT *app = shellGetApp(i);
|
||||
|
||||
if (app && app->state == AppStateRunningE) {
|
||||
if (idx == sel) {
|
||||
// Find the topmost window for this app
|
||||
for (int32_t j = sAc->stack.count - 1; j >= 0; j--) {
|
||||
WindowT *win = sAc->stack.windows[j];
|
||||
|
||||
if (win->appId == i) {
|
||||
if (win->minimized) {
|
||||
wmRestoreMinimized(&sAc->stack, &sAc->dirty, win);
|
||||
}
|
||||
|
||||
wmRaiseWindow(&sAc->stack, &sAc->dirty, j);
|
||||
wmSetFocus(&sAc->stack, &sAc->dirty, sAc->stack.count - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void refreshTaskList(void) {
|
||||
if (!sTmListBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char *items[SHELL_MAX_APPS];
|
||||
static char names[SHELL_MAX_APPS][SHELL_APP_NAME_MAX + 16];
|
||||
int32_t count = 0;
|
||||
|
||||
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
|
||||
if (i == sMyAppId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
wgtListBoxSetItems(sTmListBox, items, count);
|
||||
}
|
||||
|
||||
|
||||
static void scanAppsDir(void) {
|
||||
DIR *dir = opendir("apps");
|
||||
|
||||
if (!dir) {
|
||||
shellLog("Progman: apps/ directory not found");
|
||||
return;
|
||||
}
|
||||
|
||||
sAppCount = 0;
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL && sAppCount < MAX_APP_FILES) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Skip ourselves
|
||||
if (strcmp(ent->d_name, "progman.app") == 0 || strcmp(ent->d_name, "PROGMAN.APP") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AppEntryT *entry = &sAppFiles[sAppCount];
|
||||
|
||||
// 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), "apps/%s", ent->d_name);
|
||||
sAppCount++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
shellLog("Progman: found %ld app(s)", (long)sAppCount);
|
||||
}
|
||||
|
||||
|
||||
static void showAboutDialog(void) {
|
||||
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.",
|
||||
MB_OK | MB_ICONINFO);
|
||||
}
|
||||
|
||||
|
||||
static void updateStatusText(void) {
|
||||
if (!sStatusLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
static char buf[64];
|
||||
|
||||
// Subtract 1 to exclude ourselves from the count
|
||||
int32_t count = shellRunningAppCount() - 1;
|
||||
|
||||
if (count < 0) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Entry point
|
||||
// ============================================================
|
||||
|
||||
int32_t appMain(DxeAppContextT *ctx) {
|
||||
sCtx = ctx;
|
||||
sAc = ctx->shellCtx;
|
||||
sMyAppId = ctx->appId;
|
||||
|
||||
scanAppsDir();
|
||||
buildPmWindow();
|
||||
|
||||
// Register for state change notifications from the shell
|
||||
shellRegisterDesktopUpdate(desktopUpdate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
16
dvx/dvxApp.c
16
dvx/dvxApp.c
|
|
@ -528,7 +528,6 @@ static bool dispatchAccelKey(AppContextT *ctx, char key) {
|
|||
for (WidgetT *c = target->parent->firstChild; c; c = c->nextSibling) {
|
||||
if (c == target) {
|
||||
wgtTabControlSetActive(target->parent, tabIdx);
|
||||
wgtInvalidate(win->widgetRoot);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1085,6 +1084,18 @@ WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxCreateWindowCentered
|
||||
// ============================================================
|
||||
|
||||
WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
|
||||
int32_t x = (ctx->display.width - w) / 2;
|
||||
int32_t y = (ctx->display.height - h) / 2;
|
||||
|
||||
return dvxCreateWindow(ctx, title, x, y, w, h, resizable);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// dvxAddAccel
|
||||
// ============================================================
|
||||
|
|
@ -1235,6 +1246,9 @@ void dvxFitWindow(AppContextT *ctx, WindowT *win) {
|
|||
|
||||
// Dirty new position
|
||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
||||
|
||||
// Invalidate widget tree so it repaints at the new size
|
||||
wgtInvalidate(win->widgetRoot);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ bool dvxUpdate(AppContextT *ctx);
|
|||
// Create a window
|
||||
WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
|
||||
|
||||
// Create a centered window (position computed from screen size)
|
||||
WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
|
||||
|
||||
// Destroy a window
|
||||
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);
|
||||
|
||||
|
|
|
|||
|
|
@ -704,7 +704,6 @@ static void fdLoadDir(void) {
|
|||
|
||||
// Update path display
|
||||
wgtSetText(sFd.pathInput, sFd.curDir);
|
||||
wgtInvalidate(sFd.fileList);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -817,7 +816,6 @@ static void fdOnListClick(WidgetT *w) {
|
|||
|
||||
if (!sFd.entryIsDir[sel]) {
|
||||
wgtSetText(sFd.nameInput, sFd.entryNames[sel]);
|
||||
wgtInvalidatePaint(sFd.nameInput);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -891,7 +889,6 @@ static void fdOnOk(WidgetT *w) {
|
|||
|
||||
fdNavigate(dirName);
|
||||
wgtSetText(sFd.nameInput, "");
|
||||
wgtInvalidatePaint(sFd.nameInput);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -910,7 +907,6 @@ static void fdOnOk(WidgetT *w) {
|
|||
if (stat(testPath, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
fdNavigate(testPath);
|
||||
wgtSetText(sFd.nameInput, "");
|
||||
wgtInvalidatePaint(sFd.nameInput);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -726,6 +726,9 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
|
|||
// Operations
|
||||
// ============================================================
|
||||
|
||||
// Get the AppContextT from any widget (walks to root)
|
||||
struct AppContextT *wgtGetContext(const WidgetT *w);
|
||||
|
||||
// Mark a widget (and ancestors) for relayout and repaint
|
||||
void wgtInvalidate(WidgetT *w);
|
||||
|
||||
|
|
|
|||
|
|
@ -1377,7 +1377,7 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)win->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(win->widgetRoot);
|
||||
if (!ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
if (w->as.comboBox.selectedIdx >= count) {
|
||||
w->as.comboBox.selectedIdx = -1;
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -94,6 +96,8 @@ void wgtComboBoxSetSelected(WidgetT *w, int32_t idx) {
|
|||
w->as.comboBox.selStart = -1;
|
||||
w->as.comboBox.selEnd = -1;
|
||||
}
|
||||
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ void wgtDropdownSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
if (w->as.dropdown.selectedIdx >= count) {
|
||||
w->as.dropdown.selectedIdx = -1;
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -67,6 +69,7 @@ void wgtDropdownSetSelected(WidgetT *w, int32_t idx) {
|
|||
VALIDATE_WIDGET_VOID(w, WidgetDropdownE);
|
||||
|
||||
w->as.dropdown.selectedIdx = idx;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -579,7 +579,7 @@ void widgetOnScroll(WindowT *win, ScrollbarOrientE orient, int32_t value) {
|
|||
|
||||
// Dirty the window content area on screen so compositor redraws it
|
||||
if (win->widgetRoot) {
|
||||
AppContextT *ctx = (AppContextT *)win->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(win->widgetRoot);
|
||||
|
||||
if (ctx) {
|
||||
dvxInvalidateWindow(ctx, win);
|
||||
|
|
|
|||
|
|
@ -39,13 +39,7 @@ WidgetT *wgtImageFromFile(WidgetT *parent, const char *path) {
|
|||
}
|
||||
|
||||
// Find the AppContextT to get display format
|
||||
WidgetT *root = parent;
|
||||
|
||||
while (root->parent) {
|
||||
root = root->parent;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)root->userData;
|
||||
AppContextT *ctx = wgtGetContext(parent);
|
||||
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
|
|
@ -107,6 +101,7 @@ void wgtImageSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH, int3
|
|||
w->as.image.imgW = imgW;
|
||||
w->as.image.imgH = imgH;
|
||||
w->as.image.imgPitch = pitch;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ void wgtImageButtonSetData(WidgetT *w, uint8_t *data, int32_t imgW, int32_t imgH
|
|||
w->as.imageButton.imgW = imgW;
|
||||
w->as.imageButton.imgH = imgH;
|
||||
w->as.imageButton.imgPitch = pitch;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ static void ensureScrollVisible(WidgetT *w, int32_t idx) {
|
|||
return;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t innerH = w->h - LISTBOX_BORDER * 2;
|
||||
int32_t visibleRows = innerH / font->charHeight;
|
||||
|
|
@ -128,6 +128,7 @@ void wgtListBoxClearSelection(WidgetT *w) {
|
|||
}
|
||||
|
||||
memset(w->as.listBox.selBits, 0, w->as.listBox.itemCount);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -171,6 +172,7 @@ void wgtListBoxSelectAll(WidgetT *w) {
|
|||
}
|
||||
|
||||
memset(w->as.listBox.selBits, 1, w->as.listBox.itemCount);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -186,6 +188,7 @@ void wgtListBoxSetItemSelected(WidgetT *w, int32_t idx, bool selected) {
|
|||
}
|
||||
|
||||
w->as.listBox.selBits[idx] = selected ? 1 : 0;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -229,6 +232,8 @@ void wgtListBoxSetItems(WidgetT *w, const char **items, int32_t count) {
|
|||
if (w->as.listBox.selBits && w->as.listBox.selectedIdx >= 0) {
|
||||
w->as.listBox.selBits[w->as.listBox.selectedIdx] = 1;
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -278,6 +283,8 @@ void wgtListBoxSetSelected(WidgetT *w, int32_t idx) {
|
|||
w->as.listBox.selBits[idx] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -315,7 +322,6 @@ void widgetListBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
// Ctrl+A — select all (multi-select only)
|
||||
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
||||
wgtListBoxSelectAll(w);
|
||||
wgtInvalidatePaint(w);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +340,7 @@ void widgetListBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
return;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t visibleRows = (w->h - LISTBOX_BORDER * 2) / font->charHeight;
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ bool widgetListViewColBorderHit(const WidgetT *w, int32_t vx, int32_t vy) {
|
|||
return false;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t headerH = font->charHeight + 4;
|
||||
int32_t headerTop = w->y + LISTVIEW_BORDER;
|
||||
|
|
@ -273,6 +273,7 @@ void wgtListViewClearSelection(WidgetT *w) {
|
|||
}
|
||||
|
||||
memset(w->as.listView->selBits, 0, w->as.listView->rowCount);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -305,6 +306,7 @@ void wgtListViewSelectAll(WidgetT *w) {
|
|||
}
|
||||
|
||||
memset(w->as.listView->selBits, 1, w->as.listView->rowCount);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -322,6 +324,7 @@ void wgtListViewSetColumns(WidgetT *w, const ListViewColT *cols, int32_t count)
|
|||
w->as.listView->cols = cols;
|
||||
w->as.listView->colCount = count;
|
||||
w->as.listView->totalColW = 0;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -361,6 +364,8 @@ void wgtListViewSetData(WidgetT *w, const char **cellData, int32_t rowCount) {
|
|||
if (w->as.listView->selBits && w->as.listView->selectedIdx >= 0) {
|
||||
w->as.listView->selBits[w->as.listView->selectedIdx] = 1;
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -387,6 +392,7 @@ void wgtListViewSetItemSelected(WidgetT *w, int32_t idx, bool selected) {
|
|||
}
|
||||
|
||||
w->as.listView->selBits[idx] = selected ? 1 : 0;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -445,6 +451,8 @@ void wgtListViewSetSelected(WidgetT *w, int32_t idx) {
|
|||
w->as.listView->selBits[idx] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -458,6 +466,7 @@ void wgtListViewSetSort(WidgetT *w, int32_t col, ListViewSortE dir) {
|
|||
w->as.listView->sortCol = col;
|
||||
w->as.listView->sortDir = dir;
|
||||
listViewBuildSortIndex(w);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -492,7 +501,6 @@ void widgetListViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
// Ctrl+A — select all (multi-select only)
|
||||
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
||||
wgtListViewSelectAll(w);
|
||||
wgtInvalidatePaint(w);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -530,7 +538,7 @@ void widgetListViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
}
|
||||
|
||||
// Compute visible rows for page up/down
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t headerH = font->charHeight + 4;
|
||||
int32_t innerH = w->h - LISTVIEW_BORDER * 2 - headerH;
|
||||
|
|
|
|||
|
|
@ -216,6 +216,25 @@ void wgtSetName(WidgetT *w, const char *name) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// wgtGetContext
|
||||
// ============================================================
|
||||
|
||||
AppContextT *wgtGetContext(const WidgetT *w) {
|
||||
if (!w) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const WidgetT *root = w;
|
||||
|
||||
while (root->parent) {
|
||||
root = root->parent;
|
||||
}
|
||||
|
||||
return (AppContextT *)root->userData;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// wgtGetText
|
||||
// ============================================================
|
||||
|
|
@ -382,6 +401,8 @@ void wgtSetText(WidgetT *w, const char *text) {
|
|||
if (w->wclass && w->wclass->setText) {
|
||||
w->wclass->setText(w, text);
|
||||
}
|
||||
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -392,5 +413,6 @@ void wgtSetText(WidgetT *w, const char *text) {
|
|||
void wgtSetVisible(WidgetT *w, bool visible) {
|
||||
if (w) {
|
||||
w->visible = visible;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ void wgtProgressBarSetValue(WidgetT *w, int32_t value) {
|
|||
}
|
||||
|
||||
w->as.progressBar.value = value;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ void widgetScrollPaneLayout(WidgetT *w, const BitmapFontT *font) {
|
|||
void widgetScrollPaneOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||
(void)mod;
|
||||
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
|
||||
int32_t contentMinW;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ void wgtSliderSetValue(WidgetT *w, int32_t value) {
|
|||
}
|
||||
|
||||
w->as.slider.value = value;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -474,6 +474,7 @@ void wgtSpinnerSetRange(WidgetT *w, int32_t minVal, int32_t maxVal) {
|
|||
w->as.spinner.minValue = minVal;
|
||||
w->as.spinner.maxValue = maxVal;
|
||||
spinnerClampAndFormat(w);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -497,4 +498,5 @@ void wgtSpinnerSetValue(WidgetT *w, int32_t value) {
|
|||
|
||||
w->as.spinner.value = value;
|
||||
spinnerClampAndFormat(w);
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,4 +332,5 @@ int32_t wgtSplitterGetPos(const WidgetT *w) {
|
|||
|
||||
void wgtSplitterSetPos(WidgetT *w, int32_t pos) {
|
||||
w->as.splitter.dividerPos = pos;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ void wgtTabControlSetActive(WidgetT *w, int32_t idx) {
|
|||
VALIDATE_WIDGET_VOID(w, WidgetTabControlE);
|
||||
|
||||
w->as.tabControl.activeTab = idx;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ void clearOtherSelections(WidgetT *except) {
|
|||
return;
|
||||
}
|
||||
|
||||
AppContextT *ctx = (AppContextT *)except->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(except);
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
|
|
@ -753,7 +753,7 @@ static void maskedInputOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
done:
|
||||
// Adjust scroll
|
||||
{
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t visibleChars = (w->w - TEXT_INPUT_PAD * 2) / font->charWidth;
|
||||
|
||||
|
|
@ -962,7 +962,7 @@ void widgetTextAreaOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
int32_t *pSC = &w->as.textArea.selCursor;
|
||||
bool shift = (mod & KEY_MOD_SHIFT) != 0;
|
||||
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t innerW = w->w - TEXTAREA_BORDER * 2 - TEXTAREA_PAD * 2 - TEXTAREA_SB_W;
|
||||
int32_t visCols = innerW / font->charWidth;
|
||||
|
|
@ -2692,7 +2692,7 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
|||
adjustScroll:
|
||||
// Adjust scroll offset to keep cursor visible
|
||||
{
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t fieldW = w->w;
|
||||
|
||||
|
|
|
|||
|
|
@ -571,6 +571,7 @@ void wgtTreeItemSetExpanded(WidgetT *w, bool expanded) {
|
|||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||
|
||||
w->as.treeItem.expanded = expanded;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -614,6 +615,8 @@ void wgtTreeViewSetSelected(WidgetT *w, WidgetT *item) {
|
|||
item->as.treeItem.selected = true;
|
||||
w->as.treeView.anchorItem = item;
|
||||
}
|
||||
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -670,6 +673,7 @@ void wgtTreeItemSetSelected(WidgetT *w, bool selected) {
|
|||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||
|
||||
w->as.treeItem.selected = selected;
|
||||
wgtInvalidatePaint(w);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -814,7 +818,7 @@ void widgetTreeViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|||
sel = w->as.treeView.selectedItem;
|
||||
|
||||
if (sel) {
|
||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
||||
AppContextT *ctx = wgtGetContext(w);
|
||||
const BitmapFontT *font = &ctx->font;
|
||||
int32_t itemY = treeItemYPos(w, sel, font);
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,6 @@ static void onOkClick(WidgetT *w) {
|
|||
|
||||
if (status) {
|
||||
wgtSetText(status, "Button clicked!");
|
||||
wgtInvalidate(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +462,6 @@ static void onToolbarClick(WidgetT *w) {
|
|||
|
||||
if (status) {
|
||||
wgtSetText(status, wgtGetText(w));
|
||||
wgtInvalidate(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1039,7 +1037,6 @@ static void setupTerminalWindow(AppContextT *ctx) {
|
|||
wgtLabel(sb, "80x25 [Local]");
|
||||
|
||||
dvxFitWindow(ctx, win);
|
||||
wgtInvalidate(root);
|
||||
dvxMinimizeWindow(ctx, win);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ OBJDIR = ../obj/dvxshell
|
|||
BINDIR = ../bin
|
||||
LIBDIR = ../lib
|
||||
|
||||
SRCS = shellMain.c shellApp.c shellExport.c shellDesktop.c
|
||||
SRCS = shellMain.c shellApp.c shellExport.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(BINDIR)/dvxshell.exe
|
||||
|
||||
|
|
@ -43,7 +43,5 @@ $(BINDIR):
|
|||
$(OBJDIR)/shellMain.o: shellMain.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)/shellDesktop.o: shellDesktop.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(TARGET)
|
||||
|
|
|
|||
|
|
@ -106,20 +106,14 @@ void shellLog(const char *fmt, ...);
|
|||
void shellExportInit(void);
|
||||
|
||||
// ============================================================
|
||||
// Program Manager / Desktop
|
||||
// Desktop callback
|
||||
// ============================================================
|
||||
|
||||
// Initialize the Program Manager window
|
||||
void shellDesktopInit(AppContextT *ctx);
|
||||
// Default desktop app path
|
||||
#define SHELL_DESKTOP_APP "apps/progman.app"
|
||||
|
||||
// Update status bar with running app count
|
||||
void shellDesktopUpdateStatus(void);
|
||||
|
||||
// ============================================================
|
||||
// Task Manager
|
||||
// ============================================================
|
||||
|
||||
// Open the Task Manager dialog (Ctrl+Esc)
|
||||
void shellTaskManager(AppContextT *ctx);
|
||||
// Register a callback for app state changes (load, reap, crash).
|
||||
// The desktop app calls this during appMain to receive notifications.
|
||||
void shellRegisterDesktopUpdate(void (*updateFn)(void));
|
||||
|
||||
#endif // SHELL_APP_H
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ static void buildPmWindow(void) {
|
|||
updateStatusText();
|
||||
|
||||
dvxFitWindow(sCtx, sPmWindow);
|
||||
wgtInvalidate(root);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -202,7 +201,6 @@ static void buildTaskManager(void) {
|
|||
|
||||
refreshTaskList();
|
||||
dvxFitWindow(sCtx, sTmWindow);
|
||||
wgtInvalidate(root);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -385,7 +383,6 @@ static void refreshTaskList(void) {
|
|||
}
|
||||
|
||||
wgtListBoxSetItems(sTmListBox, items, count);
|
||||
wgtInvalidatePaint(sTmListBox);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -464,7 +461,6 @@ static void updateStatusText(void) {
|
|||
}
|
||||
|
||||
wgtSetText(sStatusLabel, buf);
|
||||
wgtInvalidate(sStatusLabel);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "taskswitch.h"
|
||||
|
||||
#include <sys/dxe.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
|
||||
static void shellRegisterExports(void);
|
||||
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
|
||||
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win);
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -42,6 +44,21 @@ static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxCreateWindowCentered — stamps win->appId
|
||||
// ============================================================
|
||||
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
|
||||
WindowT *win = dvxCreateWindowCentered(ctx, title, w, h, resizable);
|
||||
|
||||
if (win) {
|
||||
win->appId = sCurrentAppId;
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxDestroyWindow — checks for last-window reap
|
||||
// ============================================================
|
||||
|
|
@ -90,6 +107,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
|||
DXE_EXPORT(dvxInit)
|
||||
DXE_EXPORT(dvxShutdown)
|
||||
DXE_EXPORT(dvxUpdate)
|
||||
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
|
||||
DXE_EXPORT(dvxFitWindow)
|
||||
DXE_EXPORT(dvxInvalidateRect)
|
||||
DXE_EXPORT(dvxInvalidateWindow)
|
||||
|
|
@ -287,6 +305,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
|||
DXE_EXPORT(wgtGetText)
|
||||
DXE_EXPORT(wgtSetEnabled)
|
||||
DXE_EXPORT(wgtSetVisible)
|
||||
DXE_EXPORT(wgtGetContext)
|
||||
DXE_EXPORT(wgtSetName)
|
||||
DXE_EXPORT(wgtFind)
|
||||
DXE_EXPORT(wgtDestroy)
|
||||
|
|
@ -312,8 +331,18 @@ DXE_EXPORT_TABLE(shellExportTable)
|
|||
DXE_EXPORT(tsCurrentId)
|
||||
DXE_EXPORT(tsActiveCount)
|
||||
|
||||
// Shell logging
|
||||
// dvxWm.h — direct window management
|
||||
DXE_EXPORT(wmRaiseWindow)
|
||||
DXE_EXPORT(wmSetFocus)
|
||||
DXE_EXPORT(wmRestoreMinimized)
|
||||
|
||||
// Shell API
|
||||
DXE_EXPORT(shellLog)
|
||||
DXE_EXPORT(shellLoadApp)
|
||||
DXE_EXPORT(shellGetApp)
|
||||
DXE_EXPORT(shellForceKillApp)
|
||||
DXE_EXPORT(shellRunningAppCount)
|
||||
DXE_EXPORT(shellRegisterDesktopUpdate)
|
||||
|
||||
// libc — memory
|
||||
DXE_EXPORT(malloc)
|
||||
|
|
@ -364,6 +393,11 @@ DXE_EXPORT_TABLE(shellExportTable)
|
|||
DXE_EXPORT(time)
|
||||
DXE_EXPORT(localtime)
|
||||
|
||||
// libc — directory
|
||||
DXE_EXPORT(opendir)
|
||||
DXE_EXPORT(readdir)
|
||||
DXE_EXPORT(closedir)
|
||||
|
||||
// libc — misc
|
||||
DXE_EXPORT(qsort)
|
||||
DXE_EXPORT(rand)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// shellMain.c — DVX Shell entry point and main loop
|
||||
//
|
||||
// Initializes the GUI, task system, DXE export table, and Program Manager.
|
||||
// Runs the cooperative main loop, yielding to app tasks and reaping
|
||||
// terminated apps each frame.
|
||||
// Initializes the GUI, task system, DXE export table, and loads
|
||||
// the desktop app. Runs the cooperative main loop, yielding to
|
||||
// app tasks and reaping terminated apps each frame.
|
||||
|
||||
#include "shellApp.h"
|
||||
#include "dvxDialog.h"
|
||||
|
|
@ -20,14 +20,16 @@
|
|||
|
||||
static AppContextT sCtx;
|
||||
static jmp_buf sCrashJmp;
|
||||
static volatile int sCrashSignal = 0;
|
||||
static FILE *sLogFile = NULL;
|
||||
static volatile int sCrashSignal = 0;
|
||||
static FILE *sLogFile = NULL;
|
||||
static void (*sDesktopUpdateFn)(void) = NULL;
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static void crashHandler(int sig);
|
||||
static void desktopUpdate(void);
|
||||
static void idleYield(void *ctx);
|
||||
static void installCrashHandler(void);
|
||||
static void logCrash(int sig);
|
||||
|
|
@ -47,6 +49,17 @@ static void crashHandler(int sig) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// desktopUpdate — notify desktop app of state change
|
||||
// ============================================================
|
||||
|
||||
static void desktopUpdate(void) {
|
||||
if (sDesktopUpdateFn) {
|
||||
sDesktopUpdateFn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// idleYield — called when no dirty rects need compositing
|
||||
// ============================================================
|
||||
|
|
@ -136,6 +149,15 @@ void shellLog(const char *fmt, ...) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// shellRegisterDesktopUpdate
|
||||
// ============================================================
|
||||
|
||||
void shellRegisterDesktopUpdate(void (*updateFn)(void)) {
|
||||
sDesktopUpdateFn = updateFn;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// main
|
||||
// ============================================================
|
||||
|
|
@ -182,8 +204,20 @@ int main(void) {
|
|||
sCtx.idleCallback = idleYield;
|
||||
sCtx.idleCtx = &sCtx;
|
||||
|
||||
// Create the Program Manager window
|
||||
shellDesktopInit(&sCtx);
|
||||
// Load the desktop app
|
||||
int32_t desktopId = shellLoadApp(&sCtx, SHELL_DESKTOP_APP);
|
||||
|
||||
if (desktopId < 0) {
|
||||
shellLog("Failed to load desktop app '%s'", SHELL_DESKTOP_APP);
|
||||
tsShutdown();
|
||||
dvxShutdown(&sCtx);
|
||||
|
||||
if (sLogFile) {
|
||||
fclose(sLogFile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Install crash handler after everything is initialized
|
||||
installCrashHandler();
|
||||
|
|
@ -213,7 +247,7 @@ int main(void) {
|
|||
|
||||
sCurrentAppId = 0;
|
||||
sCrashSignal = 0;
|
||||
shellDesktopUpdateStatus();
|
||||
desktopUpdate();
|
||||
}
|
||||
|
||||
// Main loop
|
||||
|
|
@ -228,8 +262,8 @@ int main(void) {
|
|||
// Reap terminated apps
|
||||
shellReapApps(&sCtx);
|
||||
|
||||
// Update status display
|
||||
shellDesktopUpdateStatus();
|
||||
// Notify desktop of any state changes
|
||||
desktopUpdate();
|
||||
}
|
||||
|
||||
shellLog("DVX Shell shutting down...");
|
||||
|
|
|
|||
|
|
@ -265,7 +265,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// Fit window to widget tree
|
||||
dvxFitWindow(&ctx, win);
|
||||
wgtInvalidate(root);
|
||||
|
||||
// Poll serial during idle instead of yielding CPU
|
||||
ctx.idleCallback = idlePoll;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue