diff --git a/doomsday/engine/api/dd_plugin.h b/doomsday/engine/api/dd_plugin.h index e5beb880b2..08346acf68 100644 --- a/doomsday/engine/api/dd_plugin.h +++ b/doomsday/engine/api/dd_plugin.h @@ -36,6 +36,7 @@ #define MAX_HOOKS 16 #define HOOKF_EXCLUSIVE 0x01000000 +typedef int (*pluginfunc_t) (void); typedef int (*hookfunc_t) (int type, int parm, void *data); // Hook types. diff --git a/doomsday/engine/portable/include/library.h b/doomsday/engine/portable/include/library.h index 99c75014f9..1a2657d306 100644 --- a/doomsday/engine/portable/include/library.h +++ b/doomsday/engine/portable/include/library.h @@ -47,6 +47,12 @@ void Library_Init(void); */ void Library_Shutdown(void); +/** + * Closes the library handles of all game plugins. The library will be + * reopened automatically when needed. + */ +void Library_ReleaseGames(void); + /** * Defines an additional library @a dir where to look for dynamic libraries. */ diff --git a/doomsday/engine/portable/src/dd_main.c b/doomsday/engine/portable/src/dd_main.c index a6b4ace58d..77925cde0d 100644 --- a/doomsday/engine/portable/src/dd_main.c +++ b/doomsday/engine/portable/src/dd_main.c @@ -1099,6 +1099,14 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) Con_ClearDatabases(); + { // Tell the plugin it is being unloaded. + void* unloader = DD_FindEntryPoint(Game_PluginId(theGame), "DP_Unload"); +#ifdef _DEBUG + Con_Message("DD_ChangeGame2: Calling DP_Unload (%p)\n", unloader); +#endif + if(unloader) ((pluginfunc_t)unloader)(); + } + // The current game is now the special "null-game". theGame = nullGame; @@ -1133,6 +1141,8 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) } ) + Library_ReleaseGames(); + if(!exchangeEntryPoints(Game_PluginId(game))) { DD_ComposeMainWindowTitle(buf); @@ -1149,6 +1159,14 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload) // This is now the current game. theGame = game; + { // Tell the plugin it is being unloaded. + void* loader = DD_FindEntryPoint(Game_PluginId(theGame), "DP_Load"); +#ifdef _DEBUG + Con_Message("DD_ChangeGame2: Calling DP_Load (%p)\n", loader); +#endif + if(loader) ((pluginfunc_t)loader)(); + } + DD_ComposeMainWindowTitle(buf); Sys_SetWindowTitle(windowIDX, buf); diff --git a/doomsday/engine/portable/src/library.c b/doomsday/engine/portable/src/library.c index b32ca00fc1..0470cbaf74 100644 --- a/doomsday/engine/portable/src/library.c +++ b/doomsday/engine/portable/src/library.c @@ -33,16 +33,20 @@ #include #include -typedef void* handle_t; +#define MAX_LIBRARIES 64 /// @todo Replace with a dynamic list. -static filename_t appDir; /// @todo Use ddstring_t -static ddstring_t* lastError; +typedef void* handle_t; struct library_s { ddstring_t* path; handle_t handle; + boolean isGamePlugin; }; +static filename_t appDir; /// @todo Use ddstring_t +static ddstring_t* lastError; +static Library* loadedLibs[MAX_LIBRARIES]; + static void getBundlePath(char* path, size_t len) { if(ArgCheckWith("-libdir", 1)) @@ -71,6 +75,34 @@ static void getBundlePath(char* path, size_t len) #endif } +static void addToLoaded(Library* lib) +{ + int i; + for(i = 0; i < MAX_LIBRARIES; ++i) + { + if(!loadedLibs[i]) + { + loadedLibs[i] = lib; + return; + } + } + assert(false); +} + +static void removeFromLoaded(Library* lib) +{ + int i; + for(i = 0; i < MAX_LIBRARIES; ++i) + { + if(loadedLibs[i] == lib) + { + loadedLibs[i] = 0; + return; + } + } + assert(false); +} + void Library_Init(void) { lastError = Str_NewStd(); @@ -81,7 +113,39 @@ void Library_Shutdown(void) { Str_Delete(lastError); lastError = 0; - /// @todo Unload all remaining libraries. + /// @todo Unload all remaining libraries? +} + +void Library_ReleaseGames(void) +{ + int i; + + for(i = 0; i < MAX_LIBRARIES; ++i) + { + Library* lib = loadedLibs[i]; + if(!lib) continue; + if(lib->isGamePlugin && lib->handle) + { +#ifdef _DEBUG + fprintf(stderr, "Library_ReleaseGames: Closing '%s'\n", Str_Text(lib->path)); +#endif + dlclose(lib->handle); + lib->handle = 0; + } + } +} + +static void reopenLibraryIfNeeded(Library* lib) +{ + assert(lib); + if(!lib->handle) + { +#ifdef _DEBUG + fprintf(stderr, "reopenLibraryIfNeeded: Opening '%s'\n", Str_Text(lib->path)); +#endif + lib->handle = dlopen(Str_Text(lib->path), RTLD_NOW); + assert(lib->handle); + } } Library* Library_New(const char *fileName) @@ -130,6 +194,17 @@ Library* Library_New(const char *fileName) lib->handle = handle; lib->path = Str_NewStd(); Str_Set(lib->path, bundlePath); + + addToLoaded(lib); + + // Symbols from game plugins conflict with each other, so we have to + // keep track of them. + /// @todo Needs a more generic way to detect the type of plugin. + if(Library_Symbol(lib, "G_RegisterGames")) + { + lib->isGamePlugin = true; + } + return lib; } @@ -141,12 +216,14 @@ void Library_Delete(Library *lib) dlclose(lib->handle); } Str_Delete(lib->path); + removeFromLoaded(lib); free(lib); } void* Library_Symbol(Library* lib, const char* symbolName) { assert(lib); + reopenLibraryIfNeeded(lib); void* ptr = dlsym(lib->handle, symbolName); if(!ptr) { diff --git a/doomsday/plugins/jdoom/api/jdoom.def b/doomsday/plugins/jdoom/api/jdoom.def index bfeb9af549..861ffba711 100644 --- a/doomsday/plugins/jdoom/api/jdoom.def +++ b/doomsday/plugins/jdoom/api/jdoom.def @@ -3,3 +3,5 @@ LIBRARY JDOOM EXPORTS DP_Initialize @1 GetGameAPI @2 + DP_Load @3 + DP_Unload @4 diff --git a/doomsday/plugins/jdoom/src/d_api.c b/doomsday/plugins/jdoom/src/d_api.c index 555eb031fb..10084a2aa3 100644 --- a/doomsday/plugins/jdoom/src/d_api.c +++ b/doomsday/plugins/jdoom/src/d_api.c @@ -245,5 +245,14 @@ game_export_t* GetGameAPI(game_import_t* imports) void DP_Initialize() { Plug_AddHook(HOOK_STARTUP, G_RegisterGames); +} + +void DP_Load(void) +{ Plug_AddHook(HOOK_VIEWPORT_RESHAPE, R_UpdateViewport); } + +void DP_Unload(void) +{ + Plug_RemoveHook(HOOK_VIEWPORT_RESHAPE, R_UpdateViewport); +}