DVX_GUI/shell/shellExport.c

119 lines
4.5 KiB
C

// shellExport.c -- DXE wrapper overrides for resource tracking
//
// Registers three wrapper functions that override the real
// dvxCreateWindow, dvxCreateWindowCentered, and dvxDestroyWindow
// for all subsequently loaded app modules.
//
// The key mechanic: symbol overrides registered via
// platformRegisterSymOverrides take precedence over previously loaded
// symbols. Since libdvx (which has the real functions) was loaded
// before shellExportInit() registers these wrappers, libdvx keeps the
// real implementations. But any app module loaded afterward gets the
// wrappers, which add resource tracking (appId stamping, last-window
// reaping) transparently.
//
// All other symbol exports (dvx*, wgt*, platform*, libc) are handled
// by the loaded modules and the platform layer's export table -- they
// no longer need to be listed here.
#include "shellApp.h"
#include "dvxApp.h"
#include "dvxPlatform.h"
#include "dvxWm.h"
// ============================================================
// Prototypes
// ============================================================
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);
// ============================================================
// Wrapper: dvxCreateWindow -- stamps win->appId
// ============================================================
// The wrapper calls the real dvxCreateWindow (resolved from libdvx
// at shellcore load time), then tags the result with sCurrentAppId.
// This is how the shell knows which app owns which window, enabling
// per-app window cleanup on crash/termination.
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable) {
WindowT *win = dvxCreateWindow(ctx, title, x, y, w, h, resizable);
if (win) {
win->appId = sCurrentAppId;
}
return win;
}
// ============================================================
// 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
// ============================================================
// Beyond just destroying the window, this wrapper implements the lifecycle
// rule for callback-only apps: when their last window closes, they're done.
// Main-loop apps manage their own lifetime (their task returns from
// appMain), so this check only applies to callback-only apps.
// The appId is captured before destruction because the window struct is
// freed by dvxDestroyWindow.
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win) {
int32_t appId = win->appId;
dvxDestroyWindow(ctx, win);
// If this was a callback-only app's last window, mark for reaping
if (appId > 0) {
ShellAppT *app = shellGetApp(appId);
if (app && !app->hasMainLoop && app->state == AppStateRunningE) {
// Check if app still has any windows
bool hasWindows = false;
for (int32_t i = 0; i < ctx->stack.count; i++) {
if (ctx->stack.windows[i]->appId == appId) {
hasWindows = true;
break;
}
}
if (!hasWindows) {
app->state = AppStateTerminatingE;
}
}
}
}
// ============================================================
// shellExportInit
// ============================================================
// Register the wrapper overrides. Must be called before any
// shellLoadApp() -- wrappers only affect subsequently loaded modules.
void shellExportInit(void) {
static const PlatformSymOverrideT sOverrides[] = {
{"_dvxCreateWindow", (void *)shellWrapCreateWindow},
{"_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered},
{"_dvxDestroyWindow", (void *)shellWrapDestroyWindow},
{NULL, NULL}
};
platformRegisterSymOverrides(sOverrides);
}