DVX_GUI/dvxshell/shellMain.c

251 lines
6.8 KiB
C

// 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.
#include "shellApp.h"
#include "dvxDialog.h"
#include <stdarg.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/exceptn.h>
// ============================================================
// Module state
// ============================================================
static AppContextT sCtx;
static jmp_buf sCrashJmp;
static volatile int sCrashSignal = 0;
static FILE *sLogFile = NULL;
// ============================================================
// Prototypes
// ============================================================
static void crashHandler(int sig);
static void idleYield(void *ctx);
static void installCrashHandler(void);
static void logCrash(int sig);
// ============================================================
// crashHandler — catch page faults and other fatal signals
// ============================================================
static void crashHandler(int sig) {
logCrash(sig);
// Re-install handler (DJGPP resets to SIG_DFL after delivery)
signal(sig, crashHandler);
sCrashSignal = sig;
longjmp(sCrashJmp, 1);
}
// ============================================================
// idleYield — called when no dirty rects need compositing
// ============================================================
static void idleYield(void *ctx) {
(void)ctx;
if (tsActiveCount() > 1) {
tsYield();
}
}
// ============================================================
// installCrashHandler
// ============================================================
static void installCrashHandler(void) {
signal(SIGSEGV, crashHandler);
signal(SIGFPE, crashHandler);
signal(SIGILL, crashHandler);
}
// ============================================================
// logCrash — write exception details to the log
// ============================================================
static void logCrash(int sig) {
const char *sigName = "UNKNOWN";
if (sig == SIGSEGV) {
sigName = "SIGSEGV (page fault)";
} else if (sig == SIGFPE) {
sigName = "SIGFPE (floating point exception)";
} else if (sig == SIGILL) {
sigName = "SIGILL (illegal instruction)";
}
shellLog("=== CRASH ===");
shellLog("Signal: %d (%s)", sig, sigName);
shellLog("Current app ID: %ld", (long)sCurrentAppId);
if (sCurrentAppId > 0) {
ShellAppT *app = shellGetApp(sCurrentAppId);
if (app) {
shellLog("App name: %s", app->name);
shellLog("App path: %s", app->path);
shellLog("Has main loop: %s", app->hasMainLoop ? "yes" : "no");
shellLog("Task ID: %lu", (unsigned long)app->mainTaskId);
}
} else {
shellLog("Crashed in shell (task 0)");
}
// Dump CPU registers from exception state
jmp_buf *estate = __djgpp_exception_state_ptr;
if (estate) {
struct __jmp_buf *regs = &(*estate)[0];
shellLog("EIP: 0x%08lx CS: 0x%04x", regs->__eip, regs->__cs);
shellLog("EAX: 0x%08lx EBX: 0x%08lx ECX: 0x%08lx EDX: 0x%08lx", regs->__eax, regs->__ebx, regs->__ecx, regs->__edx);
shellLog("ESI: 0x%08lx EDI: 0x%08lx EBP: 0x%08lx ESP: 0x%08lx", regs->__esi, regs->__edi, regs->__ebp, regs->__esp);
shellLog("DS: 0x%04x ES: 0x%04x FS: 0x%04x GS: 0x%04x SS: 0x%04x", regs->__ds, regs->__es, regs->__fs, regs->__gs, regs->__ss);
shellLog("EFLAGS: 0x%08lx", regs->__eflags);
}
}
// ============================================================
// shellLog — append a line to SHELL.LOG
// ============================================================
void shellLog(const char *fmt, ...) {
if (!sLogFile) {
return;
}
va_list ap;
va_start(ap, fmt);
vfprintf(sLogFile, fmt, ap);
va_end(ap);
fprintf(sLogFile, "\n");
fflush(sLogFile);
}
// ============================================================
// main
// ============================================================
int main(void) {
sLogFile = fopen("shell.log", "w");
shellLog("DVX Shell starting...");
// Initialize GUI
int32_t result = dvxInit(&sCtx, 640, 480, 32);
if (result != 0) {
shellLog("Failed to initialize DVX GUI (error %ld)", (long)result);
if (sLogFile) {
fclose(sLogFile);
}
return 1;
}
// Initialize task system
if (tsInit() != TS_OK) {
shellLog("Failed to initialize task system");
dvxShutdown(&sCtx);
if (sLogFile) {
fclose(sLogFile);
}
return 1;
}
// Shell task (task 0) gets high priority for responsive UI
tsSetPriority(0, TS_PRIORITY_HIGH);
// Register DXE export table
shellExportInit();
// Initialize app slot table
shellAppInit();
// Set up idle callback for cooperative yielding
sCtx.idleCallback = idleYield;
sCtx.idleCtx = &sCtx;
// Create the Program Manager window
shellDesktopInit(&sCtx);
// Install crash handler after everything is initialized
installCrashHandler();
shellLog("DVX Shell ready.");
// Set recovery point for crash handler
if (setjmp(sCrashJmp) != 0) {
// Returned here from crash handler via longjmp.
// If the crash was in a non-main task, the task switcher still
// thinks that task is running. Fix it before doing anything else.
tsRecoverToMain();
shellLog("Recovering from crash, killing app %ld", (long)sCurrentAppId);
if (sCurrentAppId > 0) {
ShellAppT *app = shellGetApp(sCurrentAppId);
if (app) {
char msg[256];
snprintf(msg, sizeof(msg), "'%s' has caused a fault and will be terminated.", app->name);
shellForceKillApp(&sCtx, app);
sCurrentAppId = 0;
dvxMessageBox(&sCtx, "Application Error", msg, MB_OK | MB_ICONERROR);
}
}
sCurrentAppId = 0;
sCrashSignal = 0;
shellDesktopUpdateStatus();
}
// Main loop
while (sCtx.running) {
dvxUpdate(&sCtx);
// Give app tasks CPU time even during active frames
if (tsActiveCount() > 1) {
tsYield();
}
// Reap terminated apps
shellReapApps(&sCtx);
// Update status display
shellDesktopUpdateStatus();
}
shellLog("DVX Shell shutting down...");
// Clean shutdown: terminate all apps
shellTerminateAllApps(&sCtx);
tsShutdown();
dvxShutdown(&sCtx);
shellLog("DVX Shell exited.");
if (sLogFile) {
fclose(sLogFile);
sLogFile = NULL;
}
return 0;
}