From 4ffd62b2065358ad4aad702d916b0d717453824b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Thu, 7 Mar 2024 07:13:39 +0100 Subject: [PATCH] core: add unique "id" in buffer (issue #2081) The id is a "long long" variable with the current time (microseconds precision). It is guaranteed to be unique for all buffers, and the same number is never used again, during the lifetime of the process. It persists and is unchanged after `/upgrade`. --- ChangeLog.adoc | 1 + doc/en/weechat_plugin_api.en.adoc | 1 + doc/fr/weechat_plugin_api.fr.adoc | 1 + doc/it/weechat_plugin_api.it.adoc | 2 + doc/ja/weechat_plugin_api.ja.adoc | 2 + doc/sr/weechat_plugin_api.sr.adoc | 2 + src/core/wee-upgrade.c | 39 ++++++++-- src/gui/gui-buffer.c | 118 ++++++++++++++++++++++++----- src/gui/gui-buffer.h | 19 +++++ tests/scripts/python/testapi.py | 4 +- tests/unit/gui/test-gui-buffer.cpp | 18 +++++ 11 files changed, 181 insertions(+), 26 deletions(-) diff --git a/ChangeLog.adoc b/ChangeLog.adoc index cc2181036..9ce6ed2cf 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -15,6 +15,7 @@ For a list of important changes that require manual actions, please look at rele New features:: + * core: add unique identifier "id" in buffer (issue #2081) * core: add option `malloc_trim` in command `/sys` * core: add support of SGR mouse events (issue #2082) * core: reintroduce help on the variables and operators in `/help eval` (issue #2005) diff --git a/doc/en/weechat_plugin_api.en.adoc b/doc/en/weechat_plugin_api.en.adoc index 4ef7eb658..9a3b67636 100644 --- a/doc/en/weechat_plugin_api.en.adoc +++ b/doc/en/weechat_plugin_api.en.adoc @@ -14639,6 +14639,7 @@ Arguments: * _buffer_: buffer pointer * _property_: property name: +** _id_: unique identifier _(WeeChat ≥ 4.3.0)_ ** _plugin_: name of plugin which created this buffer ("core" for WeeChat main buffer) ** _name_: name of buffer ** _full_name_: full name of buffer ("plugin.name") _(WeeChat ≥ 0.3.7)_ diff --git a/doc/fr/weechat_plugin_api.fr.adoc b/doc/fr/weechat_plugin_api.fr.adoc index f8afcae20..18d18b049 100644 --- a/doc/fr/weechat_plugin_api.fr.adoc +++ b/doc/fr/weechat_plugin_api.fr.adoc @@ -14955,6 +14955,7 @@ Paramètres : * _buffer_ : pointeur vers le tampon * _property_ : nom de la propriété : +** _id_ : identifiant unique _(WeeChat ≥ 4.3.0)_ ** _plugin_ : nom de l'extension qui a créé ce tampon ("core" pour le tampon principal WeeChat) ** _name_ : nom du tampon ** _full_name_ : nom complet du tampon ("extension.nom") _(WeeChat ≥ 0.3.7)_ diff --git a/doc/it/weechat_plugin_api.it.adoc b/doc/it/weechat_plugin_api.it.adoc index ac20083bc..ba611b731 100644 --- a/doc/it/weechat_plugin_api.it.adoc +++ b/doc/it/weechat_plugin_api.it.adoc @@ -15327,6 +15327,8 @@ Argomenti: * _buffer_: puntatore al buffer * _property_: nome proprietà: +// TRANSLATION MISSING +** _id_: unique identifier _(WeeChat ≥ 4.3.0)_ ** _plugin_: nome del plugin che ha creato questo buffer ("core" per il buffer principale di WeeChat) ** _name_: nome del buffer ** _full_name_: nome completo del buffer ("plugin.nome") _(WeeChat ≥ 0.3.7)_ diff --git a/doc/ja/weechat_plugin_api.ja.adoc b/doc/ja/weechat_plugin_api.ja.adoc index 3822fbb67..6c6461782 100644 --- a/doc/ja/weechat_plugin_api.ja.adoc +++ b/doc/ja/weechat_plugin_api.ja.adoc @@ -14861,6 +14861,8 @@ const char *weechat_buffer_get_string (struct t_gui_buffer *buffer, * _buffer_: バッファへのポインタ * _property_: プロパティ名: +// TRANSLATION MISSING +** _id_: unique identifier _(WeeChat ≥ 4.3.0)_ ** _plugin_: バッファを作成したプラグインの名前 ("core" は WeeChat メインバッファ) ** _name_: バッファの名前 ** _full_name_: バッファの完全な名前 ("plugin.name") _(WeeChat バージョン 0.3.7 以上で利用可)_ diff --git a/doc/sr/weechat_plugin_api.sr.adoc b/doc/sr/weechat_plugin_api.sr.adoc index eae7b8cc4..10bf970a6 100644 --- a/doc/sr/weechat_plugin_api.sr.adoc +++ b/doc/sr/weechat_plugin_api.sr.adoc @@ -14222,6 +14222,8 @@ const char *weechat_buffer_get_string (struct t_gui_buffer *buffer, * _buffer_: показивач на бафер * _property_: име особине: +// TRANSLATION MISSING +** _id_: unique identifier _(WeeChat ≥ 4.3.0)_ ** _plugin_: име додатка који је креирао овај бафер („core” за главни бафер програма WeeChat) ** _name_: име бафера ** _full_name_: пуно име бафера („додатак.име”) _(WeeChat ≥ 0.3.7)_ diff --git a/src/core/wee-upgrade.c b/src/core/wee-upgrade.c index 91920f37d..05c1b9a50 100644 --- a/src/core/wee-upgrade.c +++ b/src/core/wee-upgrade.c @@ -405,10 +405,27 @@ void upgrade_weechat_read_buffer (struct t_infolist *infolist) { struct t_gui_buffer *ptr_buffer; - const char *key, *var_name, *name, *plugin_name; + const char *key, *var_name, *name, *plugin_name, *ptr_id; const char *str; - char option_name[64], *option_key, *option_var; + char option_name[64], *option_key, *option_var, *error; int index, length, main_buffer; + long long id; + + /* "id" is new in WeeChat 4.3.0 */ + id = -1; + if (infolist_search_var (infolist, "id")) + { + ptr_id = infolist_string (infolist, "id"); + if (ptr_id) + { + error = NULL; + id = strtoll (ptr_id, &error, 10); + if (!error || error[0]) + id = -1; + } + } + if (id < 0) + id = gui_buffer_generate_id (); plugin_name = infolist_string (infolist, "plugin_name"); name = infolist_string (infolist, "name"); @@ -420,19 +437,25 @@ upgrade_weechat_read_buffer (struct t_infolist *infolist) { /* use WeeChat main buffer */ upgrade_current_buffer = gui_buffers; + upgrade_current_buffer->id = id; } else { /* create buffer it it's not main buffer */ - upgrade_current_buffer = gui_buffer_new (NULL, - infolist_string (infolist, - "name"), - NULL, NULL, NULL, - NULL, NULL, NULL); + upgrade_current_buffer = gui_buffer_new_props_with_id ( + id, + NULL, /* plugin */ + infolist_string (infolist, "name"), + NULL, /* properties */ + NULL, NULL, NULL, /* input callback */ + NULL, NULL, NULL); /* close callback */ } if (!upgrade_current_buffer) return; + if (upgrade_current_buffer->id > gui_buffer_last_id_assigned) + gui_buffer_last_id_assigned = upgrade_current_buffer->id; + ptr_buffer = upgrade_current_buffer; if (infolist_integer (infolist, "current_buffer")) @@ -799,6 +822,8 @@ upgrade_weechat_read_cb (const void *pointer, void *data, (void) data; (void) upgrade_file; + gui_buffer_last_id_assigned = -1; + infolist_reset_item_cursor (infolist); while (infolist_next (infolist)) { diff --git a/src/gui/gui-buffer.c b/src/gui/gui-buffer.c index 3da3088b5..8ad9949a0 100644 --- a/src/gui/gui-buffer.c +++ b/src/gui/gui-buffer.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "../core/weechat.h" @@ -75,6 +76,8 @@ int gui_buffers_visited_count = 0; /* number of visited buffers*/ int gui_buffers_visited_frozen = 0; /* 1 to forbid list updates */ struct t_gui_buffer *gui_buffer_last_displayed = NULL; /* last b. displayed */ +long long gui_buffer_last_id_assigned = -1; /* last id assigned */ + char *gui_buffer_reserved_names[] = { GUI_BUFFER_MAIN, SECURE_BUFFER_NAME, GUI_COLOR_BUFFER_NAME, NULL @@ -106,7 +109,7 @@ char *gui_buffer_properties_get_integer[] = NULL }; char *gui_buffer_properties_get_string[] = -{ "plugin", "name", "full_name", "old_full_name", "short_name", "title", +{ "id", "plugin", "name", "full_name", "old_full_name", "short_name", "title", "input", "text_search_input", "highlight_words", "highlight_disable_regex", "highlight_regex", "highlight_tags_restrict", "highlight_tags", "hotlist_max_level_nicks", @@ -605,6 +608,34 @@ gui_buffer_insert (struct t_gui_buffer *buffer) } } +/* + * Returns a new unique id for a buffer. + * + * The id is the current time with microseconds precision. + * The same time (including microseconds) can be used only one time, so that + * all buffer ids are guaranteed to be unique. + */ + +long long +gui_buffer_generate_id () +{ + struct timeval tv; + long long id; + + gettimeofday (&tv, NULL); + + id = ((long long)tv.tv_sec * 1000000LL) + (long long)(tv.tv_usec); + + /* + * ensure we never use the same id for two buffers and that the returned + * id is strictly greater than the last assigned one + */ + if (id <= gui_buffer_last_id_assigned) + id = gui_buffer_last_id_assigned + 1; + + return id; +} + /* * Initializes input_buffer_* variables in a buffer. */ @@ -746,26 +777,28 @@ gui_buffer_apply_config_properties (struct t_gui_buffer *buffer) } /* - * Creates a new buffer in current window with some optional properties. + * Creates a new buffer in current window with some optional properties, + * using id (this function is for internal use only). * * Returns pointer to new buffer, NULL if error. */ struct t_gui_buffer * -gui_buffer_new_props (struct t_weechat_plugin *plugin, - const char *name, - struct t_hashtable *properties, - int (*input_callback)(const void *pointer, - void *data, - struct t_gui_buffer *buffer, - const char *input_data), - const void *input_callback_pointer, - void *input_callback_data, - int (*close_callback)(const void *pointer, - void *data, - struct t_gui_buffer *buffer), - const void *close_callback_pointer, - void *close_callback_data) +gui_buffer_new_props_with_id (long long id, + struct t_weechat_plugin *plugin, + const char *name, + struct t_hashtable *properties, + int (*input_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer, + const char *input_data), + const void *input_callback_pointer, + void *input_callback_data, + int (*close_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer), + const void *close_callback_pointer, + void *close_callback_data) { struct t_gui_buffer *new_buffer; int first_buffer_creation; @@ -797,6 +830,9 @@ gui_buffer_new_props (struct t_weechat_plugin *plugin, return NULL; /* init buffer */ + new_buffer->id = id; + if (new_buffer->id > gui_buffer_last_id_assigned) + gui_buffer_last_id_assigned = new_buffer->id; new_buffer->opening = 1; new_buffer->plugin = plugin; new_buffer->plugin_name_for_upgrade = NULL; @@ -971,6 +1007,41 @@ gui_buffer_new_props (struct t_weechat_plugin *plugin, return new_buffer; } +/* + * Creates a new buffer in current window with some optional properties. + * + * Returns pointer to new buffer, NULL if error. + */ + +struct t_gui_buffer * +gui_buffer_new_props (struct t_weechat_plugin *plugin, + const char *name, + struct t_hashtable *properties, + int (*input_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer, + const char *input_data), + const void *input_callback_pointer, + void *input_callback_data, + int (*close_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer), + const void *close_callback_pointer, + void *close_callback_data) +{ + return gui_buffer_new_props_with_id ( + gui_buffer_generate_id (), + plugin, + name, + properties, + input_callback, + input_callback_pointer, + input_callback_data, + close_callback, + close_callback_pointer, + close_callback_data); +} + /* * Creates a new buffer in current window. * @@ -1441,11 +1512,17 @@ const char * gui_buffer_get_string (struct t_gui_buffer *buffer, const char *property) { const char *ptr_value; + static char str_value[64]; if (!buffer || !property) return NULL; - if (strcmp (property, "plugin") == 0) + if (strcmp (property, "id") == 0) + { + snprintf (str_value, sizeof (str_value), "%lld", buffer->id); + return str_value; + } + else if (strcmp (property, "plugin") == 0) return gui_buffer_get_plugin_name (buffer); else if (strcmp (property, "name") == 0) return buffer->name; @@ -5012,6 +5089,7 @@ gui_buffer_hdata_buffer_cb (const void *pointer, void *data, 0, 0, NULL, NULL); if (hdata) { + HDATA_VAR(struct t_gui_buffer, id, LONGLONG, 0, NULL, NULL); HDATA_VAR(struct t_gui_buffer, opening, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_buffer, plugin, POINTER, 0, NULL, "plugin"); HDATA_VAR(struct t_gui_buffer, plugin_name_for_upgrade, STRING, 0, NULL, NULL); @@ -5183,7 +5261,7 @@ gui_buffer_add_to_infolist (struct t_infolist *infolist, { struct t_infolist_item *ptr_item; struct t_gui_key *ptr_key; - char option_name[64]; + char option_name[64], str_value[64]; int i; if (!infolist || !buffer) @@ -5198,6 +5276,9 @@ gui_buffer_add_to_infolist (struct t_infolist *infolist, if (!infolist_new_var_integer (ptr_item, "current_buffer", (gui_current_window->buffer == buffer) ? 1 : 0)) return 0; + snprintf (str_value, sizeof (str_value), "%lld", buffer->id); + if (!infolist_new_var_string (ptr_item, "id", str_value)) + return 0; if (!infolist_new_var_integer (ptr_item, "opening", buffer->opening)) return 0; if (!infolist_new_var_pointer (ptr_item, "plugin", buffer->plugin)) @@ -5457,6 +5538,7 @@ gui_buffer_print_log () { log_printf (""); log_printf ("[buffer (addr:0x%lx)]", ptr_buffer); + log_printf (" id. . . . . . . . . . . : %lld", ptr_buffer->id); log_printf (" opening . . . . . . . . : %d", ptr_buffer->opening); log_printf (" plugin. . . . . . . . . : 0x%lx ('%s')", ptr_buffer->plugin, gui_buffer_get_plugin_name (ptr_buffer)); diff --git a/src/gui/gui-buffer.h b/src/gui/gui-buffer.h index c97947e72..b8a20e301 100644 --- a/src/gui/gui-buffer.h +++ b/src/gui/gui-buffer.h @@ -98,6 +98,8 @@ struct t_gui_input_undo struct t_gui_buffer { + long long id; /* unique id for buffer */ + /* (timestamp with microseconds) */ int opening; /* 1 if buffer is being opened */ struct t_weechat_plugin *plugin; /* plugin which created this buffer */ /* (NULL for a WeeChat buffer) */ @@ -276,6 +278,7 @@ extern int gui_buffers_visited_index; extern int gui_buffers_visited_count; extern int gui_buffers_visited_frozen; extern struct t_gui_buffer *gui_buffer_last_displayed; +extern long long gui_buffer_last_id_assigned; extern char *gui_buffer_reserved_names[]; extern char *gui_buffer_type_string[]; extern char *gui_buffer_notify_string[]; @@ -300,9 +303,25 @@ extern void gui_buffer_local_var_add (struct t_gui_buffer *buffer, extern void gui_buffer_local_var_remove (struct t_gui_buffer *buffer, const char *name); extern void gui_buffer_notify_set_all (); +extern long long gui_buffer_generate_id (); extern int gui_buffer_is_reserved_name (const char *name); extern void gui_buffer_apply_config_option_property (struct t_gui_buffer *buffer, struct t_config_option *option); +extern struct t_gui_buffer *gui_buffer_new_props_with_id (long long id, + struct t_weechat_plugin *plugin, + const char *name, + struct t_hashtable *properties, + int (*input_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer, + const char *input_data), + const void *input_callback_pointer, + void *input_callback_data, + int (*close_callback)(const void *pointer, + void *data, + struct t_gui_buffer *buffer), + const void *close_callback_pointer, + void *close_callback_data); extern struct t_gui_buffer *gui_buffer_new_props (struct t_weechat_plugin *plugin, const char *name, struct t_hashtable *properties, diff --git a/tests/scripts/python/testapi.py b/tests/scripts/python/testapi.py index de3023bb3..920b8ddc3 100644 --- a/tests/scripts/python/testapi.py +++ b/tests/scripts/python/testapi.py @@ -705,7 +705,7 @@ def test_hdata(): line2 = weechat.hdata_pointer(hdata_line, line1, 'next_line') line3 = weechat.hdata_pointer(hdata_line, line2, 'next_line') # hdata_get_var_offset - check(weechat.hdata_get_var_offset(hdata_buffer, 'opening') == 0) + check(weechat.hdata_get_var_offset(hdata_buffer, 'id') == 0) check(weechat.hdata_get_var_offset(hdata_buffer, 'plugin') > 0) # hdata_get_var_type_string check(weechat.hdata_get_var_type_string(hdata_buffer, 'plugin') == 'pointer') @@ -751,6 +751,8 @@ def test_hdata(): weechat.buffer_set(buffer, 'hotlist', weechat.WEECHAT_HOTLIST_MESSAGE) gui_hotlist = weechat.hdata_get_list(hdata_hotlist, 'gui_hotlist') check(weechat.hdata_long(hdata_hotlist, gui_hotlist, 'creation_time.tv_usec') >= 0) + # hdata_longlong + check(weechat.hdata_longlong(hdata_buffer, buffer2, 'id') > 1708874542000000) # hdata_string check(weechat.hdata_string(hdata_buffer, buffer2, 'name') == 'test') # hdata_pointer diff --git a/tests/unit/gui/test-gui-buffer.cpp b/tests/unit/gui/test-gui-buffer.cpp index 66a219c66..b61dc1352 100644 --- a/tests/unit/gui/test-gui-buffer.cpp +++ b/tests/unit/gui/test-gui-buffer.cpp @@ -383,6 +383,23 @@ TEST(GuiBuffer, Insert) /* TODO: write tests */ } +/* + * Tests functions: + * gui_buffer_generate_id + */ + +TEST(GuiBuffer, GenerateId) +{ + long long id; + + id = gui_buffer_generate_id (); + CHECK(id > gui_buffer_last_id_assigned); + id = gui_buffer_generate_id (); + CHECK(id > gui_buffer_last_id_assigned); + id = gui_buffer_generate_id (); + CHECK(id > gui_buffer_last_id_assigned); +} + /* * Tests functions: * gui_buffer_input_buffer_init @@ -492,6 +509,7 @@ test_buffer_close_cb (const void *pointer, void *data, /* * Tests functions: + * gui_buffer_new_props_with_id * gui_buffer_new_props */