diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2021-07-02 21:54:27 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2021-07-04 13:27:33 +0200 |
commit | 954f943e8e058ae1db708857ef53806abedbaea9 (patch) | |
tree | ff725c8099a6d93b40dea71312fb4629c315b758 /src | |
parent | bba300e1914302ee72818ad49ecc45fb76afc9f1 (diff) | |
download | weechat-954f943e8e058ae1db708857ef53806abedbaea9.zip |
irc, typing: display typing status for IRC nicks
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/irc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/irc/Makefile.am | 2 | ||||
-rw-r--r-- | src/plugins/irc/irc-channel.c | 10 | ||||
-rw-r--r-- | src/plugins/irc/irc-channel.h | 18 | ||||
-rw-r--r-- | src/plugins/irc/irc-config.c | 21 | ||||
-rw-r--r-- | src/plugins/irc/irc-config.h | 3 | ||||
-rw-r--r-- | src/plugins/irc/irc-protocol.c | 119 | ||||
-rw-r--r-- | src/plugins/irc/irc-protocol.h | 8 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.c | 30 | ||||
-rw-r--r-- | src/plugins/irc/irc-typing.c | 159 | ||||
-rw-r--r-- | src/plugins/irc/irc-typing.h | 35 | ||||
-rw-r--r-- | src/plugins/irc/irc.c | 52 | ||||
-rw-r--r-- | src/plugins/typing/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/typing/Makefile.am | 2 | ||||
-rw-r--r-- | src/plugins/typing/typing-bar-item.c | 120 | ||||
-rw-r--r-- | src/plugins/typing/typing-bar-item.h | 27 | ||||
-rw-r--r-- | src/plugins/typing/typing-config.c | 50 | ||||
-rw-r--r-- | src/plugins/typing/typing-config.h | 7 | ||||
-rw-r--r-- | src/plugins/typing/typing-status.c | 280 | ||||
-rw-r--r-- | src/plugins/typing/typing-status.h | 39 | ||||
-rw-r--r-- | src/plugins/typing/typing.c | 315 |
21 files changed, 1082 insertions, 217 deletions
diff --git a/src/plugins/irc/CMakeLists.txt b/src/plugins/irc/CMakeLists.txt index c638605e7..e037ef138 100644 --- a/src/plugins/irc/CMakeLists.txt +++ b/src/plugins/irc/CMakeLists.txt @@ -43,6 +43,7 @@ add_library(irc MODULE irc-sasl.c irc-sasl.h irc-server.c irc-server.h irc-tag.c irc-tag.h + irc-typing.c irc-typing.h irc-upgrade.c irc-upgrade.h ) set_target_properties(irc PROPERTIES PREFIX "") diff --git a/src/plugins/irc/Makefile.am b/src/plugins/irc/Makefile.am index fa5833350..324bdd802 100644 --- a/src/plugins/irc/Makefile.am +++ b/src/plugins/irc/Makefile.am @@ -73,6 +73,8 @@ irc_la_SOURCES = irc.c \ irc-server.h \ irc-tag.c \ irc-tag.h \ + irc-typing.c \ + irc-typing.h \ irc-upgrade.c \ irc-upgrade.h diff --git a/src/plugins/irc/irc-channel.c b/src/plugins/irc/irc-channel.c index 5dfa4163c..36e79c5a5 100644 --- a/src/plugins/irc/irc-channel.c +++ b/src/plugins/irc/irc-channel.c @@ -40,8 +40,8 @@ #include "irc-input.h" -char *irc_channel_typing_status_string[IRC_CHANNEL_NUM_TYPING_STATUSES] = -{ "off", "typing", "paused", "done" }; +char *irc_channel_typing_state_string[IRC_CHANNEL_NUM_TYPING_STATES] = +{ "off", "active", "paused", "done" }; /* default CHANTYPES */ char *irc_channel_default_chantypes = "#&"; @@ -511,7 +511,7 @@ irc_channel_new (struct t_irc_server *server, int channel_type, irc_modelist_new (new_channel, ptr_chanmode[0]); } new_channel->join_smart_filtered = NULL; - new_channel->typing_status = IRC_CHANNEL_TYPING_STATUS_OFF; + new_channel->typing_state = IRC_CHANNEL_TYPING_STATE_OFF; new_channel->typing_status_sent = 0; new_channel->buffer = ptr_buffer; new_channel->buffer_as_string = NULL; @@ -1589,7 +1589,7 @@ irc_channel_hdata_channel_cb (const void *pointer, void *data, WEECHAT_HDATA_VAR(struct t_irc_channel, modelists, POINTER, 0, NULL, "irc_modelist"); WEECHAT_HDATA_VAR(struct t_irc_channel, last_modelist, POINTER, 0, NULL, "irc_modelist"); WEECHAT_HDATA_VAR(struct t_irc_channel, join_smart_filtered, HASHTABLE, 0, NULL, NULL); - WEECHAT_HDATA_VAR(struct t_irc_channel, typing_status, INTEGER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_channel, typing_state, INTEGER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_channel, typing_status_sent, TIME, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_channel, buffer, POINTER, 0, NULL, "buffer"); WEECHAT_HDATA_VAR(struct t_irc_channel, buffer_as_string, STRING, 0, NULL, NULL); @@ -1781,7 +1781,7 @@ irc_channel_print_log (struct t_irc_channel *channel) channel->join_smart_filtered, weechat_hashtable_get_string (channel->join_smart_filtered, "keys_values")); - weechat_log_printf (" typing_status. . . . . . : %d", channel->typing_status); + weechat_log_printf (" typing_state . . . . . . : %d", channel->typing_state); weechat_log_printf (" typing_status_sent . . . : %lld", (long long)channel->typing_status_sent); weechat_log_printf (" buffer . . . . . . . . . : 0x%lx", channel->buffer); weechat_log_printf (" buffer_as_string . . . . : '%s'", channel->buffer_as_string); diff --git a/src/plugins/irc/irc-channel.h b/src/plugins/irc/irc-channel.h index 0094be4a8..231207c51 100644 --- a/src/plugins/irc/irc-channel.h +++ b/src/plugins/irc/irc-channel.h @@ -32,14 +32,14 @@ struct t_irc_server; struct t_irc_modelist; -enum t_irc_channel_typing_status +enum t_irc_channel_typing_state { - IRC_CHANNEL_TYPING_STATUS_OFF = 0, - IRC_CHANNEL_TYPING_STATUS_TYPING, - IRC_CHANNEL_TYPING_STATUS_PAUSED, - IRC_CHANNEL_TYPING_STATUS_DONE, - /* number of channel typing statuses */ - IRC_CHANNEL_NUM_TYPING_STATUSES, + IRC_CHANNEL_TYPING_STATE_OFF = 0, + IRC_CHANNEL_TYPING_STATE_ACTIVE, + IRC_CHANNEL_TYPING_STATE_PAUSED, + IRC_CHANNEL_TYPING_STATE_DONE, + /* number of channel typing states */ + IRC_CHANNEL_NUM_TYPING_STATES, }; struct t_irc_channel_speaking @@ -83,7 +83,7 @@ struct t_irc_channel struct t_irc_modelist *modelists; /* modelists in the channel */ struct t_irc_modelist *last_modelist; /* last modelist in the channel */ struct t_hashtable *join_smart_filtered; /* smart filtered joins */ - int typing_status; /* typing status */ + int typing_state; /* typing state */ time_t typing_status_sent; /* last time typing status was sent */ struct t_gui_buffer *buffer; /* buffer allocated for channel */ char *buffer_as_string; /* used to return buffer info */ @@ -91,7 +91,7 @@ struct t_irc_channel struct t_irc_channel *next_channel; /* link to next channel */ }; -extern char *irc_channel_typing_status_string[IRC_CHANNEL_NUM_TYPING_STATUSES]; +extern char *irc_channel_typing_state_string[IRC_CHANNEL_NUM_TYPING_STATES]; extern char *irc_channel_default_chantypes; extern int irc_channel_valid (struct t_irc_server *server, diff --git a/src/plugins/irc/irc-config.c b/src/plugins/irc/irc-config.c index f68df8d36..78f58ad96 100644 --- a/src/plugins/irc/irc-config.c +++ b/src/plugins/irc/irc-config.c @@ -98,7 +98,8 @@ struct t_config_option *irc_config_look_part_closes_buffer; struct t_config_option *irc_config_look_pv_buffer; struct t_config_option *irc_config_look_pv_tags; struct t_config_option *irc_config_look_raw_messages; -struct t_config_option *irc_config_look_send_typing_status; +struct t_config_option *irc_config_look_typing_status_nicks; +struct t_config_option *irc_config_look_typing_status_self; struct t_config_option *irc_config_look_server_buffer; struct t_config_option *irc_config_look_smart_filter; struct t_config_option *irc_config_look_smart_filter_account; @@ -3116,11 +3117,21 @@ irc_config_init () "closed (messages will be displayed when opening raw data buffer)"), NULL, 0, 65535, "256", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - irc_config_look_send_typing_status = weechat_config_new_option ( + irc_config_look_typing_status_nicks = weechat_config_new_option ( irc_config_file, ptr_section, - "send_typing_status", "boolean", - N_("send typing status to channels (capability \"message-tags\" must " - "be enabled)"), + "typing_status_nicks", "boolean", + N_("display nicks typing on the channel in bar item \"typing\" " + "(option typing.look.enabled must be enabled and capability " + "\"message-tags\" must be enabled on the server)"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + irc_config_look_typing_status_self = weechat_config_new_option ( + irc_config_file, ptr_section, + "typing_status_self", "boolean", + N_("send self typing status to channels so that other users see when " + "you are typing a message " + "(option typing.look.enabled must be enabled and capability " + "\"message-tags\" must be enabled on the server)"), NULL, 0, 0, "off", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); irc_config_look_server_buffer = weechat_config_new_option ( diff --git a/src/plugins/irc/irc-config.h b/src/plugins/irc/irc-config.h index c21093b67..0b930b36b 100644 --- a/src/plugins/irc/irc-config.h +++ b/src/plugins/irc/irc-config.h @@ -138,7 +138,8 @@ extern struct t_config_option *irc_config_look_part_closes_buffer; extern struct t_config_option *irc_config_look_pv_buffer; extern struct t_config_option *irc_config_look_pv_tags; extern struct t_config_option *irc_config_look_raw_messages; -extern struct t_config_option *irc_config_look_send_typing_status; +extern struct t_config_option *irc_config_look_typing_status_nicks; +extern struct t_config_option *irc_config_look_typing_status_self; extern struct t_config_option *irc_config_look_server_buffer; extern struct t_config_option *irc_config_look_smart_filter; extern struct t_config_option *irc_config_look_smart_filter_account; diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c index 98115d261..99ba1435d 100644 --- a/src/plugins/irc/irc-protocol.c +++ b/src/plugins/irc/irc-protocol.c @@ -57,6 +57,7 @@ #include "irc-sasl.h" #include "irc-server.h" #include "irc-tag.h" +#include "irc-typing.h" /* @@ -2139,6 +2140,13 @@ IRC_PROTOCOL_CALLBACK(notice) if (ptr_channel) irc_channel_join_smart_filtered_unmask (ptr_channel, nick); + if (ptr_channel + && weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + ptr_nick = irc_nick_search (server, ptr_channel, nick); weechat_printf_date_tags ( (ptr_channel) ? ptr_channel->buffer : server->buffer, @@ -2207,6 +2215,12 @@ IRC_PROTOCOL_CALLBACK(notice) if (ptr_channel) { + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + if (!ptr_channel->topic) irc_channel_set_topic (ptr_channel, address); @@ -2392,6 +2406,9 @@ IRC_PROTOCOL_CALLBACK(part) /* part request was issued by local client ? */ if (local_part) { + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + irc_typing_channel_reset (ptr_channel); + irc_nick_free_all (server, ptr_channel); irc_channel_modelist_set_state (ptr_channel, @@ -2431,12 +2448,20 @@ IRC_PROTOCOL_CALLBACK(part) } irc_bar_item_update_channel (); } - else if (ptr_nick) + else { /* part from another user */ - irc_channel_join_smart_filtered_remove (ptr_channel, - ptr_nick->name); - irc_nick_free (server, ptr_channel, ptr_nick); + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + if (ptr_nick) + { + irc_channel_join_smart_filtered_remove (ptr_channel, + ptr_nick->name); + irc_nick_free (server, ptr_channel, ptr_nick); + } } return WEECHAT_RC_OK; @@ -2574,6 +2599,12 @@ IRC_PROTOCOL_CALLBACK(privmsg) } /* other message */ + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + ptr_nick = irc_nick_search (server, ptr_channel, nick); if (ptr_nick) @@ -2664,6 +2695,13 @@ IRC_PROTOCOL_CALLBACK(privmsg) return WEECHAT_RC_ERROR; } } + + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + irc_channel_set_topic (ptr_channel, address); if (nick_is_me) @@ -2751,6 +2789,12 @@ IRC_PROTOCOL_CALLBACK(quit) for (ptr_channel = server->channels; ptr_channel; ptr_channel = ptr_channel->next_channel) { + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + irc_typing_channel_set_nick (ptr_channel, nick, + IRC_CHANNEL_TYPING_STATE_OFF); + } + if (ptr_channel->type == IRC_CHANNEL_TYPE_PRIVATE) ptr_nick = NULL; else @@ -2906,14 +2950,46 @@ IRC_PROTOCOL_CALLBACK(setname) * (received when capability "message-tags" is enabled). * * Message looks like: - * @msgid=6gqz7dxd22v7r3x9pvukkp8nni;+tag1 :nick!user@host TAGMSG #channel + * @msgid=6gqz7dxd22v7r3x9pvu;+typing=active :nick!user@host TAGMSG #channel */ IRC_PROTOCOL_CALLBACK(tagmsg) { + struct t_irc_channel *ptr_channel; + const char *ptr_typing_value; + int state; + IRC_PROTOCOL_MIN_ARGS(3); - /* no action by default */ + if (ignored) + return WEECHAT_RC_OK; + + if (!tags) + return WEECHAT_RC_OK; + + ptr_channel = NULL; + if (irc_channel_is_channel (server, argv[2])) + ptr_channel = irc_channel_search (server, argv[2]); + else if (irc_server_strcasecmp (server, argv[2], server->nick) == 0) + ptr_channel = irc_channel_search (server, nick); + if (!ptr_channel) + return WEECHAT_RC_OK; + + if (weechat_config_boolean (irc_config_look_typing_status_nicks)) + { + ptr_typing_value = weechat_hashtable_get (tags, "+typing"); + if (ptr_typing_value && ptr_typing_value[0]) + { + if (strcmp (ptr_typing_value, "active") == 0) + state = IRC_CHANNEL_TYPING_STATE_ACTIVE; + else if (strcmp (ptr_typing_value, "paused") == 0) + state = IRC_CHANNEL_TYPING_STATE_PAUSED; + else + state = IRC_CHANNEL_TYPING_STATE_OFF; + irc_typing_channel_set_nick (ptr_channel, nick, state); + } + } + return WEECHAT_RC_OK; } @@ -3202,7 +3278,7 @@ IRC_PROTOCOL_CALLBACK(001) irc_server_set_nick (server, argv[2]); irc_protocol_cb_numeric (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); /* connection to IRC server is OK! */ @@ -3324,7 +3400,7 @@ IRC_PROTOCOL_CALLBACK(005) IRC_PROTOCOL_MIN_ARGS(4); irc_protocol_cb_numeric (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); /* save prefix */ @@ -5868,7 +5944,7 @@ IRC_PROTOCOL_CALLBACK(432) struct t_gui_buffer *ptr_buffer; irc_protocol_cb_generic_error (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); if (!server->is_connected) @@ -5953,7 +6029,7 @@ IRC_PROTOCOL_CALLBACK(433) else { return irc_protocol_cb_generic_error (server, - date, nick, address, host, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); } @@ -5974,7 +6050,7 @@ IRC_PROTOCOL_CALLBACK(437) struct t_gui_buffer *ptr_buffer; irc_protocol_cb_generic_error (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); if (!server->is_connected) @@ -6076,7 +6152,7 @@ IRC_PROTOCOL_CALLBACK(470) int lines_count; irc_protocol_cb_generic_error (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); if ((argc >= 5) && !irc_channel_search (server, argv[3])) @@ -6563,7 +6639,7 @@ IRC_PROTOCOL_CALLBACK(901) else { irc_protocol_cb_numeric (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); } @@ -6586,7 +6662,7 @@ IRC_PROTOCOL_CALLBACK(sasl_end_ok) } irc_protocol_cb_numeric (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); if (!server->is_connected) @@ -6615,7 +6691,7 @@ IRC_PROTOCOL_CALLBACK(sasl_end_fail) } irc_protocol_cb_numeric (server, - date, nick, address, host, command, + date, tags, nick, address, host, command, ignored, argc, argv, argv_eol); sasl_fail = IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SASL_FAIL); @@ -6826,6 +6902,7 @@ irc_protocol_recv_command (struct t_irc_server *server, argv = NULL; argv_eol = NULL; date = 0; + hash_tags = NULL; ptr_msg_after_tags = irc_message; @@ -6848,7 +6925,6 @@ irc_protocol_recv_command (struct t_irc_server *server, irc_tag_parse (tags, hash_tags, NULL); date = irc_protocol_parse_time ( weechat_hashtable_get (hash_tags, "time")); - weechat_hashtable_free (hash_tags); } free (tags); } @@ -6991,11 +7067,10 @@ irc_protocol_recv_command (struct t_irc_server *server, argv_eol = weechat_string_split (message_colors_decoded, " ", NULL, flags, 0, NULL); - return_code = (int) (cmd_recv_func) (server, - date, nick, address_color, - host_color, cmd_name, - message_ignored, argc, argv, - argv_eol); + return_code = (int) (cmd_recv_func) (server, date, hash_tags, nick, + address_color, host_color, + cmd_name, message_ignored, + argc, argv, argv_eol); if (return_code == WEECHAT_RC_ERROR) { @@ -7040,4 +7115,6 @@ end: weechat_string_free_split (argv); if (argv_eol) weechat_string_free_split (argv_eol); + if (hash_tags) + weechat_hashtable_free (hash_tags); } diff --git a/src/plugins/irc/irc-protocol.h b/src/plugins/irc/irc-protocol.h index 29d27a08d..c34b4bdae 100644 --- a/src/plugins/irc/irc-protocol.h +++ b/src/plugins/irc/irc-protocol.h @@ -26,6 +26,7 @@ int \ irc_protocol_cb_##__command (struct t_irc_server *server, \ time_t date, \ + struct t_hashtable *tags, \ const char *nick, \ const char *address, \ const char *host, \ @@ -43,6 +44,7 @@ #define IRC_PROTOCOL_MIN_ARGS(__min_args) \ (void) date; \ + (void) tags; \ (void) nick; \ (void) address; \ (void) host; \ @@ -75,9 +77,9 @@ struct t_irc_server; typedef int (t_irc_recv_func)(struct t_irc_server *server, - time_t date, const char *nick, - const char *address, const char *host, - const char *command, + time_t date, struct t_hashtable *tags, + const char *nick, const char *address, + const char *host, const char *command, int ignored, int argc, char **argv, char **argv_eol); diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index 6dcf5bb18..d06d5bafd 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -64,6 +64,7 @@ #include "irc-raw.h" #include "irc-redirect.h" #include "irc-sasl.h" +#include "irc-typing.h" struct t_irc_server *irc_servers = NULL; @@ -3734,33 +3735,8 @@ irc_server_timer_cb (const void *pointer, void *data, int remaining_calls) ptr_redirect = ptr_next_redirect; } - /* send typing status on channels */ - if (weechat_config_boolean (irc_config_look_send_typing_status)) - { - for (ptr_channel = ptr_server->channels; ptr_channel; - ptr_channel = ptr_channel->next_channel) - { - if ((ptr_channel->typing_status != IRC_CHANNEL_TYPING_STATUS_OFF) - && (ptr_channel->typing_status_sent + 3 < current_time)) - { - irc_server_sendf ( - ptr_server, - IRC_SERVER_SEND_OUTQ_PRIO_LOW, NULL, - "@+typing=%s TAGMSG %s", - irc_channel_typing_status_string[ptr_channel->typing_status], - ptr_channel->name); - if (ptr_channel->typing_status == IRC_CHANNEL_TYPING_STATUS_TYPING) - { - ptr_channel->typing_status_sent = current_time; - } - else - { - ptr_channel->typing_status = IRC_CHANNEL_TYPING_STATUS_OFF; - ptr_channel->typing_status_sent = 0; - } - } - } - } + /* send typing status on channels/privates */ + irc_typing_send_to_targets (ptr_server); /* purge some data (every 10 minutes) */ if (current_time > ptr_server->last_data_purge + (60 * 10)) diff --git a/src/plugins/irc/irc-typing.c b/src/plugins/irc/irc-typing.c new file mode 100644 index 000000000..49ecc1502 --- /dev/null +++ b/src/plugins/irc/irc-typing.c @@ -0,0 +1,159 @@ +/* + * irc-typing.c - manage typing status on channels/private + * + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "irc.h" +#include "irc-buffer.h" +#include "irc-channel.h" +#include "irc-config.h" +#include "irc-server.h" + + +/* + * Callback for signals "typing_self_*". + */ + +int +irc_typing_signal_typing_self_cb (const void *pointer, void *data, + const char *signal, const char *type_data, + void *signal_data) +{ + struct t_irc_server *ptr_server; + struct t_irc_channel *ptr_channel; + int new_state; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + + /* sending self typing status is allowed? */ + if (!weechat_config_boolean (irc_config_look_typing_status_self)) + return WEECHAT_RC_OK; + + /* search server/channel with buffer */ + irc_buffer_get_server_and_channel (signal_data, &ptr_server, &ptr_channel); + if (!ptr_server || !ptr_channel) + return WEECHAT_RC_OK; + + /* typing works only if capability "message-tags" is enabled */ + if (!weechat_hashtable_has_key (ptr_server->cap_list, "message-tags")) + return WEECHAT_RC_OK; + + if (strcmp (signal, "typing_self_typing") == 0) + new_state = IRC_CHANNEL_TYPING_STATE_ACTIVE; + else if (strcmp (signal, "typing_self_paused") == 0) + new_state = IRC_CHANNEL_TYPING_STATE_PAUSED; + else if (strcmp (signal, "typing_self_cleared") == 0) + new_state = IRC_CHANNEL_TYPING_STATE_DONE; + else if (strcmp (signal, "typing_self_sent") == 0) + new_state = IRC_CHANNEL_TYPING_STATE_OFF; + else + new_state = -1; + + if ((new_state >= 0) && (new_state != ptr_channel->typing_state)) + { + ptr_channel->typing_state = new_state; + ptr_channel->typing_status_sent = 0; + } + + return WEECHAT_RC_OK; +} + +/* + * Sends self typing status to channels/privates of a server. + */ + +void +irc_typing_send_to_targets (struct t_irc_server *server) +{ + struct t_irc_channel *ptr_channel; + time_t current_time; + + if (!weechat_config_boolean (irc_config_look_typing_status_self)) + return; + + current_time = time (NULL); + + for (ptr_channel = server->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (!ptr_channel->part + && (ptr_channel->typing_state != IRC_CHANNEL_TYPING_STATE_OFF) + && (ptr_channel->typing_status_sent + 3 < current_time)) + { + irc_server_sendf ( + server, + IRC_SERVER_SEND_OUTQ_PRIO_LOW, NULL, + "@+typing=%s TAGMSG %s", + irc_channel_typing_state_string[ptr_channel->typing_state], + ptr_channel->name); + if (ptr_channel->typing_state == IRC_CHANNEL_TYPING_STATE_ACTIVE) + { + ptr_channel->typing_status_sent = current_time; + } + else + { + ptr_channel->typing_state = IRC_CHANNEL_TYPING_STATE_OFF; + ptr_channel->typing_status_sent = 0; + } + } + } +} + +/* + * Sets state of a nick on a channel. + */ + +void +irc_typing_channel_set_nick (struct t_irc_channel *channel, const char *nick, + int state) +{ + char signal_data[1024]; + + snprintf (signal_data, sizeof (signal_data), + "0x%lx;%s;%s", + (unsigned long)channel->buffer, + (state == IRC_CHANNEL_TYPING_STATE_ACTIVE) ? "typing" : + ((state == IRC_CHANNEL_TYPING_STATE_PAUSED) ? "paused" : "off"), + nick); + weechat_hook_signal_send ("typing_set_nick", + WEECHAT_HOOK_SIGNAL_STRING, + signal_data); +} + +/* + * Resets all nicks state on a channel. + */ + +void +irc_typing_channel_reset (struct t_irc_channel *channel) +{ + weechat_hook_signal_send ("typing_reset_buffer", + WEECHAT_HOOK_SIGNAL_POINTER, + channel->buffer); +} diff --git a/src/plugins/irc/irc-typing.h b/src/plugins/irc/irc-typing.h new file mode 100644 index 000000000..ad3139aa7 --- /dev/null +++ b/src/plugins/irc/irc-typing.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_IRC_TYPING_H +#define WEECHAT_PLUGIN_IRC_TYPING_H + +struct t_irc_server; + +extern int irc_typing_signal_typing_self_cb (const void *pointer, void *data, + const char *signal, + const char *type_data, + void *signal_data); +extern void irc_typing_send_to_targets (struct t_irc_server *server); +extern void irc_typing_channel_set_nick (struct t_irc_channel *channel, + const char *nick, + int state); +extern void irc_typing_channel_reset (struct t_irc_channel *channel); + +#endif /* WEECHAT_PLUGIN_IRC_TYPING_H */ diff --git a/src/plugins/irc/irc.c b/src/plugins/irc/irc.c index e0029f1fd..7c600d339 100644 --- a/src/plugins/irc/irc.c +++ b/src/plugins/irc/irc.c @@ -43,6 +43,7 @@ #include "irc-redirect.h" #include "irc-server.h" #include "irc-tag.h" +#include "irc-typing.h" #include "irc-upgrade.h" @@ -160,53 +161,6 @@ irc_signal_upgrade_cb (const void *pointer, void *data, } /* - * Callback for signals "typing_*". - */ - -int -irc_signal_typing_cb (const void *pointer, void *data, - const char *signal, const char *type_data, - void *signal_data) -{ - struct t_irc_server *ptr_server; - struct t_irc_channel *ptr_channel; - int new_status; - - /* make C compiler happy */ - (void) pointer; - (void) data; - (void) signal; - (void) type_data; - - /* search server/channel with buffer */ - irc_buffer_get_server_and_channel (signal_data, &ptr_server, &ptr_channel); - if (!ptr_server || !ptr_channel) - return WEECHAT_RC_OK; - - /* typing works only if capability "message-tags" is enabled */ - if (!weechat_hashtable_has_key (ptr_server->cap_list, "message-tags")) - return WEECHAT_RC_OK; - - new_status = -1; - if (strcmp (signal, "typing_active") == 0) - new_status = IRC_CHANNEL_TYPING_STATUS_TYPING; - else if (strcmp (signal, "typing_paused") == 0) - new_status = IRC_CHANNEL_TYPING_STATUS_PAUSED; - else if (strcmp (signal, "typing_cleared") == 0) - new_status = IRC_CHANNEL_TYPING_STATUS_DONE; - else if (strcmp (signal, "typing_sent") == 0) - new_status = IRC_CHANNEL_TYPING_STATUS_OFF; - - if ((new_status >= 0) && (new_status != ptr_channel->typing_status)) - { - ptr_channel->typing_status = new_status; - ptr_channel->typing_status_sent = 0; - } - - return WEECHAT_RC_OK; -} - -/* * Initializes IRC plugin. */ @@ -247,8 +201,8 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) &irc_server_xfer_send_accept_resume_cb, NULL, NULL); weechat_hook_signal ("irc_input_send", &irc_input_send_cb, NULL, NULL); - weechat_hook_signal ("typing_*", - &irc_signal_typing_cb, NULL, NULL); + weechat_hook_signal ("typing_self_*", + &irc_typing_signal_typing_self_cb, NULL, NULL); /* hook hsignals for redirection */ weechat_hook_hsignal ("irc_redirect_pattern", diff --git a/src/plugins/typing/CMakeLists.txt b/src/plugins/typing/CMakeLists.txt index c755217a1..f08223e3f 100644 --- a/src/plugins/typing/CMakeLists.txt +++ b/src/plugins/typing/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(typing MODULE typing.c typing.h + typing-bar-item.c typing-bar-item.h typing-config.c typing-config.h typing-status.c typing-status.h ) diff --git a/src/plugins/typing/Makefile.am b/src/plugins/typing/Makefile.am index ad9d38276..ba6345f09 100644 --- a/src/plugins/typing/Makefile.am +++ b/src/plugins/typing/Makefile.am @@ -25,6 +25,8 @@ lib_LTLIBRARIES = typing.la typing_la_SOURCES = typing.c \ typing.h \ + typing-bar-item.c \ + typing-bar-item.h \ typing-config.c \ typing-config.h \ typing-status.c \ diff --git a/src/plugins/typing/typing-bar-item.c b/src/plugins/typing/typing-bar-item.c new file mode 100644 index 000000000..2ecae5870 --- /dev/null +++ b/src/plugins/typing/typing-bar-item.c @@ -0,0 +1,120 @@ +/* + * typing-bar-item.c - bar items for typing plugin + * + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "../weechat-plugin.h" +#include "typing.h" +#include "typing-bar-item.h" +#include "typing-config.h" +#include "typing-status.h" + + +/* + * Callback used to build a string with the list of nicks typing on the buffer. + */ + +void +typing_bar_item_nicks_map_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + char **str_nicks_typing; + const char *ptr_nick; + struct t_typing_status *ptr_typing_status; + + /* make C compiler happy */ + (void) hashtable; + + str_nicks_typing = (char **)data; + + ptr_nick = (const char *)key; + ptr_typing_status = (struct t_typing_status *)value; + + if ((ptr_typing_status->state == TYPING_STATUS_STATE_TYPING) + || (ptr_typing_status->state == TYPING_STATUS_STATE_PAUSED)) + { + if (*str_nicks_typing[0]) + weechat_string_dyn_concat (str_nicks_typing, ", ", -1); + if (ptr_typing_status->state == TYPING_STATUS_STATE_PAUSED) + weechat_string_dyn_concat (str_nicks_typing, "(", -1); + weechat_string_dyn_concat (str_nicks_typing, ptr_nick, -1); + if (ptr_typing_status->state == TYPING_STATUS_STATE_PAUSED) + weechat_string_dyn_concat (str_nicks_typing, ")", -1); + } +} + +/* + * Returns content of bar item "typing": users currently typing on the buffer. + */ + +char * +typing_bar_item_typing (const void *pointer, void *data, + struct t_gui_bar_item *item, + struct t_gui_window *window, + struct t_gui_buffer *buffer, + struct t_hashtable *extra_info) +{ + struct t_hashtable *ptr_nicks; + char **str_nicks_typing, **str_typing; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) item; + (void) window; + (void) extra_info; + + if (!weechat_config_boolean (typing_config_look_enabled_nicks)) + return NULL; + + ptr_nicks = weechat_hashtable_get (typing_status_nicks, buffer); + if (!ptr_nicks) + return NULL; + + if (weechat_hashtable_get_integer (ptr_nicks, "items_count") == 0) + return NULL; + + str_nicks_typing = weechat_string_dyn_alloc (128); + weechat_hashtable_map (ptr_nicks, + &typing_bar_item_nicks_map_cb, str_nicks_typing); + + str_typing = weechat_string_dyn_alloc (256); + weechat_string_dyn_concat (str_typing, _("Typing: "), -1); + weechat_string_dyn_concat (str_typing, *str_nicks_typing, -1); + + weechat_string_dyn_free (str_nicks_typing, 1); + + return weechat_string_dyn_free (str_typing, 0); +} + +/* + * Initializes typing bar items. + */ + +void +typing_bar_item_init () +{ + weechat_bar_item_new (TYPING_BAR_ITEM_NAME, + &typing_bar_item_typing, NULL, NULL); +} diff --git a/src/plugins/typing/typing-bar-item.h b/src/plugins/typing/typing-bar-item.h new file mode 100644 index 000000000..864697668 --- /dev/null +++ b/src/plugins/typing/typing-bar-item.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_TYPING_BAR_ITEM_H +#define WEECHAT_PLUGIN_TYPING_BAR_ITEM_H + +#define TYPING_BAR_ITEM_NAME "typing" + +extern void typing_bar_item_init (); + +#endif /* WEECHAT_PLUGIN_TYPING_BAR_ITEM_H */ diff --git a/src/plugins/typing/typing-config.c b/src/plugins/typing/typing-config.c index 42d0d1051..dacfd6b70 100644 --- a/src/plugins/typing/typing-config.c +++ b/src/plugins/typing/typing-config.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "typing.h" #include "typing-config.h" +#include "typing-bar-item.h" struct t_config_file *typing_config_file = NULL; @@ -34,8 +35,11 @@ struct t_config_section *typing_config_section_completion = NULL; /* typing config, look section */ -struct t_config_option *typing_config_look_enabled; -struct t_config_option *typing_config_look_delay_pause; +struct t_config_option *typing_config_look_delay_purge_paused; +struct t_config_option *typing_config_look_delay_purge_typing; +struct t_config_option *typing_config_look_delay_set_paused; +struct t_config_option *typing_config_look_enabled_nicks; +struct t_config_option *typing_config_look_enabled_self; /* @@ -73,6 +77,7 @@ typing_config_change_enabled (const void *pointer, void *data, (void) option; typing_setup_hooks (); + weechat_bar_item_update (TYPING_BAR_ITEM_NAME); } /* @@ -109,21 +114,44 @@ typing_config_init () return 0; } - typing_config_look_enabled = weechat_config_new_option ( + typing_config_look_delay_purge_paused = weechat_config_new_option ( typing_config_file, ptr_section, - "enabled", "boolean", - N_("typing enabled"), - NULL, 0, 0, "on", NULL, 0, - NULL, NULL, NULL, - &typing_config_change_enabled, NULL, NULL, - NULL, NULL, NULL); - typing_config_look_delay_pause = weechat_config_new_option ( + "delay_purge_paused", "integer", + N_("number of seconds after paused status has been set: if reached, " + "the typing status is removed"), + NULL, 1, 3600, "30", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + typing_config_look_delay_purge_typing = weechat_config_new_option ( typing_config_file, ptr_section, - "delay_pause", "integer", + "delay_purge_typing", "integer", + N_("number of seconds after typing status has been set: if reached, " + "the typing status is removed"), + NULL, 1, 3600, "6", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + typing_config_look_delay_set_paused = weechat_config_new_option ( + typing_config_file, ptr_section, + "delay_set_paused", "integer", N_("number of seconds after typing last char: if reached, the typing " "status becomes \"paused\" and no more typing signals are sent"), NULL, 1, 3600, "10", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + typing_config_look_enabled_nicks = weechat_config_new_option ( + typing_config_file, ptr_section, + "enabled_nicks", "boolean", + N_("typing enabled for other nicks (display typing info for nicks " + "typing in the current buffer)"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, + &typing_config_change_enabled, NULL, NULL, + NULL, NULL, NULL); + typing_config_look_enabled_self = weechat_config_new_option ( + typing_config_file, ptr_section, + "enabled_self", "boolean", + N_("typing enabled for self messages (send typing info to other users)"), + NULL, 0, 0, "off", NULL, 0, + NULL, NULL, NULL, + &typing_config_change_enabled, NULL, NULL, + NULL, NULL, NULL); return 1; } diff --git a/src/plugins/typing/typing-config.h b/src/plugins/typing/typing-config.h index e367f724c..c0da80558 100644 --- a/src/plugins/typing/typing-config.h +++ b/src/plugins/typing/typing-config.h @@ -22,8 +22,11 @@ #define TYPING_CONFIG_NAME "typing" -extern struct t_config_option *typing_config_look_enabled; -extern struct t_config_option *typing_config_look_delay_pause; +extern struct t_config_option *typing_config_look_delay_purge_paused; +extern struct t_config_option *typing_config_look_delay_purge_typing; +extern struct t_config_option *typing_config_look_delay_set_paused; +extern struct t_config_option *typing_config_look_enabled_nicks; +extern struct t_config_option *typing_config_look_enabled_self; extern int typing_config_init (); extern int typing_config_read (); diff --git a/src/plugins/typing/typing-status.c b/src/plugins/typing/typing-status.c index b3b31f679..298b9d496 100644 --- a/src/plugins/typing/typing-status.c +++ b/src/plugins/typing/typing-status.c @@ -32,16 +32,44 @@ #include "typing-status.h" +char *typing_status_state_string[TYPING_STATUS_NUM_STATES] = +{ "off", "typing", "paused", "cleared" }; + +/* hashtable[buffer -> t_typing_status] */ struct t_hashtable *typing_status_self = NULL; +/* hashtable[buffer -> hashtable[nick -> t_typing_status]] */ +struct t_hashtable *typing_status_nicks = NULL; + /* - * Removes a typing status. + * Searches a state by name. + * + * Returns index of stats in enum t_typing_status_state, -1 if not found. + */ + +int +typing_status_search_state (const char *state) +{ + int i; + + for (i = 0; i < TYPING_STATUS_NUM_STATES; i++) + { + if (strcmp (typing_status_state_string[i], state) == 0) + return i; + } + + return -1; +} + +/* + * Removes self typing status for a buffer: key is a buffer pointer, value + * is a t_typing_status pointer. */ void -typing_status_free_value_cb (struct t_hashtable *hashtable, - const void *key, const void *value) +typing_status_self_free_value_cb (struct t_hashtable *hashtable, + const void *key, const void *value) { struct t_gui_buffer *ptr_buffer; struct t_typing_status *ptr_typing_status; @@ -52,14 +80,14 @@ typing_status_free_value_cb (struct t_hashtable *hashtable, ptr_buffer = (struct t_gui_buffer *)key; ptr_typing_status = (struct t_typing_status *)value; - if (!ptr_typing_status) + if (!ptr_buffer || !ptr_typing_status) return; if (weechat_typing_plugin->debug) { weechat_printf_date_tags ( NULL, 0, "no_log", - "%s: stop typing status for buffer \"%s\"", + "%s: removing self typing status for buffer \"%s\"", TYPING_PLUGIN_NAME, weechat_buffer_get_string (ptr_buffer, "name")); } @@ -68,7 +96,7 @@ typing_status_free_value_cb (struct t_hashtable *hashtable, } /* - * Adds a new typing status. + * Adds a new self typing status. * * Returns: * 1: OK @@ -76,46 +104,241 @@ typing_status_free_value_cb (struct t_hashtable *hashtable, */ struct t_typing_status * -typing_status_add (struct t_gui_buffer *buffer) +typing_status_self_add (struct t_gui_buffer *buffer, int state, int last_typed) { - struct t_typing_status *new_typing_status; + struct t_typing_status *ptr_typing_status; - if (!buffer) + if (!buffer || (state < 0) || (state >= TYPING_STATUS_NUM_STATES)) return NULL; if (!typing_status_self) { - typing_status_self = weechat_hashtable_new (64, - WEECHAT_HASHTABLE_POINTER, - WEECHAT_HASHTABLE_POINTER, - NULL, - NULL); + typing_status_self = weechat_hashtable_new ( + 64, + WEECHAT_HASHTABLE_POINTER, /* buffer */ + WEECHAT_HASHTABLE_POINTER, /* t_typing_status */ + NULL, + NULL); if (!typing_status_self) return NULL; weechat_hashtable_set_pointer (typing_status_self, "callback_free_value", - &typing_status_free_value_cb); + &typing_status_self_free_value_cb); } - new_typing_status = malloc (sizeof (*new_typing_status)); - if (!new_typing_status) + ptr_typing_status = weechat_hashtable_get (typing_status_self, buffer); + if (!ptr_typing_status) + { + if (weechat_typing_plugin->debug) + { + weechat_printf_date_tags ( + NULL, 0, "no_log", + "%s: creating self typing status for buffer \"%s\"", + TYPING_PLUGIN_NAME, + weechat_buffer_get_string (buffer, "name")); + } + ptr_typing_status = malloc (sizeof (*ptr_typing_status)); + if (!ptr_typing_status) + return NULL; + } + + ptr_typing_status->state = state; + ptr_typing_status->last_typed = last_typed; + + weechat_hashtable_set (typing_status_self, buffer, ptr_typing_status); + + return ptr_typing_status; +} + +/* + * Searches a self typing status for a buffer. + * + * Returns pointer to t_typing_status found, NULL if not found. + */ + +struct t_typing_status * +typing_status_self_search (struct t_gui_buffer *buffer) +{ + if (!typing_status_self) return NULL; + return weechat_hashtable_get (typing_status_self, buffer); +} + +/* + * Removes nicks typing status: key is a buffer pointer, value is a hashtable + * pointer. + */ + +void +typing_status_nicks_free_value_cb (struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_gui_buffer *ptr_buffer; + struct t_hashtable *ptr_nicks; + + /* make C compiler happy */ + (void) hashtable; + + ptr_buffer = (struct t_gui_buffer *)key; + ptr_nicks = (struct t_hashtable *)value; + + if (!ptr_buffer || !ptr_nicks) + return; + if (weechat_typing_plugin->debug) { - weechat_printf_date_tags (NULL, 0, "no_log", - "%s: start typing status for buffer \"%s\"", - TYPING_PLUGIN_NAME, - weechat_buffer_get_string (buffer, "name")); + weechat_printf_date_tags ( + NULL, 0, "no_log", + "%s: removing nicks typing status for buffer \"%s\"", + TYPING_PLUGIN_NAME, + weechat_buffer_get_string (ptr_buffer, "name")); } - new_typing_status->status = TYPING_STATUS_STATUS_OFF; - new_typing_status->last_typed = 0; - new_typing_status->last_signal_sent = 0; + weechat_hashtable_free (ptr_nicks); +} + +/* + * Removes a nick typing status: key is a nick (string), value is a + * t_typing_status pointer. + */ + +void +typing_status_nick_free_value_cb (struct t_hashtable *hashtable, + const void *key, const void *value) +{ + const char *ptr_nick; + struct t_typing_status *ptr_typing_status; + + /* make C compiler happy */ + (void) hashtable; + + ptr_nick = (const char *)key; + ptr_typing_status = (struct t_typing_status *)value; - weechat_hashtable_set (typing_status_self, buffer, new_typing_status); + if (!ptr_nick || !ptr_typing_status) + return; - return new_typing_status; + free (ptr_typing_status); +} + +/* + * Adds a nick typing status for a buffer. + * + * Returns: + * 1: OK + * 0: error + */ + +struct t_typing_status * +typing_status_nick_add (struct t_gui_buffer *buffer, const char *nick, + int state, int last_typed) +{ + struct t_hashtable *ptr_nicks; + struct t_typing_status *ptr_typing_status; + + if (!buffer || !nick || (state < 0) || (state >= TYPING_STATUS_NUM_STATES)) + return NULL; + + if (!typing_status_nicks) + { + typing_status_nicks = weechat_hashtable_new ( + 64, + WEECHAT_HASHTABLE_POINTER, /* buffer */ + WEECHAT_HASHTABLE_POINTER, /* hashtable */ + NULL, + NULL); + if (!typing_status_nicks) + return NULL; + weechat_hashtable_set_pointer (typing_status_nicks, + "callback_free_value", + &typing_status_nicks_free_value_cb); + } + + ptr_nicks = weechat_hashtable_get (typing_status_nicks, buffer); + if (!ptr_nicks) + { + ptr_nicks = weechat_hashtable_new ( + 32, + WEECHAT_HASHTABLE_STRING, /* nick */ + WEECHAT_HASHTABLE_POINTER, /* t_typing_status */ + NULL, + NULL); + if (!ptr_nicks) + return NULL; + weechat_hashtable_set_pointer (ptr_nicks, + "callback_free_value", + &typing_status_nick_free_value_cb); + weechat_hashtable_set (typing_status_nicks, buffer, ptr_nicks); + } + + ptr_typing_status = weechat_hashtable_get (ptr_nicks, nick); + if (!ptr_typing_status) + { + if (weechat_typing_plugin->debug) + { + weechat_printf_date_tags ( + NULL, 0, "no_log", + "%s: creating typing status for buffer \"%s\" and nick \"%s\"", + TYPING_PLUGIN_NAME, + weechat_buffer_get_string (buffer, "name"), + nick); + } + ptr_typing_status = malloc (sizeof (*ptr_typing_status)); + if (!ptr_typing_status) + return NULL; + } + + ptr_typing_status->state = state; + ptr_typing_status->last_typed = last_typed; + + weechat_hashtable_set (ptr_nicks, nick, ptr_typing_status); + + return ptr_typing_status; +} + +/* + * Removes a nick typing status from a buffer. + * + * Returns: + * 1: OK + * 0: error + */ + +void +typing_status_nick_remove (struct t_gui_buffer *buffer, const char *nick) +{ + struct t_hashtable *ptr_nicks; + + if (!typing_status_nicks) + return; + + ptr_nicks = weechat_hashtable_get (typing_status_nicks, buffer); + if (!ptr_nicks) + return; + + weechat_hashtable_remove (ptr_nicks, nick); +} + +/* + * Searches a nick typing status for a buffer. + * + * Returns pointer to t_typing_status found, NULL if not found. + */ + +struct t_typing_status * +typing_status_nick_search (struct t_gui_buffer *buffer, const char *nick) +{ + struct t_hashtable *ptr_nicks; + + if (!typing_status_nicks) + return NULL; + + ptr_nicks = weechat_hashtable_get (typing_status_nicks, buffer); + if (!ptr_nicks) + return NULL; + + return weechat_hashtable_get (ptr_nicks, nick); } /* @@ -130,4 +353,9 @@ typing_status_end () weechat_hashtable_free (typing_status_self); typing_status_self = NULL; } + if (typing_status_nicks) + { + weechat_hashtable_free (typing_status_nicks); + typing_status_nicks = NULL; + } } diff --git a/src/plugins/typing/typing-status.h b/src/plugins/typing/typing-status.h index 868db775e..8b5cc2417 100644 --- a/src/plugins/typing/typing-status.h +++ b/src/plugins/typing/typing-status.h @@ -20,33 +20,42 @@ #ifndef WEECHAT_PLUGIN_TYPING_STATUS_H #define WEECHAT_PLUGIN_TYPING_STATUS_H -#include <stdio.h> #include <time.h> -struct t_infolist; - -enum t_typing_status_status +enum t_typing_status_state { - TYPING_STATUS_STATUS_OFF = 0, - TYPING_STATUS_STATUS_TYPING, - TYPING_STATUS_STATUS_PAUSED, - TYPING_STATUS_STATUS_CLEARED, + TYPING_STATUS_STATE_OFF = 0, + TYPING_STATUS_STATE_TYPING, + TYPING_STATUS_STATE_PAUSED, + TYPING_STATUS_STATE_CLEARED, /* number of typing status statuses */ - TYPING_STATUS_NUM_STATUSES, + TYPING_STATUS_NUM_STATES, }; -/* self typing status */ +/* typing status */ struct t_typing_status { - int status; /* status */ - time_t last_typed; /* last char typed */ - time_t last_signal_sent; /* last signal sent */ + int state; /* current state */ + time_t last_typed; /* when was last char typed */ }; extern struct t_hashtable *typing_status_self; - -extern struct t_typing_status *typing_status_add (struct t_gui_buffer *buffer); +extern struct t_hashtable *typing_status_nicks; + +extern int typing_status_search_state (const char *state); +extern struct t_typing_status *typing_status_self_add (struct t_gui_buffer *buffer, + int state, + int last_typed); +extern struct t_typing_status *typing_status_self_search (struct t_gui_buffer *buffer); +extern struct t_typing_status *typing_status_nick_add (struct t_gui_buffer *buffer, + const char *nick, + int state, + int last_typed); +extern void typing_status_nick_remove (struct t_gui_buffer *buffer, + const char *nick); +extern struct t_typing_status *typing_status_nick_search (struct t_gui_buffer *buffer, + const char *nick); extern void typing_status_end (); #endif /* WEECHAT_PLUGIN_TYPING_STATUS_H */ diff --git a/src/plugins/typing/typing.c b/src/plugins/typing/typing.c index a061591e2..033ac5838 100644 --- a/src/plugins/typing/typing.c +++ b/src/plugins/typing/typing.c @@ -26,6 +26,7 @@ #include "../weechat-plugin.h" #include "typing.h" +#include "typing-bar-item.h" #include "typing-config.h" #include "typing-status.h" @@ -43,6 +44,10 @@ struct t_hook *typing_signal_buffer_closing = NULL; struct t_hook *typing_signal_input_text_changed = NULL; struct t_hook *typing_modifier_input_text_for_buffer = NULL; struct t_hook *typing_timer = NULL; +struct t_hook *typing_signal_typing_set_nick = NULL; +struct t_hook *typing_signal_typing_reset_buffer = NULL; + +int typing_update_item = 0; /* @@ -52,9 +57,7 @@ struct t_hook *typing_timer = NULL; */ int -typing_send_signal (struct t_gui_buffer *buffer, - struct t_typing_status *typing_status, - const char *signal_name) +typing_send_signal (struct t_gui_buffer *buffer, const char *signal_name) { if (weechat_typing_plugin->debug) { @@ -64,7 +67,6 @@ typing_send_signal (struct t_gui_buffer *buffer, weechat_buffer_get_string (buffer, "full_name")); } - typing_status->last_signal_sent = time (NULL); return weechat_hook_signal_send (signal_name, WEECHAT_HOOK_SIGNAL_POINTER, buffer); @@ -86,6 +88,7 @@ typing_buffer_closing_signal_cb (const void *pointer, void *data, (void) type_data; weechat_hashtable_remove (typing_status_self, signal_data); + weechat_hashtable_remove (typing_status_nicks, signal_data); return WEECHAT_RC_OK; } @@ -126,30 +129,33 @@ typing_input_text_changed_signal_cb (const void *pointer, void *data, if (!ptr_input_for_buffer) return WEECHAT_RC_OK; - ptr_typing_status = weechat_hashtable_get (typing_status_self, - ptr_buffer); + ptr_typing_status = typing_status_self_search (ptr_buffer); if (!ptr_typing_status) - ptr_typing_status = typing_status_add (ptr_buffer); + { + ptr_typing_status = typing_status_self_add ( + ptr_buffer, + TYPING_STATUS_STATE_TYPING, + 0); + } if (!ptr_typing_status) return WEECHAT_RC_OK; - ptr_typing_status->status = TYPING_STATUS_STATUS_TYPING; + ptr_typing_status->state = TYPING_STATUS_STATE_TYPING; ptr_typing_status->last_typed = time (NULL); } else { /* user was typing something? */ - ptr_typing_status = weechat_hashtable_get (typing_status_self, - ptr_buffer); + ptr_typing_status = typing_status_self_search (ptr_buffer); if (ptr_typing_status - && ((ptr_typing_status->status == TYPING_STATUS_STATUS_TYPING) - || (ptr_typing_status->status == TYPING_STATUS_STATUS_PAUSED))) + && ((ptr_typing_status->state == TYPING_STATUS_STATE_TYPING) + || (ptr_typing_status->state == TYPING_STATUS_STATE_PAUSED))) { /* * input cleared: maybe something was sent, not sure, so we just - * set the status to "cleared", a signal can be sent later + * set the state to "cleared", a signal can be sent later * in timer */ - ptr_typing_status->status = TYPING_STATUS_STATUS_CLEARED; + ptr_typing_status->state = TYPING_STATUS_STATE_CLEARED; } } @@ -194,13 +200,17 @@ typing_input_text_for_buffer_modifier_cb (const void *pointer, if (!ptr_input_for_buffer) return NULL; - ptr_typing_status = weechat_hashtable_get (typing_status_self, ptr_buffer); + ptr_typing_status = typing_status_self_search (ptr_buffer); if (!ptr_typing_status) - ptr_typing_status = typing_status_add (ptr_buffer); + { + ptr_typing_status = typing_status_self_add (ptr_buffer, + TYPING_STATUS_STATE_OFF, + 0); + } if (!ptr_typing_status) return NULL; - typing_send_signal (ptr_buffer, ptr_typing_status, "typing_sent"); + typing_send_signal (ptr_buffer, "typing_self_sent"); weechat_hashtable_remove (typing_status_self, ptr_buffer); return NULL; @@ -212,73 +222,251 @@ typing_input_text_for_buffer_modifier_cb (const void *pointer, */ void -typing_status_self_map_cb (void *data, - struct t_hashtable *hashtable, - const void *key, const void *value) +typing_status_self_status_map_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) { struct t_gui_buffer *ptr_buffer; struct t_typing_status *ptr_typing_status; const char *ptr_input, *ptr_input_for_buffer; + time_t current_time; int delay_pause; - /* make C compiler happy */ - (void) data; + current_time = *((time_t *)data); ptr_buffer = (struct t_gui_buffer *)key; ptr_typing_status = (struct t_typing_status *)value; - if (ptr_typing_status->status == TYPING_STATUS_STATUS_TYPING) + if (!ptr_buffer || !ptr_typing_status) + return; + + if (ptr_typing_status->state == TYPING_STATUS_STATE_TYPING) { ptr_input = weechat_buffer_get_string (ptr_buffer, "input"); ptr_input_for_buffer = weechat_string_input_for_buffer (ptr_input); if (ptr_input_for_buffer) { /* check if typing is paused */ - delay_pause = weechat_config_integer (typing_config_look_delay_pause); - if (ptr_typing_status->last_typed < time (NULL) - delay_pause) + delay_pause = weechat_config_integer (typing_config_look_delay_set_paused); + if (ptr_typing_status->last_typed < current_time - delay_pause) { - ptr_typing_status->status = TYPING_STATUS_STATUS_PAUSED; - typing_send_signal (ptr_buffer, ptr_typing_status, - "typing_paused"); + ptr_typing_status->state = TYPING_STATUS_STATE_PAUSED; + typing_send_signal (ptr_buffer, "typing_self_paused"); weechat_hashtable_remove (hashtable, ptr_buffer); } else { - typing_send_signal (ptr_buffer, ptr_typing_status, - "typing_active"); + typing_send_signal (ptr_buffer, "typing_self_typing"); } } else { - typing_send_signal (ptr_buffer, ptr_typing_status, - "typing_cleared"); + typing_send_signal (ptr_buffer, "typing_self_cleared"); weechat_hashtable_remove (hashtable, ptr_buffer); } } - else if (ptr_typing_status->status == TYPING_STATUS_STATUS_CLEARED) + else if (ptr_typing_status->state == TYPING_STATUS_STATE_CLEARED) { - typing_send_signal (ptr_buffer, ptr_typing_status, - "typing_cleared"); + typing_send_signal (ptr_buffer, "typing_self_cleared"); weechat_hashtable_remove (hashtable, ptr_buffer); } } /* - * Callback for modifier "input_text_for_buffer". + * Callback called periodically (via a timer) for each entry in hashtable + * "typing_status_nicks". + */ + +void +typing_status_nicks_status_map_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + const char *ptr_nick; + struct t_typing_status *ptr_typing_status; + time_t current_time; + int delay_purge_pause, delay_purge_typing; + + current_time = *((time_t *)data); + + ptr_nick = (const char *)key; + ptr_typing_status = (struct t_typing_status *)value; + + if (!ptr_nick || !ptr_typing_status) + return; + + delay_purge_pause = weechat_config_integer ( + typing_config_look_delay_purge_paused); + delay_purge_typing = weechat_config_integer ( + typing_config_look_delay_purge_typing); + + if (((ptr_typing_status->state == TYPING_STATUS_STATE_PAUSED) + && (ptr_typing_status->last_typed < current_time - delay_purge_pause)) + || ((ptr_typing_status->state == TYPING_STATUS_STATE_TYPING) + && (ptr_typing_status->last_typed < current_time - delay_purge_typing))) + { + weechat_hashtable_remove (hashtable, key); + typing_update_item = 1; + } +} + +/* + * Callback called periodically (via a timer) for each entry in hashtable + * "typing_status_nicks". + */ + +void +typing_status_nicks_hash_map_cb (void *data, + struct t_hashtable *hashtable, + const void *key, const void *value) +{ + struct t_hashtable *ptr_nicks; + + ptr_nicks = (struct t_hashtable *)value; + + if (!ptr_nicks) + return; + + weechat_hashtable_map (ptr_nicks, + &typing_status_nicks_status_map_cb, + data); + + /* no more nicks for the buffer? then remove the buffer */ + if (weechat_hashtable_get_integer (ptr_nicks, "items_count") == 0) + weechat_hashtable_remove (hashtable, key); +} + +/* + * Typing timer used to send continuously the self typing status. */ int -typing_timer_cb (const void *pointer, - void *data, - int remaining_calls) +typing_timer_cb (const void *pointer, void *data, int remaining_calls) { + time_t current_time; + /* make C compiler happy */ (void) pointer; (void) data; (void) remaining_calls; + typing_update_item = 0; + current_time = time (NULL); + weechat_hashtable_map (typing_status_self, - &typing_status_self_map_cb, NULL); + &typing_status_self_status_map_cb, ¤t_time); + weechat_hashtable_map (typing_status_nicks, + &typing_status_nicks_hash_map_cb, ¤t_time); + + if (typing_update_item) + weechat_bar_item_update (TYPING_BAR_ITEM_NAME); + + return WEECHAT_RC_OK; +} + +/* + * Callback for signal "typing_set_nick". + */ + +int +typing_typing_set_nick_signal_cb (const void *pointer, void *data, + const char *signal, + const char *type_data, void *signal_data) +{ + char **items; + int num_items, rc, state, updated; + unsigned long value; + struct t_gui_buffer *ptr_buffer; + struct t_typing_status *ptr_typing_status; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + + items = weechat_string_split ((const char *)signal_data, ";", NULL, + 0, 3, &num_items); + if (!items || (num_items != 3)) + goto end; + + rc = sscanf (items[0], "%lx", &value); + if ((rc == EOF) || (rc == 0)) + goto end; + ptr_buffer = (struct t_gui_buffer *)value; + if (!ptr_buffer) + goto end; + + state = typing_status_search_state (items[1]); + if (state < 0) + goto end; + + if (!items[2][0]) + goto end; + + updated = 0; + ptr_typing_status = typing_status_nick_search (ptr_buffer, items[2]); + if ((state == TYPING_STATUS_STATE_TYPING) + || (state == TYPING_STATUS_STATE_PAUSED)) + { + if (ptr_typing_status) + { + if (ptr_typing_status->state != state) + updated = 1; + ptr_typing_status->state = state; + ptr_typing_status->last_typed = time (NULL); + } + else + { + typing_status_nick_add (ptr_buffer, items[2], state, time (NULL)); + updated = 1; + } + } + else + { + if (ptr_typing_status) + updated = 1; + typing_status_nick_remove (ptr_buffer, items[2]); + } + + if (updated) + weechat_bar_item_update (TYPING_BAR_ITEM_NAME); + +end: + if (items) + weechat_string_free_split (items); + + return WEECHAT_RC_OK; +} + +/* + * Callback for signal "typing_reset_buffer". + */ + +int +typing_typing_reset_buffer_signal_cb (const void *pointer, void *data, + const char *signal, + const char *type_data, void *signal_data) +{ + int items_count; + struct t_gui_buffer *ptr_buffer; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + + ptr_buffer = (struct t_gui_buffer *)signal_data; + + if (!typing_status_nicks) + return WEECHAT_RC_OK; + + items_count = weechat_hashtable_get_integer (typing_status_nicks, + "items_count"); + weechat_hashtable_remove (typing_status_nicks, ptr_buffer); + if (items_count > 0) + weechat_bar_item_update (TYPING_BAR_ITEM_NAME); return WEECHAT_RC_OK; } @@ -290,12 +478,15 @@ typing_timer_cb (const void *pointer, void typing_setup_hooks () { - if (weechat_config_boolean (typing_config_look_enabled)) + if (weechat_config_boolean (typing_config_look_enabled_self)) { if (!typing_signal_buffer_closing) { if (weechat_typing_plugin->debug >= 2) - weechat_printf (NULL, "%s: creating hooks", TYPING_PLUGIN_NAME); + { + weechat_printf (NULL, "%s: creating hooks (self)", + TYPING_PLUGIN_NAME); + } typing_signal_buffer_closing = weechat_hook_signal ( "buffer_closing", &typing_buffer_closing_signal_cb, NULL, NULL); @@ -315,7 +506,10 @@ typing_setup_hooks () if (typing_signal_buffer_closing) { if (weechat_typing_plugin->debug >= 2) - weechat_printf (NULL, "%s: removing hooks", TYPING_PLUGIN_NAME); + { + weechat_printf (NULL, "%s: removing hooks (self)", + TYPING_PLUGIN_NAME); + } weechat_unhook (typing_signal_buffer_closing); typing_signal_buffer_closing = NULL; weechat_unhook (typing_signal_input_text_changed); @@ -326,6 +520,39 @@ typing_setup_hooks () typing_timer = NULL; } } + + if (weechat_config_boolean (typing_config_look_enabled_nicks)) + { + if (!typing_signal_typing_set_nick) + { + if (weechat_typing_plugin->debug >= 2) + { + weechat_printf (NULL, "%s: creating hooks (nicks)", + TYPING_PLUGIN_NAME); + } + typing_signal_typing_set_nick = weechat_hook_signal ( + "typing_set_nick", + &typing_typing_set_nick_signal_cb, NULL, NULL); + typing_signal_typing_reset_buffer = weechat_hook_signal ( + "typing_reset_buffer", + &typing_typing_reset_buffer_signal_cb, NULL, NULL); + } + } + else + { + if (typing_signal_typing_set_nick) + { + if (weechat_typing_plugin->debug >= 2) + { + weechat_printf (NULL, "%s: removing hooks (nicks)", + TYPING_PLUGIN_NAME); + } + weechat_unhook (typing_signal_typing_set_nick); + typing_signal_typing_set_nick = NULL; + weechat_unhook (typing_signal_typing_reset_buffer); + typing_signal_typing_reset_buffer = NULL; + } + } } /* @@ -348,6 +575,8 @@ weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[]) typing_setup_hooks (); + typing_bar_item_init (); + return WEECHAT_RC_OK; } |