// shellExport.c -- DXE wrapper overrides for resource tracking // // Registers three wrapper functions via dlregsym that override the // real dvxCreateWindow, dvxCreateWindowCentered, and dvxDestroyWindow // for all subsequently loaded app DXEs. // // The key mechanic: dlregsym takes precedence over RTLD_GLOBAL exports. // Since libdvx.dxe (which has the real functions) was loaded before // shellExportInit() registers these wrappers, libdvx.dxe keeps the // real implementations. But any app DXE 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 DXE modules and the loader's dlregsym table -- they no longer // need to be listed here. #include "shellApp.h" #include "dvxApp.h" #include "dvxWm.h" #include // ============================================================ // 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.dxe // 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; } } } } // ============================================================ // Wrapper export table // ============================================================ // Only three entries: the resource-tracking wrappers exported under // the original function names. All other symbols come from the // loaded DXE modules (via RTLD_GLOBAL) and the loader's dlregsym. DXE_EXPORT_TABLE(sWrapperTable) { "_dvxCreateWindow", (void *)shellWrapCreateWindow }, { "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered }, { "_dvxDestroyWindow", (void *)shellWrapDestroyWindow }, DXE_EXPORT_END // ============================================================ // shellExportInit // ============================================================ // Register the wrapper overrides. Must be called before any // shellLoadApp() -- wrappers only affect subsequently loaded DXEs. void shellExportInit(void) { dlregsym(sWrapperTable); }