From f28bbd476eedcd80e048b02e5e2aa8937173faf4 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 21 Dec 2025 19:39:05 -0600 Subject: [PATCH] Library loading / unloading working. --- Makefile | 15 +++- dyn/kpsvideo/kpsvideo.c | 8 +- dyn/stbimage/stbimage.c | 2 +- env.sh | 2 +- kpsmpgc/main.c | 12 ++- roo_e/main.c | 155 +++++++++++++++++++++++----------- roo_e/roo_e.h | 71 ++++++++++++++++ {include => roo_e}/stddclmr.h | 0 8 files changed, 207 insertions(+), 58 deletions(-) create mode 100644 roo_e/roo_e.h rename {include => roo_e}/stddclmr.h (100%) diff --git a/Makefile b/Makefile index cb4e8f2..60d5cb5 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,9 @@ # +# This Makefile is terrible. I have no idea what I'm doing. + + # Tools CC := gcc AR := ar @@ -42,7 +45,7 @@ FNT := $(BIN)/fonts FIN := font/in # Include Paths -INC := ../include include 3rdparty 3rdparty/pthreads/include +INC := include 3rdparty 3rdparty/pthreads/include roo_e # Font Compiler Source and Target - NOTE: This is a Linux binary! FONT_ELF := font @@ -81,7 +84,7 @@ use_build_script: # Build Everything .PHONY: dos -dos: $(BIN)/$(ROOE_EXE) $(APP)/$(MPGC_EXE) $(DYN_FILES) +dos: $(BIN)/$(ROOE_EXE) $(APP)/$(MPGC_EXE) $(DYN_FILES) sdk_extra .PHONY: linux linux: $(FONTS) @@ -113,7 +116,7 @@ $(APP)/$(MPGC_EXE): $(MPGC_OBJ) Makefile mkdir -p $(dir $@) $(DXE3GEN) -o $@ $(MPGC_OBJ) -U $(LDFLAGS) $(MPGC_LIB) -# DYN Targets +# DYN Targets - This is NOT how make should be used. .PHONY: dyns $(DYN_FILES): $(DYN_SOURCE) Makefile echo $(DYN_SOURCE) @@ -124,6 +127,12 @@ $(DYN_FILES): $(DYN_SOURCE) Makefile mv $@.a $(SDK)/lib/. if [ ! -z "$(shell find dyn/$(basename $(notdir $@)) -name '*.h')" ]; then cp $(shell find dyn/$(basename $(notdir $@)) -name '*.h') $(SDK)/include/.; fi +# Extra SDK Files +.PHONEY: sdk_extra +sdk_extra: 3rdparty/stbds.h 3rdparty/stbimage.h Makefile + cp 3rdparty/stbds.h 3rdparty/stbimage.h ${SDK}/include/. + cp roo_e/*.h ${SDK}/include/. + # Build C Files $(OBJ)/%.c.o: %.c mkdir -p $(dir $@) diff --git a/dyn/kpsvideo/kpsvideo.c b/dyn/kpsvideo/kpsvideo.c index 4093ed6..d003d00 100644 --- a/dyn/kpsvideo/kpsvideo.c +++ b/dyn/kpsvideo/kpsvideo.c @@ -21,11 +21,17 @@ */ +#include "roo_e.h" #include "kpsvideo.h" -#include int dynStart(void) { printf("kpsvideo starting!\n"); return 0; } + + +int dynStop(void) { + printf("kpsvideo stopping!\n"); + return 0; +} diff --git a/dyn/stbimage/stbimage.c b/dyn/stbimage/stbimage.c index 6450b6c..5f18a6b 100644 --- a/dyn/stbimage/stbimage.c +++ b/dyn/stbimage/stbimage.c @@ -21,7 +21,7 @@ */ -#include +#include "roo_e.h" #define STB_IMAGE_IMPLEMENTATION #include "stbimage.h" diff --git a/env.sh b/env.sh index 10d9977..c323055 100755 --- a/env.sh +++ b/env.sh @@ -25,4 +25,4 @@ eval "$(../toolchains/toolchains.sh use x86 dos)" -export C_INCLUDE_PATH=${HOME}/code/toolchains/x-tools/djgpp/i586-pc-msdosdjgpp/sys-include +export C_INCLUDE_PATH=${HOME}/code/toolchains/x-tools/djgpp/i586-pc-msdosdjgpp/sys-include:roo_e diff --git a/kpsmpgc/main.c b/kpsmpgc/main.c index 7aa63f1..a96f5fb 100644 --- a/kpsmpgc/main.c +++ b/kpsmpgc/main.c @@ -21,16 +21,16 @@ */ -#include +#include "roo_e.h" #include "stbimage.h" -void __attribute__((constructor)) registerApp(void) { +ROO_CONSTRUCTOR registerApp(void) { printf("App Loaded!\n"); } -void __attribute__((destructor)) unregisterApp(void) { +ROO_DESTRUCTOR unregisterApp(void) { printf("App Unloaded!\n"); } @@ -43,3 +43,9 @@ int appMain(const int argc, char* argv[]) { printf("stbi_failure_reason: [%s]\n", stbi_failure_reason()); return 0; } + + +int appStop(RooStopReasonT why) { + printf("App Stopped!\n"); + return ROO_ABORT_NONE; +} diff --git a/roo_e/main.c b/roo_e/main.c index fbf1929..175163e 100644 --- a/roo_e/main.c +++ b/roo_e/main.c @@ -22,22 +22,11 @@ */ -#include -#include -#include -#include -#include -#include "pthread.h" - -// ReSharper disable once CppUnusedIncludeDirective -#include "stddclmr.h" -#include "util.h" - #define STB_DS_IMPLEMENTATION -#include "stbds.h" +#include "roo_e.h" -int startApp(const char *appName, int argc, char *argv[]); +static RooNamedPointerT **_appList = NULL; // NOLINT #ifdef PLATFORM_DOS @@ -118,55 +107,83 @@ void *dxeResolver(const char *symbol) { void *to; } funcPtrCast; - printf("%s: undefined symbol in dynamic module!\n", symbol); + printf("ROO/E: %s: undefined symbol in dynamic module!\n", symbol); funcPtrCast.from = lastResort; return funcPtrCast.to; // NOLINT } static int lastResort(void) { - printf("Last resort function called!\n"); + printf("ROO/E: Last resort function called!\n"); return 0; } #endif // PLATFORM_DOS -int startApp(const char *appName, const int argc, char *argv[]) { - void *dxe = NULL; - int result = -1; - char *filename = NULL; - int (*appMain)(const int argc, char *argv[]); +int rooStartApp(const char *appName, const int argc, char *argv[], RooNamedPointerT **handle) { + RooNamedPointerT *namedPointer = NULL; + int result = -1; + int (*appMain)(const int argc, char *argv[]); // NOLINT union { void *from; int (*to)(const int argc, char *argv[]); - } appPtrCast; + } appPtrCast; // NOLINT dlerror(); // Clear any existing error. - filename = utilCreateString("app/%s.app", appName); - dxe = dlopen(filename, RTLD_LAZY); - if (!dxe) { - printf("Unable to load %s! %s\n", filename, dlerror()); + NEW(RooNamedPointerT, namedPointer); + namedPointer->name = utilCreateString("app/%s.app", appName); + namedPointer->pointer = dlopen(namedPointer->name, RTLD_LAZY); + if (!namedPointer->pointer) { + printf("ROO/E: Unable to load %s! %s\n", namedPointer->name, dlerror()); } else { - appPtrCast.from = dlsym(dxe, "_appMain"); + appPtrCast.from = dlsym(namedPointer->pointer, "_appMain"); appMain = appPtrCast.to; // NOLINT result = appMain(argc, argv); + arrput(_appList, namedPointer); + if (handle != NULL) *handle = namedPointer->pointer; } - dlclose(dxe); - return result; } +RooAbortReasonT rooStopApp(RooNamedPointerT **appHandle, RooStopReasonT reason) { + RooAbortReasonT abort = ROO_ABORT_NONE; + RooNamedPointerT *namedPointer = NULL; + int (*appStop)(RooStopReasonT reason); + + union { + void *from; + int (*to)(RooStopReasonT reason); + } appPtrCast; // NOLINT + + namedPointer = *appHandle; + + appPtrCast.from = dlsym(namedPointer->pointer, "_appStop"); + if (appPtrCast.from != NULL) { + appStop = appPtrCast.to; // NOLINT + abort = appStop(reason); + } + if (abort == ROO_ABORT_NONE) { + dlclose(namedPointer->pointer); + DEL(namedPointer->name); + DEL(namedPointer); + } + + return abort; +} + + int main(const int argc, char *argv[]) { - void *dxe = NULL; - DIR *dir = NULL; - struct dirent *dirent = NULL; - char *filename = NULL; - int (*dynInit)(void); - int result; + DIR *dir = NULL; + struct dirent *dirent = NULL; + int result; + RooAbortReasonT abort; + RooNamedPointerT **dynList = NULL; + RooNamedPointerT *namedPointer = NULL; + int (*dynInitStop)(void); union { void *from; @@ -184,40 +201,80 @@ int main(const int argc, char *argv[]) { #endif // PLATFORM_DOS // Load libraries. - debug("Loading dynamic libraries.\n"); + debug("ROO/E: Loading dynamic libraries.\n"); if ((dir = opendir("dyn/")) == NULL) { - printf("Unable to open dyn directory!\n"); + printf("ROO/E: Unable to open dyn directory!\n"); exit(1); } while ((dirent = readdir(dir))) { if ((dirent->d_type == DT_REG) || (dirent->d_type == DT_LNK)) { if (utilEndsWith(dirent->d_name, ".dyn", false)) { - filename = utilCreateString("dyn/%s", dirent->d_name); + NEW(RooNamedPointerT, namedPointer); + namedPointer->name = utilCreateString("dyn/%s", dirent->d_name); dlerror(); // Clear any existing error. - dxe = dlopen(filename,RTLD_LAZY | RTLD_GLOBAL); - if (!dxe) { - printf("Unable to load %s! %s\n", filename, dlerror()); + namedPointer->pointer = dlopen(namedPointer->name,RTLD_LAZY | RTLD_GLOBAL); + if (!namedPointer->pointer) { + printf("ROO/E: Unable to load %s! %s\n", namedPointer->name, dlerror()); exit(1); } - debug("Loaded %s.\n", filename); - dynPtrCast.from = dlsym(dxe, "_dynStart"); + debug("ROO/E: Loaded %s.\n", namedPointer->name); + dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStart"); if (dynPtrCast.from != NULL) { - dynInit = dynPtrCast.to; // NOLINT - debug("Starting %s.\n", filename); - result = dynInit(); + dynInitStop = dynPtrCast.to; // NOLINT + debug("ROO/E: Starting %s.\n", namedPointer->name); + result = dynInitStop(); if (result != 0) { - printf("%s failed to start!\n", filename); + printf("ROO/E: %s failed to start!\n", namedPointer->name); exit(1); } } - free(filename); + arrput(dynList, namedPointer); } } } closedir(dir); - // Load the Shell. - result = startApp("kpsmpgc", 0, NULL); + debug("ROO/E: Loaded %d libraries.\n", arrlen(dynList)); + + // Load the Shell. This needs to be configurable. + result = rooStartApp("kpsmpgc", 0, NULL, NULL); + + // Run as long as apps are running. + while (arrlen(_appList) > 0) { + // Wait for all apps to exit. + + // Unload APPs. + while (arrlen(_appList) > 0) { + namedPointer = _appList[0]; // NOLINT + debug("ROO/E: Stopping %s\n", namedPointer->name); + abort = rooStopApp(&namedPointer, ROO_STOP_SHUTDOWN); + if (abort == ROO_ABORT_NONE) { + arrdel(_appList, 0); + } else { + break; // Somebody didn't want to stop. + } + } + } + + // Unload DYNs + debug("ROO/E: Unloading %d libraries.\n", arrlen(dynList)); + while (arrlen(dynList) > 0) { + namedPointer = dynList[0]; // NOLINT + debug("ROO/E: Stopping %s\n", namedPointer->name); + dynPtrCast.from = dlsym(namedPointer->pointer, "_dynStop"); + if (dynPtrCast.from != NULL) { + dynInitStop = dynPtrCast.to; // NOLINT + result = dynInitStop(); + if (result != 0) { + printf("ROO/E: %s failed to stop!\n", namedPointer->name); + exit(1); + } + } + arrdel(dynList, 0); + dlclose(namedPointer->pointer); + DEL(namedPointer->name); + DEL(namedPointer); + } return result; } diff --git a/roo_e/roo_e.h b/roo_e/roo_e.h new file mode 100644 index 0000000..37b7662 --- /dev/null +++ b/roo_e/roo_e.h @@ -0,0 +1,71 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2026 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + */ + + +#ifndef ROO_E_H +#define ROO_E_H + + +#include +#include +#include +#include +#include + +#include "pthread.h" +#include "stbds.h" +#include "util.h" +#include "stddclmr.h" + + +#define ROO_CONSTRUCTOR void __attribute__((constructor)) +#define ROO_DESTRUCTOR void __attribute__((destructor)) + +// Allocation helpers. ***TODO*** Check for failure. +#define NEW(t,v) (v)=(t*)malloc(sizeof(t)) +#define DEL(v) {if(v) {free(v); v=NULL;}} + + +typedef struct RooNamedPointerS { + char *name; + void *pointer; +} RooNamedPointerT; + +typedef enum { + ROO_STOP_EXITING = 0, // App quit + ROO_STOP_SHUTDOWN, // Roo/E shutdown + ROO_STOP_COUNT +} RooStopReasonT; + +typedef enum { + ROO_ABORT_NONE = 0, // Don't abort stopping the app + ROO_ABORT_CANCEL, // Try to keep the app alive + ROO_ABORT_COUNT +} RooAbortReasonT; + + +int rooStartApp(const char *appName, int argc, char *argv[], RooNamedPointerT **handle); +RooAbortReasonT rooStopApp(RooNamedPointerT **appHandle, RooStopReasonT reason); + + +#endif // ROO_E_H diff --git a/include/stddclmr.h b/roo_e/stddclmr.h similarity index 100% rename from include/stddclmr.h rename to roo_e/stddclmr.h