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
|
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 = about notepad clock
|
APPS = progman notepad clock
|
||||||
|
|
||||||
.PHONY: all clean $(APPS)
|
.PHONY: all clean $(APPS)
|
||||||
|
|
||||||
all: $(APPS)
|
all: $(APPS)
|
||||||
|
|
||||||
about: $(BINDIR)/about.app
|
progman: $(BINDIR)/progman.app
|
||||||
notepad: $(BINDIR)/notepad.app
|
notepad: $(BINDIR)/notepad.app
|
||||||
clock: $(BINDIR)/clock.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 $<
|
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||||
|
|
||||||
$(BINDIR)/notepad.app: $(OBJDIR)/notepad.o | $(BINDIR)
|
$(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)
|
$(BINDIR)/clock.app: $(OBJDIR)/clock.o | $(BINDIR)
|
||||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
|
$(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 $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OBJDIR)/notepad.o: notepad/notepad.c | $(OBJDIR)
|
$(OBJDIR)/notepad.o: notepad/notepad.c | $(OBJDIR)
|
||||||
|
|
@ -45,7 +45,7 @@ $(BINDIR):
|
||||||
mkdir -p $(BINDIR)
|
mkdir -p $(BINDIR)
|
||||||
|
|
||||||
# Dependencies
|
# 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)/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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
for (WidgetT *c = target->parent->firstChild; c; c = c->nextSibling) {
|
||||||
if (c == target) {
|
if (c == target) {
|
||||||
wgtTabControlSetActive(target->parent, tabIdx);
|
wgtTabControlSetActive(target->parent, tabIdx);
|
||||||
wgtInvalidate(win->widgetRoot);
|
|
||||||
break;
|
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
|
// dvxAddAccel
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -1235,6 +1246,9 @@ void dvxFitWindow(AppContextT *ctx, WindowT *win) {
|
||||||
|
|
||||||
// Dirty new position
|
// Dirty new position
|
||||||
dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h);
|
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
|
// Create a window
|
||||||
WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
|
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
|
// Destroy a window
|
||||||
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);
|
void dvxDestroyWindow(AppContextT *ctx, WindowT *win);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -704,7 +704,6 @@ static void fdLoadDir(void) {
|
||||||
|
|
||||||
// Update path display
|
// Update path display
|
||||||
wgtSetText(sFd.pathInput, sFd.curDir);
|
wgtSetText(sFd.pathInput, sFd.curDir);
|
||||||
wgtInvalidate(sFd.fileList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -817,7 +816,6 @@ static void fdOnListClick(WidgetT *w) {
|
||||||
|
|
||||||
if (!sFd.entryIsDir[sel]) {
|
if (!sFd.entryIsDir[sel]) {
|
||||||
wgtSetText(sFd.nameInput, sFd.entryNames[sel]);
|
wgtSetText(sFd.nameInput, sFd.entryNames[sel]);
|
||||||
wgtInvalidatePaint(sFd.nameInput);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,7 +889,6 @@ static void fdOnOk(WidgetT *w) {
|
||||||
|
|
||||||
fdNavigate(dirName);
|
fdNavigate(dirName);
|
||||||
wgtSetText(sFd.nameInput, "");
|
wgtSetText(sFd.nameInput, "");
|
||||||
wgtInvalidatePaint(sFd.nameInput);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -910,7 +907,6 @@ static void fdOnOk(WidgetT *w) {
|
||||||
if (stat(testPath, &st) == 0 && S_ISDIR(st.st_mode)) {
|
if (stat(testPath, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||||
fdNavigate(testPath);
|
fdNavigate(testPath);
|
||||||
wgtSetText(sFd.nameInput, "");
|
wgtSetText(sFd.nameInput, "");
|
||||||
wgtInvalidatePaint(sFd.nameInput);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -726,6 +726,9 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
|
||||||
// Operations
|
// 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
|
// Mark a widget (and ancestors) for relayout and repaint
|
||||||
void wgtInvalidate(WidgetT *w);
|
void wgtInvalidate(WidgetT *w);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1377,7 +1377,7 @@ int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)win->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(win->widgetRoot);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ void wgtComboBoxSetItems(WidgetT *w, const char **items, int32_t count) {
|
||||||
if (w->as.comboBox.selectedIdx >= count) {
|
if (w->as.comboBox.selectedIdx >= count) {
|
||||||
w->as.comboBox.selectedIdx = -1;
|
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.selStart = -1;
|
||||||
w->as.comboBox.selEnd = -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) {
|
if (w->as.dropdown.selectedIdx >= count) {
|
||||||
w->as.dropdown.selectedIdx = -1;
|
w->as.dropdown.selectedIdx = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -67,6 +69,7 @@ void wgtDropdownSetSelected(WidgetT *w, int32_t idx) {
|
||||||
VALIDATE_WIDGET_VOID(w, WidgetDropdownE);
|
VALIDATE_WIDGET_VOID(w, WidgetDropdownE);
|
||||||
|
|
||||||
w->as.dropdown.selectedIdx = idx;
|
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
|
// Dirty the window content area on screen so compositor redraws it
|
||||||
if (win->widgetRoot) {
|
if (win->widgetRoot) {
|
||||||
AppContextT *ctx = (AppContextT *)win->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(win->widgetRoot);
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
dvxInvalidateWindow(ctx, win);
|
dvxInvalidateWindow(ctx, win);
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,7 @@ WidgetT *wgtImageFromFile(WidgetT *parent, const char *path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the AppContextT to get display format
|
// Find the AppContextT to get display format
|
||||||
WidgetT *root = parent;
|
AppContextT *ctx = wgtGetContext(parent);
|
||||||
|
|
||||||
while (root->parent) {
|
|
||||||
root = root->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)root->userData;
|
|
||||||
|
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return NULL;
|
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.imgW = imgW;
|
||||||
w->as.image.imgH = imgH;
|
w->as.image.imgH = imgH;
|
||||||
w->as.image.imgPitch = pitch;
|
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.imgW = imgW;
|
||||||
w->as.imageButton.imgH = imgH;
|
w->as.imageButton.imgH = imgH;
|
||||||
w->as.imageButton.imgPitch = pitch;
|
w->as.imageButton.imgPitch = pitch;
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ static void ensureScrollVisible(WidgetT *w, int32_t idx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t innerH = w->h - LISTBOX_BORDER * 2;
|
int32_t innerH = w->h - LISTBOX_BORDER * 2;
|
||||||
int32_t visibleRows = innerH / font->charHeight;
|
int32_t visibleRows = innerH / font->charHeight;
|
||||||
|
|
@ -128,6 +128,7 @@ void wgtListBoxClearSelection(WidgetT *w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(w->as.listBox.selBits, 0, w->as.listBox.itemCount);
|
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);
|
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;
|
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) {
|
if (w->as.listBox.selBits && w->as.listBox.selectedIdx >= 0) {
|
||||||
w->as.listBox.selBits[w->as.listBox.selectedIdx] = 1;
|
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;
|
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)
|
// Ctrl+A — select all (multi-select only)
|
||||||
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
||||||
wgtListBoxSelectAll(w);
|
wgtListBoxSelectAll(w);
|
||||||
wgtInvalidatePaint(w);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,7 +340,7 @@ void widgetListBoxOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t visibleRows = (w->h - LISTBOX_BORDER * 2) / font->charHeight;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t headerH = font->charHeight + 4;
|
int32_t headerH = font->charHeight + 4;
|
||||||
int32_t headerTop = w->y + LISTVIEW_BORDER;
|
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);
|
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);
|
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->cols = cols;
|
||||||
w->as.listView->colCount = count;
|
w->as.listView->colCount = count;
|
||||||
w->as.listView->totalColW = 0;
|
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) {
|
if (w->as.listView->selBits && w->as.listView->selectedIdx >= 0) {
|
||||||
w->as.listView->selBits[w->as.listView->selectedIdx] = 1;
|
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;
|
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;
|
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->sortCol = col;
|
||||||
w->as.listView->sortDir = dir;
|
w->as.listView->sortDir = dir;
|
||||||
listViewBuildSortIndex(w);
|
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)
|
// Ctrl+A — select all (multi-select only)
|
||||||
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
if (multi && ctrl && (key == 'a' || key == 'A' || key == 1)) {
|
||||||
wgtListViewSelectAll(w);
|
wgtListViewSelectAll(w);
|
||||||
wgtInvalidatePaint(w);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -530,7 +538,7 @@ void widgetListViewOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute visible rows for page up/down
|
// Compute visible rows for page up/down
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t headerH = font->charHeight + 4;
|
int32_t headerH = font->charHeight + 4;
|
||||||
int32_t innerH = w->h - LISTVIEW_BORDER * 2 - headerH;
|
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
|
// wgtGetText
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -382,6 +401,8 @@ void wgtSetText(WidgetT *w, const char *text) {
|
||||||
if (w->wclass && w->wclass->setText) {
|
if (w->wclass && w->wclass->setText) {
|
||||||
w->wclass->setText(w, text);
|
w->wclass->setText(w, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -392,5 +413,6 @@ void wgtSetText(WidgetT *w, const char *text) {
|
||||||
void wgtSetVisible(WidgetT *w, bool visible) {
|
void wgtSetVisible(WidgetT *w, bool visible) {
|
||||||
if (w) {
|
if (w) {
|
||||||
w->visible = visible;
|
w->visible = visible;
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ void wgtProgressBarSetValue(WidgetT *w, int32_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w->as.progressBar.value = 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 widgetScrollPaneOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
(void)mod;
|
(void)mod;
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
|
|
||||||
int32_t contentMinW;
|
int32_t contentMinW;
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ void wgtSliderSetValue(WidgetT *w, int32_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w->as.slider.value = 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.minValue = minVal;
|
||||||
w->as.spinner.maxValue = maxVal;
|
w->as.spinner.maxValue = maxVal;
|
||||||
spinnerClampAndFormat(w);
|
spinnerClampAndFormat(w);
|
||||||
|
wgtInvalidatePaint(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -497,4 +498,5 @@ void wgtSpinnerSetValue(WidgetT *w, int32_t value) {
|
||||||
|
|
||||||
w->as.spinner.value = value;
|
w->as.spinner.value = value;
|
||||||
spinnerClampAndFormat(w);
|
spinnerClampAndFormat(w);
|
||||||
|
wgtInvalidatePaint(w);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,4 +332,5 @@ int32_t wgtSplitterGetPos(const WidgetT *w) {
|
||||||
|
|
||||||
void wgtSplitterSetPos(WidgetT *w, int32_t pos) {
|
void wgtSplitterSetPos(WidgetT *w, int32_t pos) {
|
||||||
w->as.splitter.dividerPos = pos;
|
w->as.splitter.dividerPos = pos;
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ void wgtTabControlSetActive(WidgetT *w, int32_t idx) {
|
||||||
VALIDATE_WIDGET_VOID(w, WidgetTabControlE);
|
VALIDATE_WIDGET_VOID(w, WidgetTabControlE);
|
||||||
|
|
||||||
w->as.tabControl.activeTab = idx;
|
w->as.tabControl.activeTab = idx;
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ void clearOtherSelections(WidgetT *except) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)except->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(except);
|
||||||
|
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -753,7 +753,7 @@ static void maskedInputOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
||||||
done:
|
done:
|
||||||
// Adjust scroll
|
// Adjust scroll
|
||||||
{
|
{
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t visibleChars = (w->w - TEXT_INPUT_PAD * 2) / font->charWidth;
|
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;
|
int32_t *pSC = &w->as.textArea.selCursor;
|
||||||
bool shift = (mod & KEY_MOD_SHIFT) != 0;
|
bool shift = (mod & KEY_MOD_SHIFT) != 0;
|
||||||
|
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t innerW = w->w - TEXTAREA_BORDER * 2 - TEXTAREA_PAD * 2 - TEXTAREA_SB_W;
|
int32_t innerW = w->w - TEXTAREA_BORDER * 2 - TEXTAREA_PAD * 2 - TEXTAREA_SB_W;
|
||||||
int32_t visCols = innerW / font->charWidth;
|
int32_t visCols = innerW / font->charWidth;
|
||||||
|
|
@ -2692,7 +2692,7 @@ void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, char *buf, int32_
|
||||||
adjustScroll:
|
adjustScroll:
|
||||||
// Adjust scroll offset to keep cursor visible
|
// Adjust scroll offset to keep cursor visible
|
||||||
{
|
{
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t fieldW = w->w;
|
int32_t fieldW = w->w;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,7 @@ void wgtTreeItemSetExpanded(WidgetT *w, bool expanded) {
|
||||||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||||
|
|
||||||
w->as.treeItem.expanded = expanded;
|
w->as.treeItem.expanded = expanded;
|
||||||
|
wgtInvalidate(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -614,6 +615,8 @@ void wgtTreeViewSetSelected(WidgetT *w, WidgetT *item) {
|
||||||
item->as.treeItem.selected = true;
|
item->as.treeItem.selected = true;
|
||||||
w->as.treeView.anchorItem = item;
|
w->as.treeView.anchorItem = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wgtInvalidatePaint(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -670,6 +673,7 @@ void wgtTreeItemSetSelected(WidgetT *w, bool selected) {
|
||||||
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
VALIDATE_WIDGET_VOID(w, WidgetTreeItemE);
|
||||||
|
|
||||||
w->as.treeItem.selected = selected;
|
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;
|
sel = w->as.treeView.selectedItem;
|
||||||
|
|
||||||
if (sel) {
|
if (sel) {
|
||||||
AppContextT *ctx = (AppContextT *)w->window->widgetRoot->userData;
|
AppContextT *ctx = wgtGetContext(w);
|
||||||
const BitmapFontT *font = &ctx->font;
|
const BitmapFontT *font = &ctx->font;
|
||||||
int32_t itemY = treeItemYPos(w, sel, font);
|
int32_t itemY = treeItemYPos(w, sel, font);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,6 @@ static void onOkClick(WidgetT *w) {
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
wgtSetText(status, "Button clicked!");
|
wgtSetText(status, "Button clicked!");
|
||||||
wgtInvalidate(status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,7 +462,6 @@ static void onToolbarClick(WidgetT *w) {
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
wgtSetText(status, wgtGetText(w));
|
wgtSetText(status, wgtGetText(w));
|
||||||
wgtInvalidate(status);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1039,7 +1037,6 @@ static void setupTerminalWindow(AppContextT *ctx) {
|
||||||
wgtLabel(sb, "80x25 [Local]");
|
wgtLabel(sb, "80x25 [Local]");
|
||||||
|
|
||||||
dvxFitWindow(ctx, win);
|
dvxFitWindow(ctx, win);
|
||||||
wgtInvalidate(root);
|
|
||||||
dvxMinimizeWindow(ctx, win);
|
dvxMinimizeWindow(ctx, win);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ OBJDIR = ../obj/dvxshell
|
||||||
BINDIR = ../bin
|
BINDIR = ../bin
|
||||||
LIBDIR = ../lib
|
LIBDIR = ../lib
|
||||||
|
|
||||||
SRCS = shellMain.c shellApp.c shellExport.c shellDesktop.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)/dvxshell.exe
|
||||||
|
|
||||||
|
|
@ -43,7 +43,5 @@ $(BINDIR):
|
||||||
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
$(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)/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
|
||||||
$(OBJDIR)/shellDesktop.o: shellDesktop.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJDIR) $(TARGET)
|
rm -rf $(OBJDIR) $(TARGET)
|
||||||
|
|
|
||||||
|
|
@ -106,20 +106,14 @@ void shellLog(const char *fmt, ...);
|
||||||
void shellExportInit(void);
|
void shellExportInit(void);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Program Manager / Desktop
|
// Desktop callback
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// Initialize the Program Manager window
|
// Default desktop app path
|
||||||
void shellDesktopInit(AppContextT *ctx);
|
#define SHELL_DESKTOP_APP "apps/progman.app"
|
||||||
|
|
||||||
// Update status bar with running app count
|
// Register a callback for app state changes (load, reap, crash).
|
||||||
void shellDesktopUpdateStatus(void);
|
// The desktop app calls this during appMain to receive notifications.
|
||||||
|
void shellRegisterDesktopUpdate(void (*updateFn)(void));
|
||||||
// ============================================================
|
|
||||||
// Task Manager
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
// Open the Task Manager dialog (Ctrl+Esc)
|
|
||||||
void shellTaskManager(AppContextT *ctx);
|
|
||||||
|
|
||||||
#endif // SHELL_APP_H
|
#endif // SHELL_APP_H
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,6 @@ static void buildPmWindow(void) {
|
||||||
updateStatusText();
|
updateStatusText();
|
||||||
|
|
||||||
dvxFitWindow(sCtx, sPmWindow);
|
dvxFitWindow(sCtx, sPmWindow);
|
||||||
wgtInvalidate(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -202,7 +201,6 @@ static void buildTaskManager(void) {
|
||||||
|
|
||||||
refreshTaskList();
|
refreshTaskList();
|
||||||
dvxFitWindow(sCtx, sTmWindow);
|
dvxFitWindow(sCtx, sTmWindow);
|
||||||
wgtInvalidate(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -385,7 +383,6 @@ static void refreshTaskList(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wgtListBoxSetItems(sTmListBox, items, count);
|
wgtListBoxSetItems(sTmListBox, items, count);
|
||||||
wgtInvalidatePaint(sTmListBox);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -464,7 +461,6 @@ static void updateStatusText(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wgtSetText(sStatusLabel, buf);
|
wgtSetText(sStatusLabel, buf);
|
||||||
wgtInvalidate(sStatusLabel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "taskswitch.h"
|
#include "taskswitch.h"
|
||||||
|
|
||||||
#include <sys/dxe.h>
|
#include <sys/dxe.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
static void shellRegisterExports(void);
|
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 *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);
|
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
|
// Wrapper: dvxDestroyWindow — checks for last-window reap
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -90,6 +107,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(dvxInit)
|
DXE_EXPORT(dvxInit)
|
||||||
DXE_EXPORT(dvxShutdown)
|
DXE_EXPORT(dvxShutdown)
|
||||||
DXE_EXPORT(dvxUpdate)
|
DXE_EXPORT(dvxUpdate)
|
||||||
|
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
|
||||||
DXE_EXPORT(dvxFitWindow)
|
DXE_EXPORT(dvxFitWindow)
|
||||||
DXE_EXPORT(dvxInvalidateRect)
|
DXE_EXPORT(dvxInvalidateRect)
|
||||||
DXE_EXPORT(dvxInvalidateWindow)
|
DXE_EXPORT(dvxInvalidateWindow)
|
||||||
|
|
@ -287,6 +305,7 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(wgtGetText)
|
DXE_EXPORT(wgtGetText)
|
||||||
DXE_EXPORT(wgtSetEnabled)
|
DXE_EXPORT(wgtSetEnabled)
|
||||||
DXE_EXPORT(wgtSetVisible)
|
DXE_EXPORT(wgtSetVisible)
|
||||||
|
DXE_EXPORT(wgtGetContext)
|
||||||
DXE_EXPORT(wgtSetName)
|
DXE_EXPORT(wgtSetName)
|
||||||
DXE_EXPORT(wgtFind)
|
DXE_EXPORT(wgtFind)
|
||||||
DXE_EXPORT(wgtDestroy)
|
DXE_EXPORT(wgtDestroy)
|
||||||
|
|
@ -312,8 +331,18 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(tsCurrentId)
|
DXE_EXPORT(tsCurrentId)
|
||||||
DXE_EXPORT(tsActiveCount)
|
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(shellLog)
|
||||||
|
DXE_EXPORT(shellLoadApp)
|
||||||
|
DXE_EXPORT(shellGetApp)
|
||||||
|
DXE_EXPORT(shellForceKillApp)
|
||||||
|
DXE_EXPORT(shellRunningAppCount)
|
||||||
|
DXE_EXPORT(shellRegisterDesktopUpdate)
|
||||||
|
|
||||||
// libc — memory
|
// libc — memory
|
||||||
DXE_EXPORT(malloc)
|
DXE_EXPORT(malloc)
|
||||||
|
|
@ -364,6 +393,11 @@ DXE_EXPORT_TABLE(shellExportTable)
|
||||||
DXE_EXPORT(time)
|
DXE_EXPORT(time)
|
||||||
DXE_EXPORT(localtime)
|
DXE_EXPORT(localtime)
|
||||||
|
|
||||||
|
// libc — directory
|
||||||
|
DXE_EXPORT(opendir)
|
||||||
|
DXE_EXPORT(readdir)
|
||||||
|
DXE_EXPORT(closedir)
|
||||||
|
|
||||||
// libc — misc
|
// libc — misc
|
||||||
DXE_EXPORT(qsort)
|
DXE_EXPORT(qsort)
|
||||||
DXE_EXPORT(rand)
|
DXE_EXPORT(rand)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// shellMain.c — DVX Shell entry point and main loop
|
// shellMain.c — DVX Shell entry point and main loop
|
||||||
//
|
//
|
||||||
// Initializes the GUI, task system, DXE export table, and Program Manager.
|
// Initializes the GUI, task system, DXE export table, and loads
|
||||||
// Runs the cooperative main loop, yielding to app tasks and reaping
|
// the desktop app. Runs the cooperative main loop, yielding to
|
||||||
// terminated apps each frame.
|
// app tasks and reaping terminated apps each frame.
|
||||||
|
|
||||||
#include "shellApp.h"
|
#include "shellApp.h"
|
||||||
#include "dvxDialog.h"
|
#include "dvxDialog.h"
|
||||||
|
|
@ -20,14 +20,16 @@
|
||||||
|
|
||||||
static AppContextT sCtx;
|
static AppContextT sCtx;
|
||||||
static jmp_buf sCrashJmp;
|
static jmp_buf sCrashJmp;
|
||||||
static volatile int sCrashSignal = 0;
|
static volatile int sCrashSignal = 0;
|
||||||
static FILE *sLogFile = NULL;
|
static FILE *sLogFile = NULL;
|
||||||
|
static void (*sDesktopUpdateFn)(void) = NULL;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Prototypes
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void crashHandler(int sig);
|
static void crashHandler(int sig);
|
||||||
|
static void desktopUpdate(void);
|
||||||
static void idleYield(void *ctx);
|
static void idleYield(void *ctx);
|
||||||
static void installCrashHandler(void);
|
static void installCrashHandler(void);
|
||||||
static void logCrash(int sig);
|
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
|
// 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
|
// main
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -182,8 +204,20 @@ int main(void) {
|
||||||
sCtx.idleCallback = idleYield;
|
sCtx.idleCallback = idleYield;
|
||||||
sCtx.idleCtx = &sCtx;
|
sCtx.idleCtx = &sCtx;
|
||||||
|
|
||||||
// Create the Program Manager window
|
// Load the desktop app
|
||||||
shellDesktopInit(&sCtx);
|
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
|
// Install crash handler after everything is initialized
|
||||||
installCrashHandler();
|
installCrashHandler();
|
||||||
|
|
@ -213,7 +247,7 @@ int main(void) {
|
||||||
|
|
||||||
sCurrentAppId = 0;
|
sCurrentAppId = 0;
|
||||||
sCrashSignal = 0;
|
sCrashSignal = 0;
|
||||||
shellDesktopUpdateStatus();
|
desktopUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
|
|
@ -228,8 +262,8 @@ int main(void) {
|
||||||
// Reap terminated apps
|
// Reap terminated apps
|
||||||
shellReapApps(&sCtx);
|
shellReapApps(&sCtx);
|
||||||
|
|
||||||
// Update status display
|
// Notify desktop of any state changes
|
||||||
shellDesktopUpdateStatus();
|
desktopUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
shellLog("DVX Shell shutting down...");
|
shellLog("DVX Shell shutting down...");
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// Fit window to widget tree
|
// Fit window to widget tree
|
||||||
dvxFitWindow(&ctx, win);
|
dvxFitWindow(&ctx, win);
|
||||||
wgtInvalidate(root);
|
|
||||||
|
|
||||||
// Poll serial during idle instead of yielding CPU
|
// Poll serial during idle instead of yielding CPU
|
||||||
ctx.idleCallback = idlePoll;
|
ctx.idleCallback = idlePoll;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue