DVX_GUI/dvxshell/shellExport.c

487 lines
15 KiB
C

// shellExport.c — DXE export table and wrapper functions for DVX Shell
//
// Exports all dvx*/wgt*/ts* symbols that DXE apps need. A few functions
// are wrapped for resource tracking (window ownership via appId).
//
// DXE3 is DJGPP's dynamic linking mechanism. Unlike ELF shared libraries,
// DXE modules have no implicit access to the host's symbol table. Every
// function or variable the DXE needs must be explicitly listed in an
// export table registered via dlregsym() BEFORE any dlopen() call. If a
// symbol is missing, dlopen() returns NULL with a "symbol not found" error.
//
// This file is essentially the ABI contract between the shell and apps.
// Three categories of exports:
//
// 1. Wrapped functions: dvxCreateWindow, dvxCreateWindowCentered,
// dvxDestroyWindow. These are intercepted to stamp win->appId for
// resource ownership tracking. The DXE sees them under their original
// names — the app code calls dvxCreateWindow() normally and gets our
// wrapper transparently.
//
// 2. Direct exports: all other dvx/wgt/wm/ts functions. These are safe
// to call without shell-side interception.
//
// 3. libc functions: DXE modules are statically linked against DJGPP's
// libc, but DJGPP's DXE3 loader requires explicit re-export of any
// libc symbols the module references. Without these entries, the DXE
// would fail to load with unresolved symbol errors. This is a DXE3
// design limitation — there's no automatic fallback to the host's libc.
#include "shellApp.h"
#include "dvxApp.h"
#include "dvxDialog.h"
#include "dvxWidget.h"
#include "dvxDraw.h"
#include "dvxVideo.h"
#include "dvxWm.h"
#include "taskswitch.h"
#include <sys/dxe.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
// ============================================================
// Prototypes
// ============================================================
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);
// ============================================================
// Wrapper: dvxCreateWindow — stamps win->appId
// ============================================================
// The wrapper calls the real dvxCreateWindow, 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. The app never
// sees the difference — the wrapper has the same signature and is
// exported under the same name as the original function.
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;
}
}
}
}
// ============================================================
// Export table
// ============================================================
// DXE_EXPORT_TABLE generates a DXE symbol table array. DXE_EXPORT(fn)
// expands to { "_fn", (void *)fn } — the underscore prefix matches COFF
// symbol naming. For wrapped functions we use raw entries with explicit
// names so the DXE sees "_dvxCreateWindow" but gets our wrapper's address.
DXE_EXPORT_TABLE(shellExportTable)
// Wrapped functions (exported under original names, but pointing to
// our wrappers that add resource tracking)
{ "_dvxCreateWindow", (void *)shellWrapCreateWindow },
{ "_dvxDestroyWindow", (void *)shellWrapDestroyWindow },
// dvxApp.h — direct exports
DXE_EXPORT(dvxInit)
DXE_EXPORT(dvxShutdown)
DXE_EXPORT(dvxUpdate)
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
DXE_EXPORT(dvxFitWindow)
DXE_EXPORT(dvxInvalidateRect)
DXE_EXPORT(dvxInvalidateWindow)
DXE_EXPORT(dvxMinimizeWindow)
DXE_EXPORT(dvxMaximizeWindow)
DXE_EXPORT(dvxQuit)
DXE_EXPORT(dvxSetTitle)
DXE_EXPORT(dvxGetFont)
DXE_EXPORT(dvxGetColors)
DXE_EXPORT(dvxGetDisplay)
DXE_EXPORT(dvxGetBlitOps)
DXE_EXPORT(dvxSetWindowIcon)
DXE_EXPORT(dvxLoadImage)
DXE_EXPORT(dvxFreeImage)
DXE_EXPORT(dvxSaveImage)
DXE_EXPORT(dvxScreenshot)
DXE_EXPORT(dvxWindowScreenshot)
DXE_EXPORT(dvxCreateAccelTable)
DXE_EXPORT(dvxFreeAccelTable)
DXE_EXPORT(dvxAddAccel)
DXE_EXPORT(dvxCascadeWindows)
DXE_EXPORT(dvxTileWindows)
DXE_EXPORT(dvxTileWindowsH)
DXE_EXPORT(dvxTileWindowsV)
DXE_EXPORT(dvxClipboardCopy)
DXE_EXPORT(dvxClipboardGet)
// dvxDialog.h
DXE_EXPORT(dvxMessageBox)
DXE_EXPORT(dvxFileDialog)
// dvxDraw.h
DXE_EXPORT(rectFill)
DXE_EXPORT(rectCopy)
DXE_EXPORT(drawBevel)
DXE_EXPORT(drawChar)
DXE_EXPORT(drawText)
DXE_EXPORT(drawTextN)
DXE_EXPORT(textWidth)
DXE_EXPORT(drawTextAccel)
DXE_EXPORT(textWidthAccel)
DXE_EXPORT(drawFocusRect)
DXE_EXPORT(drawHLine)
DXE_EXPORT(drawVLine)
// dvxVideo.h
DXE_EXPORT(packColor)
// dvxWm.h
DXE_EXPORT(wmAddMenuBar)
DXE_EXPORT(wmAddMenu)
DXE_EXPORT(wmAddMenuItem)
DXE_EXPORT(wmAddMenuCheckItem)
DXE_EXPORT(wmAddMenuRadioItem)
DXE_EXPORT(wmAddMenuSeparator)
DXE_EXPORT(wmAddSubMenu)
DXE_EXPORT(wmAddVScrollbar)
DXE_EXPORT(wmAddHScrollbar)
DXE_EXPORT(wmSetTitle)
DXE_EXPORT(wmSetIcon)
DXE_EXPORT(wmCreateMenu)
DXE_EXPORT(wmFreeMenu)
DXE_EXPORT(wmUpdateContentRect)
DXE_EXPORT(wmReallocContentBuf)
// dvxWidget.h — window integration
DXE_EXPORT(wgtInitWindow)
// dvxWidget.h — containers
DXE_EXPORT(wgtVBox)
DXE_EXPORT(wgtHBox)
DXE_EXPORT(wgtFrame)
// dvxWidget.h — basic widgets
DXE_EXPORT(wgtLabel)
DXE_EXPORT(wgtButton)
DXE_EXPORT(wgtCheckbox)
DXE_EXPORT(wgtTextInput)
DXE_EXPORT(wgtPasswordInput)
DXE_EXPORT(wgtMaskedInput)
// dvxWidget.h — radio buttons
DXE_EXPORT(wgtRadioGroup)
DXE_EXPORT(wgtRadio)
// dvxWidget.h — spacing
DXE_EXPORT(wgtSpacer)
DXE_EXPORT(wgtHSeparator)
DXE_EXPORT(wgtVSeparator)
// dvxWidget.h — complex widgets
DXE_EXPORT(wgtListBox)
DXE_EXPORT(wgtTextArea)
// dvxWidget.h — dropdown/combo
DXE_EXPORT(wgtDropdown)
DXE_EXPORT(wgtDropdownSetItems)
DXE_EXPORT(wgtDropdownGetSelected)
DXE_EXPORT(wgtDropdownSetSelected)
DXE_EXPORT(wgtComboBox)
DXE_EXPORT(wgtComboBoxSetItems)
DXE_EXPORT(wgtComboBoxGetSelected)
DXE_EXPORT(wgtComboBoxSetSelected)
// dvxWidget.h — progress bar
DXE_EXPORT(wgtProgressBar)
DXE_EXPORT(wgtProgressBarV)
DXE_EXPORT(wgtProgressBarSetValue)
DXE_EXPORT(wgtProgressBarGetValue)
// dvxWidget.h — slider
DXE_EXPORT(wgtSlider)
DXE_EXPORT(wgtSliderSetValue)
DXE_EXPORT(wgtSliderGetValue)
// dvxWidget.h — spinner
DXE_EXPORT(wgtSpinner)
DXE_EXPORT(wgtSpinnerSetValue)
DXE_EXPORT(wgtSpinnerGetValue)
DXE_EXPORT(wgtSpinnerSetRange)
DXE_EXPORT(wgtSpinnerSetStep)
// dvxWidget.h — tab control
DXE_EXPORT(wgtTabControl)
DXE_EXPORT(wgtTabPage)
DXE_EXPORT(wgtTabControlSetActive)
DXE_EXPORT(wgtTabControlGetActive)
// dvxWidget.h — status bar / toolbar
DXE_EXPORT(wgtStatusBar)
DXE_EXPORT(wgtToolbar)
// dvxWidget.h — tree view
DXE_EXPORT(wgtTreeView)
DXE_EXPORT(wgtTreeViewGetSelected)
DXE_EXPORT(wgtTreeViewSetSelected)
DXE_EXPORT(wgtTreeViewSetMultiSelect)
DXE_EXPORT(wgtTreeViewSetReorderable)
DXE_EXPORT(wgtTreeItem)
DXE_EXPORT(wgtTreeItemSetExpanded)
DXE_EXPORT(wgtTreeItemIsExpanded)
DXE_EXPORT(wgtTreeItemIsSelected)
DXE_EXPORT(wgtTreeItemSetSelected)
// dvxWidget.h — list view
DXE_EXPORT(wgtListView)
DXE_EXPORT(wgtListViewSetColumns)
DXE_EXPORT(wgtListViewSetData)
DXE_EXPORT(wgtListViewGetSelected)
DXE_EXPORT(wgtListViewSetSelected)
DXE_EXPORT(wgtListViewSetSort)
DXE_EXPORT(wgtListViewSetHeaderClickCallback)
DXE_EXPORT(wgtListViewSetMultiSelect)
DXE_EXPORT(wgtListViewIsItemSelected)
DXE_EXPORT(wgtListViewSetItemSelected)
DXE_EXPORT(wgtListViewSelectAll)
DXE_EXPORT(wgtListViewClearSelection)
DXE_EXPORT(wgtListViewSetReorderable)
// dvxWidget.h — scroll pane / splitter
DXE_EXPORT(wgtScrollPane)
DXE_EXPORT(wgtSplitter)
DXE_EXPORT(wgtSplitterSetPos)
DXE_EXPORT(wgtSplitterGetPos)
// dvxWidget.h — image / image button
DXE_EXPORT(wgtImageButton)
DXE_EXPORT(wgtImageButtonFromFile)
DXE_EXPORT(wgtImageButtonSetData)
DXE_EXPORT(wgtImage)
DXE_EXPORT(wgtImageFromFile)
DXE_EXPORT(wgtImageSetData)
// dvxWidget.h — canvas
DXE_EXPORT(wgtCanvas)
DXE_EXPORT(wgtCanvasClear)
DXE_EXPORT(wgtCanvasSetMouseCallback)
DXE_EXPORT(wgtCanvasSetPenColor)
DXE_EXPORT(wgtCanvasSetPenSize)
DXE_EXPORT(wgtCanvasSave)
DXE_EXPORT(wgtCanvasLoad)
DXE_EXPORT(wgtCanvasDrawLine)
DXE_EXPORT(wgtCanvasDrawRect)
DXE_EXPORT(wgtCanvasFillRect)
DXE_EXPORT(wgtCanvasFillCircle)
DXE_EXPORT(wgtCanvasSetPixel)
DXE_EXPORT(wgtCanvasGetPixel)
// dvxWidget.h — ANSI terminal
DXE_EXPORT(wgtAnsiTerm)
DXE_EXPORT(wgtAnsiTermWrite)
DXE_EXPORT(wgtAnsiTermClear)
DXE_EXPORT(wgtAnsiTermSetComm)
DXE_EXPORT(wgtAnsiTermSetScrollback)
DXE_EXPORT(wgtAnsiTermPoll)
DXE_EXPORT(wgtAnsiTermRepaint)
// dvxWidget.h — operations
DXE_EXPORT(wgtInvalidate)
DXE_EXPORT(wgtInvalidatePaint)
DXE_EXPORT(wgtSetDebugLayout)
DXE_EXPORT(wgtSetText)
DXE_EXPORT(wgtGetText)
DXE_EXPORT(wgtSetEnabled)
DXE_EXPORT(wgtSetVisible)
DXE_EXPORT(wgtGetContext)
DXE_EXPORT(wgtSetName)
DXE_EXPORT(wgtFind)
DXE_EXPORT(wgtDestroy)
// dvxWidget.h — list box ops
DXE_EXPORT(wgtListBoxSetItems)
DXE_EXPORT(wgtListBoxGetSelected)
DXE_EXPORT(wgtListBoxSetSelected)
DXE_EXPORT(wgtListBoxSetMultiSelect)
DXE_EXPORT(wgtListBoxIsItemSelected)
DXE_EXPORT(wgtListBoxSetItemSelected)
DXE_EXPORT(wgtListBoxSelectAll)
DXE_EXPORT(wgtListBoxClearSelection)
DXE_EXPORT(wgtListBoxSetReorderable)
// dvxWidget.h — layout
DXE_EXPORT(wgtResolveSize)
DXE_EXPORT(wgtLayout)
DXE_EXPORT(wgtPaint)
// taskswitch.h — only yield and query functions are exported.
// tsCreate/tsKill/etc. are NOT exported because apps should not
// manipulate the task system directly — the shell manages task
// lifecycle through shellLoadApp/shellForceKillApp.
DXE_EXPORT(tsYield)
DXE_EXPORT(tsCurrentId)
DXE_EXPORT(tsActiveCount)
// 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 exports below. DXE3 modules are compiled as relocatable objects,
// not fully linked executables. Any libc function the DXE calls must be
// re-exported here so the DXE3 loader can resolve the reference at
// dlopen time. Forgetting an entry produces a cryptic "unresolved
// symbol" error at load time — no lazy binding fallback exists.
// libc — memory
DXE_EXPORT(malloc)
DXE_EXPORT(free)
DXE_EXPORT(calloc)
DXE_EXPORT(realloc)
// libc — string
DXE_EXPORT(memcpy)
DXE_EXPORT(memset)
DXE_EXPORT(memmove)
DXE_EXPORT(memcmp)
DXE_EXPORT(strlen)
DXE_EXPORT(strcmp)
DXE_EXPORT(strncmp)
DXE_EXPORT(strcpy)
DXE_EXPORT(strncpy)
DXE_EXPORT(strcat)
DXE_EXPORT(strncat)
DXE_EXPORT(strchr)
DXE_EXPORT(strrchr)
DXE_EXPORT(strstr)
DXE_EXPORT(strtol)
// libc — I/O
DXE_EXPORT(printf)
DXE_EXPORT(fprintf)
DXE_EXPORT(sprintf)
DXE_EXPORT(snprintf)
DXE_EXPORT(puts)
DXE_EXPORT(fopen)
DXE_EXPORT(fclose)
DXE_EXPORT(fread)
DXE_EXPORT(fwrite)
DXE_EXPORT(fgets)
DXE_EXPORT(fseek)
DXE_EXPORT(ftell)
DXE_EXPORT(feof)
DXE_EXPORT(ferror)
// libc — math
DXE_EXPORT(sin)
DXE_EXPORT(cos)
DXE_EXPORT(sqrt)
// libc — time
DXE_EXPORT(clock)
DXE_EXPORT(time)
DXE_EXPORT(localtime)
// libc — directory
DXE_EXPORT(opendir)
DXE_EXPORT(readdir)
DXE_EXPORT(closedir)
// libc — filesystem
DXE_EXPORT(stat)
// libc — misc
DXE_EXPORT(qsort)
DXE_EXPORT(rand)
DXE_EXPORT(srand)
DXE_EXPORT(abs)
DXE_EXPORT(atoi)
DXE_EXPORT_END
// ============================================================
// shellRegisterExports
// ============================================================
// dlregsym registers our export table with DJGPP's DXE3 runtime.
// Must be called once before any dlopen — subsequent dlopen calls
// will search this table to resolve DXE symbol references.
static void shellRegisterExports(void) {
dlregsym(shellExportTable);
}
// ============================================================
// Public init function
// ============================================================
void shellExportInit(void) {
shellRegisterExports();
}