| @@ -273,6 +273,7 @@ int main(int argc, char* argv[]) { | |||
| plugin::destroy(); | |||
| INFO("Destroying network"); | |||
| network::destroy(); | |||
| settings::destroy(); | |||
| INFO("Destroying logger"); | |||
| logger::destroy(); | |||
| @@ -13,6 +13,7 @@ namespace plugin { | |||
| PRIVATE void init(); | |||
| PRIVATE void destroy(); | |||
| PRIVATE void settingsMergeJson(json_t* rootJ); | |||
| /** Finds a loaded Plugin by slug. */ | |||
| Plugin* getPlugin(const std::string& pluginSlug); | |||
| @@ -16,3 +16,19 @@ Optional in plugins. | |||
| */ | |||
| extern "C" | |||
| void destroy(); | |||
| /** Called when saving user settings. | |||
| Stored in `settings["pluginSettings"][pluginSlug]`. | |||
| Useful for persisting plugin-wide settings. | |||
| Optional in plugins. | |||
| */ | |||
| extern "C" | |||
| json_t* settingsToJson(); | |||
| /** Called after initializing plugin if user plugin settings property is defined. | |||
| Optional in plugins. | |||
| */ | |||
| extern "C" | |||
| void settingsFromJson(json_t* rootJ); | |||
| @@ -83,6 +83,7 @@ enum BrowserSort { | |||
| }; | |||
| extern BrowserSort browserSort; | |||
| extern float browserZoom; | |||
| extern json_t* pluginSettingsJ; | |||
| struct ModuleInfo { | |||
| bool enabled = true; | |||
| @@ -110,6 +111,7 @@ extern std::map<std::string, PluginWhitelist> moduleWhitelist; | |||
| bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug); | |||
| PRIVATE void init(); | |||
| PRIVATE void destroy(); | |||
| PRIVATE json_t* toJson(); | |||
| PRIVATE void fromJson(json_t* rootJ); | |||
| PRIVATE void save(std::string path = ""); | |||
| @@ -41,6 +41,18 @@ namespace plugin { | |||
| // private API | |||
| //////////////////// | |||
| static void* getSymbol(void* handle, const char* name) { | |||
| if (!handle) | |||
| return NULL; | |||
| #if defined ARCH_WIN | |||
| return GetProcAddress((HMODULE) handle, name); | |||
| #else | |||
| return dlsym(handle, name); | |||
| #endif | |||
| } | |||
| /** Returns library handle */ | |||
| static void* loadLibrary(std::string libraryPath) { | |||
| #if defined ARCH_WIN | |||
| @@ -100,12 +112,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { | |||
| plugin->handle = loadLibrary(libraryPath); | |||
| // Get plugin's init() function | |||
| InitCallback initCallback; | |||
| #if defined ARCH_WIN | |||
| initCallback = (InitCallback) GetProcAddress((HMODULE) plugin->handle, "init"); | |||
| #else | |||
| initCallback = (InitCallback) dlsym(plugin->handle, "init"); | |||
| #endif | |||
| InitCallback initCallback = (InitCallback) getSymbol(plugin->handle, "init"); | |||
| if (!initCallback) | |||
| throw Exception("Failed to read init() symbol in %s", libraryPath.c_str()); | |||
| @@ -169,6 +176,15 @@ static Plugin* loadPlugin(std::string path) { | |||
| Plugin* existingPlugin = getPlugin(plugin->slug); | |||
| if (existingPlugin) | |||
| throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()); | |||
| // Call settingsFromJson() if exists | |||
| // Returns NULL for Core. | |||
| auto settingsFromJson = (decltype(&::settingsFromJson)) getSymbol(plugin->handle, "settingsFromJson"); | |||
| if (settingsFromJson) { | |||
| json_t* settingsJ = json_object_get(settings::pluginSettingsJ, plugin->slug.c_str()); | |||
| if (settingsJ) | |||
| settingsFromJson(settingsJ); | |||
| } | |||
| } | |||
| catch (Exception& e) { | |||
| WARN("Could not load plugin %s: %s", path.c_str(), e.what()); | |||
| @@ -265,11 +281,7 @@ static void destroyPlugin(Plugin* plugin) { | |||
| typedef void (*DestroyCallback)(); | |||
| DestroyCallback destroyCallback = NULL; | |||
| if (handle) { | |||
| #if defined ARCH_WIN | |||
| destroyCallback = (DestroyCallback) GetProcAddress((HMODULE) handle, "destroy"); | |||
| #else | |||
| destroyCallback = (DestroyCallback) dlsym(handle, "destroy"); | |||
| #endif | |||
| destroyCallback = (DestroyCallback) getSymbol(handle, "destroy"); | |||
| } | |||
| if (destroyCallback) { | |||
| try { | |||
| @@ -303,6 +315,20 @@ void destroy() { | |||
| } | |||
| void settingsMergeJson(json_t* rootJ) { | |||
| for (Plugin* plugin : plugins) { | |||
| auto settingsToJson = (decltype(&::settingsToJson)) getSymbol(plugin->handle, "settingsToJson"); | |||
| if (settingsToJson) { | |||
| json_t* settingsJ = settingsToJson(); | |||
| json_object_set_new(rootJ, plugin->slug.c_str(), settingsJ); | |||
| } | |||
| else { | |||
| json_object_del(rootJ, plugin->slug.c_str()); | |||
| } | |||
| } | |||
| } | |||
| /** Given slug => fallback slug. | |||
| Correctly handles bidirectional fallbacks. | |||
| To request fallback slugs to be added to this list, open a GitHub issue. | |||
| @@ -61,6 +61,7 @@ int tipIndex = -1; | |||
| bool discordUpdateActivity = true; | |||
| BrowserSort browserSort = BROWSER_SORT_UPDATED; | |||
| float browserZoom = -1.f; | |||
| json_t* pluginSettingsJ = NULL; | |||
| std::map<std::string, std::map<std::string, ModuleInfo>> moduleInfos; | |||
| std::map<std::string, PluginWhitelist> moduleWhitelist; | |||
| @@ -98,6 +99,12 @@ void init() { | |||
| } | |||
| void destroy() { | |||
| if (pluginSettingsJ) | |||
| json_decref(pluginSettingsJ); | |||
| } | |||
| json_t* toJson() { | |||
| json_t* rootJ = json_object(); | |||
| @@ -176,6 +183,13 @@ json_t* toJson() { | |||
| json_object_set_new(rootJ, "browserZoom", json_real(browserZoom)); | |||
| // Merge pluginSettings instead of replace so plugins that fail to load don't cause their settings to be deleted. | |||
| if (!pluginSettingsJ) | |||
| pluginSettingsJ = json_object(); | |||
| plugin::settingsMergeJson(pluginSettingsJ); | |||
| // Don't use *_set_new() here because we need to keep the reference to pluginSettingsJ. | |||
| json_object_set(rootJ, "pluginSettings", pluginSettingsJ); | |||
| // moduleInfos | |||
| json_t* moduleInfosJ = json_object(); | |||
| for (const auto& pluginPair : moduleInfos) { | |||
| @@ -375,6 +389,15 @@ void fromJson(json_t* rootJ) { | |||
| if (browserZoomJ) | |||
| browserZoom = json_number_value(browserZoomJ); | |||
| // Delete previous pluginSettings object | |||
| if (pluginSettingsJ) { | |||
| json_decref(pluginSettingsJ); | |||
| pluginSettingsJ = NULL; | |||
| } | |||
| pluginSettingsJ = json_object_get(rootJ, "pluginSettings"); | |||
| if (pluginSettingsJ) | |||
| json_incref(pluginSettingsJ); | |||
| moduleInfos.clear(); | |||
| json_t* moduleInfosJ = json_object_get(rootJ, "moduleInfos"); | |||
| if (moduleInfosJ) { | |||