From f76ecade7ede024d19857e1dd74939193eef895c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Wed, 27 May 2026 21:16:07 +0200 Subject: [PATCH] script: track per-script theme contributions and purge on script unload Make individual scripts the unit of ownership for theme contributions so that loading a script that calls weechat.theme_register(...) and later unloading it correctly removes the script's overrides. Plugin API addition (weechat-plugin.h): - new function pointer t_weechat_plugin.theme_unregister_script delegates to core's theme_unregister_script. - new convenience macro weechat_theme_unregister_script(script). - WEECHAT_PLUGIN_API_VERSION bumped to 20260527-02. Script API additions (plugin-script-api.{c,h}): - new plugin_script_api_theme_register (plugin, script, name, overrides) forwards to the plugin API with the script pointer as the contribution owner, so contributions are tracked per-script (not per script-language plugin). Lifecycle wiring (plugin-script.c): - new file-local plugin_script_remove_themes (plugin, script) calls weechat_theme_unregister_script. - plugin_script_remove now calls it alongside the other plugin_script_remove_* helpers, so script-unload tears down everything (configs, bar items, themes, hooks). Eight language bindings updated to call plugin_script_api_theme_register instead of weechat_theme_register directly, so they pass the script pointer as owner: - python, perl, ruby, lua, tcl, javascript, php, guile. The behavior change is end-to-end visible only at script unload: before, an unloaded script's theme overrides lingered in the registry forever and would be re-applied on the next /theme apply; after, they vanish when the script unloads. /plugin unload of an entire script-language plugin already worked via commit 24's hook in plugin_remove (which drops both plugin-only contributions and their script-owned children when the language plugin itself goes away, because the contribution's plugin pointer matches). No new unit tests in this commit: the underlying theme_unregister_script function is covered by TEST(CoreTheme, UnregisterByOwner), and the remaining changes are plumbing. --- src/plugins/guile/weechat-guile-api.c | 6 ++++-- src/plugins/javascript/weechat-js-api.cpp | 5 ++++- src/plugins/lua/weechat-lua-api.c | 5 ++++- src/plugins/perl/weechat-perl-api.c | 5 ++++- src/plugins/php/weechat-php-api.c | 6 ++++-- src/plugins/plugin-script-api.c | 22 ++++++++++++++++++++++ src/plugins/plugin-script-api.h | 4 ++++ src/plugins/plugin-script.c | 14 ++++++++++++++ src/plugins/plugin.c | 1 + src/plugins/python/weechat-python-api.c | 5 ++++- src/plugins/ruby/weechat-ruby-api.c | 5 ++++- src/plugins/tcl/weechat-tcl-api.c | 5 ++++- src/plugins/weechat-plugin.h | 6 +++++- 13 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/plugins/guile/weechat-guile-api.c b/src/plugins/guile/weechat-guile-api.c index ba7863992..4bc53a18d 100644 --- a/src/plugins/guile/weechat-guile-api.c +++ b/src/plugins/guile/weechat-guile-api.c @@ -2019,8 +2019,10 @@ weechat_guile_api_theme_register (SCM name, SCM overrides) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (API_SCM_TO_STRING(name), - c_overrides)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_guile_plugin, + guile_current_script, + API_SCM_TO_STRING(name), c_overrides)); weechat_hashtable_free (c_overrides); diff --git a/src/plugins/javascript/weechat-js-api.cpp b/src/plugins/javascript/weechat-js-api.cpp index 5d69d2ac7..d7e4b119e 100644 --- a/src/plugins/javascript/weechat-js-api.cpp +++ b/src/plugins/javascript/weechat-js-api.cpp @@ -1912,7 +1912,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (*name, hashtable)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_js_plugin, + js_current_script, + *name, hashtable)); if (hashtable) weechat_hashtable_free (hashtable); diff --git a/src/plugins/lua/weechat-lua-api.c b/src/plugins/lua/weechat-lua-api.c index 3ae79952b..519dff6d3 100644 --- a/src/plugins/lua/weechat-lua-api.c +++ b/src/plugins/lua/weechat-lua-api.c @@ -2097,7 +2097,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (name, hashtable)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_lua_plugin, + lua_current_script, + name, hashtable)); weechat_hashtable_free (hashtable); diff --git a/src/plugins/perl/weechat-perl-api.c b/src/plugins/perl/weechat-perl-api.c index 9c19676a7..7e806e251 100644 --- a/src/plugins/perl/weechat-perl-api.c +++ b/src/plugins/perl/weechat-perl-api.c @@ -2017,7 +2017,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (name, hashtable)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_perl_plugin, + perl_current_script, + name, hashtable)); weechat_hashtable_free (hashtable); diff --git a/src/plugins/php/weechat-php-api.c b/src/plugins/php/weechat-php-api.c index c461c077a..d84dce1af 100644 --- a/src/plugins/php/weechat-php-api.c +++ b/src/plugins/php/weechat-php-api.c @@ -2194,8 +2194,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register ((const char *)name, - overrides)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_php_plugin, + php_current_script, + (const char *)name, overrides)); weechat_hashtable_free (overrides); diff --git a/src/plugins/plugin-script-api.c b/src/plugins/plugin-script-api.c index a4aa7ece4..3cfe21d35 100644 --- a/src/plugins/plugin-script-api.c +++ b/src/plugins/plugin-script-api.c @@ -1568,6 +1568,28 @@ plugin_script_api_config_unset_plugin (struct t_weechat_plugin *weechat_plugin, return return_code; } +/* + * Register theme overrides on behalf of a script. + * + * Forwards to weechat_plugin->theme_register with the script pointer + * as the owner, so the contribution can be auto-purged when the + * script unloads (see plugin_script_remove_themes). + */ + +struct t_theme * +plugin_script_api_theme_register (struct t_weechat_plugin *weechat_plugin, + struct t_plugin_script *script, + const char *name, + struct t_hashtable *overrides) +{ + if (!script) + return NULL; + return (weechat_plugin->theme_register) (weechat_plugin, + script, + name, + overrides); +} + /* * Create an upgrade file. * diff --git a/src/plugins/plugin-script-api.h b/src/plugins/plugin-script-api.h index 7378e3af2..96949c29e 100644 --- a/src/plugins/plugin-script-api.h +++ b/src/plugins/plugin-script-api.h @@ -438,6 +438,10 @@ extern void plugin_script_api_config_set_desc_plugin (struct t_weechat_plugin *w extern int plugin_script_api_config_unset_plugin (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *script, const char *option); +extern struct t_theme *plugin_script_api_theme_register (struct t_weechat_plugin *weechat_plugin, + struct t_plugin_script *script, + const char *name, + struct t_hashtable *overrides); extern struct t_upgrade_file *plugin_script_api_upgrade_new (struct t_weechat_plugin *weechat_plugin, struct t_plugin_script *script, const char *filename, diff --git a/src/plugins/plugin-script.c b/src/plugins/plugin-script.c index 65db1c90a..aaa31f1d7 100644 --- a/src/plugins/plugin-script.c +++ b/src/plugins/plugin-script.c @@ -1080,6 +1080,18 @@ plugin_script_free (struct t_plugin_script *script) free (script); } +/* + * Remove all theme contributions registered by this script. + */ + +void +plugin_script_remove_themes (struct t_weechat_plugin *weechat_plugin, + struct t_plugin_script *script) +{ + weechat_theme_unregister_script (script); + (void) weechat_plugin; /* used implicitly via the macro */ +} + /* * Remove a script from list of scripts. */ @@ -1101,6 +1113,8 @@ plugin_script_remove (struct t_weechat_plugin *weechat_plugin, plugin_script_remove_configs (weechat_plugin, script); + plugin_script_remove_themes (weechat_plugin, script); + /* * remove again all hooks created by this script (just in case new hooks * were created by the calls above) diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 20f84c44f..3da0f00e5 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -801,6 +801,7 @@ plugin_load (const char *filename, int init_plugin, int argc, char **argv) new_plugin->config_unset_plugin = &plugin_api_config_unset_plugin; new_plugin->theme_register = &theme_register; + new_plugin->theme_unregister_script = &theme_unregister_script; new_plugin->key_bind = &gui_key_bind_plugin; new_plugin->key_unbind = &gui_key_unbind_plugin; diff --git a/src/plugins/python/weechat-python-api.c b/src/plugins/python/weechat-python-api.c index 6745a3511..e3fbeeebd 100644 --- a/src/plugins/python/weechat-python-api.c +++ b/src/plugins/python/weechat-python-api.c @@ -2019,7 +2019,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (name, hashtable)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_python_plugin, + python_current_script, + name, hashtable)); weechat_hashtable_free (hashtable); diff --git a/src/plugins/ruby/weechat-ruby-api.c b/src/plugins/ruby/weechat-ruby-api.c index b7ba1805d..b1d93b7a1 100644 --- a/src/plugins/ruby/weechat-ruby-api.c +++ b/src/plugins/ruby/weechat-ruby-api.c @@ -2498,7 +2498,10 @@ weechat_ruby_api_theme_register (VALUE class, VALUE name, VALUE overrides) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (c_name, c_overrides)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_ruby_plugin, + ruby_current_script, + c_name, c_overrides)); weechat_hashtable_free (c_overrides); diff --git a/src/plugins/tcl/weechat-tcl-api.c b/src/plugins/tcl/weechat-tcl-api.c index 816126274..75d366386 100644 --- a/src/plugins/tcl/weechat-tcl-api.c +++ b/src/plugins/tcl/weechat-tcl-api.c @@ -2051,7 +2051,10 @@ API_FUNC(theme_register) WEECHAT_HASHTABLE_STRING, WEECHAT_HASHTABLE_STRING); - result = API_PTR2STR(weechat_theme_register (name, hashtable)); + result = API_PTR2STR(plugin_script_api_theme_register ( + weechat_tcl_plugin, + tcl_current_script, + name, hashtable)); weechat_hashtable_free (hashtable); diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index d11bdb289..1dd3bc1fc 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -77,7 +77,7 @@ struct t_weelist_item; * please change the date with current one; for a second change at same * date, increment the 01, otherwise please keep 01. */ -#define WEECHAT_PLUGIN_API_VERSION "20260531-01" +#define WEECHAT_PLUGIN_API_VERSION "20260531-02" /* macros for defining plugin infos */ #define WEECHAT_PLUGIN_NAME(__name) \ @@ -734,6 +734,8 @@ struct t_weechat_plugin const void *script, const char *name, struct t_hashtable *overrides); + void (*theme_unregister_script) (struct t_weechat_plugin *plugin, + const void *script); /* key bindings */ int (*key_bind) (const char *context, struct t_hashtable *keys); @@ -1866,6 +1868,8 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin); #define weechat_theme_register(__name, __overrides) \ (weechat_plugin->theme_register)(weechat_plugin, NULL, \ __name, __overrides) +#define weechat_theme_unregister_script(__script) \ + (weechat_plugin->theme_unregister_script)(weechat_plugin, __script) /* key bindings */ #define weechat_key_bind(__context, __keys) \