irc, typing: display typing status for IRC nicks

This commit is contained in:
Sébastien Helleu 2021-07-02 21:54:27 +02:00
parent bba300e191
commit 954f943e8e
23 changed files with 1089 additions and 216 deletions

View File

@ -266,6 +266,8 @@
./src/plugins/irc/irc-server.h
./src/plugins/irc/irc-tag.c
./src/plugins/irc/irc-tag.h
./src/plugins/irc/irc-typing.c
./src/plugins/irc/irc-typing.h
./src/plugins/irc/irc-upgrade.c
./src/plugins/irc/irc-upgrade.h
./src/plugins/javascript/weechat-js-api.h
@ -404,6 +406,8 @@
./src/plugins/trigger/trigger-config.h
./src/plugins/trigger/trigger.h
./src/plugins/typing/typing.c
./src/plugins/typing/typing-bar-item.c
./src/plugins/typing/typing-bar-item.h
./src/plugins/typing/typing-config.c
./src/plugins/typing/typing-config.h
./src/plugins/typing/typing.h

View File

@ -267,6 +267,8 @@ SET(WEECHAT_SOURCES
./src/plugins/irc/irc-server.h
./src/plugins/irc/irc-tag.c
./src/plugins/irc/irc-tag.h
./src/plugins/irc/irc-typing.c
./src/plugins/irc/irc-typing.h
./src/plugins/irc/irc-upgrade.c
./src/plugins/irc/irc-upgrade.h
./src/plugins/javascript/weechat-js-api.h
@ -405,6 +407,8 @@ SET(WEECHAT_SOURCES
./src/plugins/trigger/trigger-config.h
./src/plugins/trigger/trigger.h
./src/plugins/typing/typing.c
./src/plugins/typing/typing-bar-item.c
./src/plugins/typing/typing-bar-item.h
./src/plugins/typing/typing-config.c
./src/plugins/typing/typing-config.h
./src/plugins/typing/typing.h

View File

@ -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 "")

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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 (

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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))

View File

@ -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);
}

View File

@ -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 */

View File

@ -43,6 +43,7 @@
#include "irc-redirect.h"
#include "irc-server.h"
#include "irc-tag.h"
#include "irc-typing.h"
#include "irc-upgrade.h"
@ -159,53 +160,6 @@ irc_signal_upgrade_cb (const void *pointer, void *data,
return WEECHAT_RC_OK;
}
/*
* 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",

View File

@ -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
)

View File

@ -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 \

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 ();

View File

@ -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);
}
weechat_hashtable_set (typing_status_self, buffer, new_typing_status);
/*
* Removes a nick typing status: key is a nick (string), value is a
* t_typing_status pointer.
*/
return new_typing_status;
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;
if (!ptr_nick || !ptr_typing_status)
return;
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;
}
}

View File

@ -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_hashtable *typing_status_nicks;
extern struct t_typing_status *typing_status_add (struct t_gui_buffer *buffer);
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 */

View File

@ -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, &current_time);
weechat_hashtable_map (typing_status_nicks,
&typing_status_nicks_hash_map_cb, &current_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;
}