typing: replace linked list with a hashtable to store typing status on buffers

This commit is contained in:
Sébastien Helleu 2021-07-01 21:26:34 +02:00
parent b108e97085
commit 7954dbc1f4
7 changed files with 278 additions and 354 deletions

View File

@ -19,8 +19,8 @@
add_library(typing MODULE
typing.c typing.h
typing-buffer.c typing-buffer.h
typing-config.c typing-config.h
typing-status.c typing-status.h
)
set_target_properties(typing PROPERTIES PREFIX "")

View File

@ -25,10 +25,10 @@ lib_LTLIBRARIES = typing.la
typing_la_SOURCES = typing.c \
typing.h \
typing-buffer.c \
typing-buffer.h \
typing-config.c \
typing-config.h
typing-config.h \
typing-status.c \
typing-status.h
typing_la_LDFLAGS = -module -no-undefined
typing_la_LIBADD = $(TYPING_LFLAGS)

View File

@ -1,198 +0,0 @@
/*
* typing-buffer.c - typing buffer list management
*
* 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/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../weechat-plugin.h"
#include "typing.h"
#include "typing-buffer.h"
struct t_typing_buffer *typing_buffers = NULL;
struct t_typing_buffer *last_typing_buffer = NULL;
/*
* Checks if a typing buffer pointer is valid.
*
* Returns:
* 1: typing buffer exists
* 0: typing buffer does not exist
*/
int
typing_buffer_valid (struct t_typing_buffer *typing_buffer)
{
struct t_typing_buffer *ptr_typing_buffer;
if (!typing_buffer)
return 0;
for (ptr_typing_buffer = typing_buffers; ptr_typing_buffer;
ptr_typing_buffer = ptr_typing_buffer->next_buffer)
{
if (ptr_typing_buffer == typing_buffer)
return 1;
}
/* typing_buffer not found */
return 0;
}
/*
* Adds a new buffer for typing status.
*
* Returns pointer to new typing buffer, NULL if error.
*/
struct t_typing_buffer *
typing_buffer_add (struct t_gui_buffer *buffer)
{
struct t_typing_buffer *new_typing_buffer;
if (!buffer)
return NULL;
if (weechat_typing_plugin->debug)
{
weechat_printf_date_tags (NULL, 0, "no_log",
"%s: start typing for buffer \"%s\"",
TYPING_PLUGIN_NAME,
weechat_buffer_get_string (buffer, "name"));
}
new_typing_buffer = malloc (sizeof (*new_typing_buffer));
if (new_typing_buffer)
{
new_typing_buffer->buffer = buffer;
new_typing_buffer->status = TYPING_BUFFER_STATUS_OFF;
new_typing_buffer->last_typed = 0;
new_typing_buffer->last_signal_sent = 0;
new_typing_buffer->prev_buffer = last_typing_buffer;
new_typing_buffer->next_buffer = NULL;
if (last_typing_buffer)
last_typing_buffer->next_buffer = new_typing_buffer;
else
typing_buffers = new_typing_buffer;
last_typing_buffer = new_typing_buffer;
}
return new_typing_buffer;
}
/*
* Searches for typing buffer by buffer pointer.
*
* Returns pointer to typing buffer found, NULL if not found.
*/
struct t_typing_buffer *
typing_buffer_search_buffer (struct t_gui_buffer *buffer)
{
struct t_typing_buffer *ptr_typing_buffer;
for (ptr_typing_buffer = typing_buffers; ptr_typing_buffer;
ptr_typing_buffer = ptr_typing_buffer->next_buffer)
{
if (ptr_typing_buffer->buffer == buffer)
return ptr_typing_buffer;
}
/* typing buffer not found */
return NULL;
}
/*
* Removes a typing buffer from list.
*/
void
typing_buffer_free (struct t_typing_buffer *typing_buffer)
{
struct t_typing_buffer *new_typing_buffers;
struct t_gui_buffer *ptr_buffer;
if (!typing_buffer)
return;
ptr_buffer = typing_buffer->buffer;
/* remove typing buffer */
if (last_typing_buffer == typing_buffer)
last_typing_buffer = typing_buffer->prev_buffer;
if (typing_buffer->prev_buffer)
{
(typing_buffer->prev_buffer)->next_buffer = typing_buffer->next_buffer;
new_typing_buffers = typing_buffers;
}
else
new_typing_buffers = typing_buffer->next_buffer;
if (typing_buffer->next_buffer)
(typing_buffer->next_buffer)->prev_buffer = typing_buffer->prev_buffer;
free (typing_buffer);
typing_buffers = new_typing_buffers;
if (weechat_typing_plugin->debug)
{
weechat_printf_date_tags (
NULL, 0, "no_log",
"%s: stop typing for buffer \"%s\"",
TYPING_PLUGIN_NAME,
weechat_buffer_get_string (ptr_buffer, "name"));
}
}
/*
* Adds a typing buffer in an infolist.
*
* Returns:
* 1: OK
* 0: error
*/
int
typing_buffer_add_to_infolist (struct t_infolist *infolist,
struct t_typing_buffer *typing_buffer)
{
struct t_infolist_item *ptr_item;
if (!infolist || !typing_buffer)
return 0;
ptr_item = weechat_infolist_new_item (infolist);
if (!ptr_item)
return 0;
if (!weechat_infolist_new_var_pointer (ptr_item, "buffer", typing_buffer->buffer))
return 0;
return 1;
}

View File

@ -1,60 +0,0 @@
/*
* 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_BUFFER_H
#define WEECHAT_PLUGIN_TYPING_BUFFER_H
#include <stdio.h>
#include <time.h>
struct t_infolist;
enum t_typing_buffer_status
{
TYPING_BUFFER_STATUS_OFF = 0,
TYPING_BUFFER_STATUS_TYPING,
TYPING_BUFFER_STATUS_PAUSED,
TYPING_BUFFER_STATUS_CLEARED,
/* number of typing buffer statuses */
TYPING_BUFFER_NUM_STATUSES,
};
/* own typing status */
struct t_typing_buffer
{
struct t_gui_buffer *buffer; /* pointer to buffer */
int status; /* status */
time_t last_typed; /* last char typed */
time_t last_signal_sent; /* last signal sent */
struct t_typing_buffer *prev_buffer; /* link to previous buffer */
struct t_typing_buffer *next_buffer; /* link to next buffer */
};
extern struct t_typing_buffer *typing_buffers;
extern struct t_typing_buffer *last_typing_buffer;
extern int typing_buffer_valid (struct t_typing_buffer *typing_buffer);
extern struct t_typing_buffer *typing_buffer_add (struct t_gui_buffer *buffer);
extern struct t_typing_buffer *typing_buffer_search_buffer (struct t_gui_buffer *buffer);
extern void typing_buffer_free (struct t_typing_buffer *typing_buffer);
extern int typing_buffer_add_to_infolist (struct t_infolist *infolist,
struct t_typing_buffer *typing_buffer);
#endif /* WEECHAT_PLUGIN_TYPING_BUFFER_H */

View File

@ -0,0 +1,133 @@
/*
* typing-status.c - manage self and other users typing status
*
* 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/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "../weechat-plugin.h"
#include "typing.h"
#include "typing-status.h"
struct t_hashtable *typing_status_self = NULL;
/*
* Removes a typing status.
*/
void
typing_status_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;
/* make C compiler happy */
(void) hashtable;
ptr_buffer = (struct t_gui_buffer *)key;
ptr_typing_status = (struct t_typing_status *)value;
if (!ptr_typing_status)
return;
if (weechat_typing_plugin->debug)
{
weechat_printf_date_tags (
NULL, 0, "no_log",
"%s: stop typing status for buffer \"%s\"",
TYPING_PLUGIN_NAME,
weechat_buffer_get_string (ptr_buffer, "name"));
}
free (ptr_typing_status);
}
/*
* Adds a new typing status.
*
* Returns:
* 1: OK
* 0: error
*/
struct t_typing_status *
typing_status_add (struct t_gui_buffer *buffer)
{
struct t_typing_status *new_typing_status;
if (!buffer)
return NULL;
if (!typing_status_self)
{
typing_status_self = weechat_hashtable_new (64,
WEECHAT_HASHTABLE_POINTER,
WEECHAT_HASHTABLE_POINTER,
NULL,
NULL);
if (!typing_status_self)
return NULL;
weechat_hashtable_set_pointer (typing_status_self,
"callback_free_value",
&typing_status_free_value_cb);
}
new_typing_status = malloc (sizeof (*new_typing_status));
if (!new_typing_status)
return NULL;
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"));
}
new_typing_status->status = TYPING_STATUS_STATUS_OFF;
new_typing_status->last_typed = 0;
new_typing_status->last_signal_sent = 0;
weechat_hashtable_set (typing_status_self, buffer, new_typing_status);
return new_typing_status;
}
/*
* Ends typing status.
*/
void
typing_status_end ()
{
if (typing_status_self)
{
weechat_hashtable_free (typing_status_self);
typing_status_self = NULL;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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_STATUS_H
#define WEECHAT_PLUGIN_TYPING_STATUS_H
#include <stdio.h>
#include <time.h>
struct t_infolist;
enum t_typing_status_status
{
TYPING_STATUS_STATUS_OFF = 0,
TYPING_STATUS_STATUS_TYPING,
TYPING_STATUS_STATUS_PAUSED,
TYPING_STATUS_STATUS_CLEARED,
/* number of typing status statuses */
TYPING_STATUS_NUM_STATUSES,
};
/* self typing status */
struct t_typing_status
{
int status; /* status */
time_t last_typed; /* last char typed */
time_t last_signal_sent; /* last signal sent */
};
extern struct t_hashtable *typing_status_self;
extern struct t_typing_status *typing_status_add (struct t_gui_buffer *buffer);
extern void typing_status_end ();
#endif /* WEECHAT_PLUGIN_TYPING_STATUS_H */

View File

@ -26,8 +26,8 @@
#include "../weechat-plugin.h"
#include "typing.h"
#include "typing-buffer.h"
#include "typing-config.h"
#include "typing-status.h"
WEECHAT_PLUGIN_NAME(TYPING_PLUGIN_NAME);
@ -52,7 +52,8 @@ struct t_hook *typing_timer = NULL;
*/
int
typing_send_signal (struct t_typing_buffer *typing_buffer,
typing_send_signal (struct t_gui_buffer *buffer,
struct t_typing_status *typing_status,
const char *signal_name)
{
if (weechat_typing_plugin->debug)
@ -60,30 +61,13 @@ typing_send_signal (struct t_typing_buffer *typing_buffer,
weechat_printf (NULL, "%s: sending signal \"%s\" for buffer %s",
TYPING_PLUGIN_NAME,
signal_name,
weechat_buffer_get_string (typing_buffer->buffer,
"full_name"));
weechat_buffer_get_string (buffer, "full_name"));
}
typing_buffer->last_signal_sent = time (NULL);
typing_status->last_signal_sent = time (NULL);
return weechat_hook_signal_send (signal_name,
WEECHAT_HOOK_SIGNAL_POINTER,
typing_buffer->buffer);
}
/*
* Gets typing buffer, creates it if not existing.
*/
struct t_typing_buffer *
typing_get_typing_buffer (struct t_gui_buffer *buffer)
{
struct t_typing_buffer *ptr_typing_buffer;
ptr_typing_buffer = typing_buffer_search_buffer (buffer);
if (ptr_typing_buffer)
return ptr_typing_buffer;
return typing_buffer_add (buffer);
buffer);
}
/*
@ -95,17 +79,13 @@ typing_buffer_closing_signal_cb (const void *pointer, void *data,
const char *signal,
const char *type_data, void *signal_data)
{
struct t_typing_buffer *ptr_typing_buffer;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
ptr_typing_buffer = typing_buffer_search_buffer (signal_data);
if (ptr_typing_buffer)
typing_buffer_free (ptr_typing_buffer);
weechat_hashtable_remove (typing_status_self, signal_data);
return WEECHAT_RC_OK;
}
@ -122,7 +102,7 @@ typing_input_text_changed_signal_cb (const void *pointer, void *data,
int text_search;
const char *ptr_input, *ptr_input_for_buffer;
struct t_gui_buffer *ptr_buffer;
struct t_typing_buffer *ptr_typing_buffer;
struct t_typing_status *ptr_typing_status;
/* make C compiler happy */
(void) pointer;
@ -146,28 +126,30 @@ typing_input_text_changed_signal_cb (const void *pointer, void *data,
if (!ptr_input_for_buffer)
return WEECHAT_RC_OK;
ptr_typing_buffer = typing_buffer_search_buffer (ptr_buffer);
if (!ptr_typing_buffer)
ptr_typing_buffer = typing_buffer_add (ptr_buffer);
if (!ptr_typing_buffer)
ptr_typing_status = weechat_hashtable_get (typing_status_self,
ptr_buffer);
if (!ptr_typing_status)
ptr_typing_status = typing_status_add (ptr_buffer);
if (!ptr_typing_status)
return WEECHAT_RC_OK;
ptr_typing_buffer->status = TYPING_BUFFER_STATUS_TYPING;
ptr_typing_buffer->last_typed = time (NULL);
ptr_typing_status->status = TYPING_STATUS_STATUS_TYPING;
ptr_typing_status->last_typed = time (NULL);
}
else
{
/* user was typing something? */
ptr_typing_buffer = typing_buffer_search_buffer (ptr_buffer);
if (ptr_typing_buffer
&& ((ptr_typing_buffer->status == TYPING_BUFFER_STATUS_TYPING)
|| (ptr_typing_buffer->status == TYPING_BUFFER_STATUS_PAUSED)))
ptr_typing_status = weechat_hashtable_get (typing_status_self,
ptr_buffer);
if (ptr_typing_status
&& ((ptr_typing_status->status == TYPING_STATUS_STATUS_TYPING)
|| (ptr_typing_status->status == TYPING_STATUS_STATUS_PAUSED)))
{
/*
* input cleared: maybe something was sent, not sure, so we just
* set the status to "cleared", a signal can be sent later
* in timer
*/
ptr_typing_buffer->status = TYPING_BUFFER_STATUS_CLEARED;
ptr_typing_status->status = TYPING_STATUS_STATUS_CLEARED;
}
}
@ -189,7 +171,7 @@ typing_input_text_for_buffer_modifier_cb (const void *pointer,
unsigned long value;
const char *ptr_input_for_buffer;
struct t_gui_buffer *ptr_buffer;
struct t_typing_buffer *ptr_typing_buffer;
struct t_typing_status *ptr_typing_status;
/* make C compiler happy */
(void) pointer;
@ -212,18 +194,75 @@ typing_input_text_for_buffer_modifier_cb (const void *pointer,
if (!ptr_input_for_buffer)
return NULL;
ptr_typing_buffer = typing_buffer_search_buffer (ptr_buffer);
if (!ptr_typing_buffer)
ptr_typing_buffer = typing_buffer_add (ptr_buffer);
if (!ptr_typing_buffer)
ptr_typing_status = weechat_hashtable_get (typing_status_self, ptr_buffer);
if (!ptr_typing_status)
ptr_typing_status = typing_status_add (ptr_buffer);
if (!ptr_typing_status)
return NULL;
typing_send_signal (ptr_typing_buffer, "typing_sent");
typing_buffer_free (ptr_typing_buffer);
typing_send_signal (ptr_buffer, ptr_typing_status, "typing_sent");
weechat_hashtable_remove (typing_status_self, ptr_buffer);
return NULL;
}
/*
* Callback called periodically (via a timer) for each entry in hashtable
* "typing_status_self".
*/
void
typing_status_self_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;
int delay_pause;
/* make C compiler happy */
(void) 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)
{
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)
{
ptr_typing_status->status = TYPING_STATUS_STATUS_PAUSED;
typing_send_signal (ptr_buffer, ptr_typing_status,
"typing_paused");
weechat_hashtable_remove (hashtable, ptr_buffer);
}
else
{
typing_send_signal (ptr_buffer, ptr_typing_status,
"typing_active");
}
}
else
{
typing_send_signal (ptr_buffer, ptr_typing_status,
"typing_cleared");
weechat_hashtable_remove (hashtable, ptr_buffer);
}
}
else if (ptr_typing_status->status == TYPING_STATUS_STATUS_CLEARED)
{
typing_send_signal (ptr_buffer, ptr_typing_status,
"typing_cleared");
weechat_hashtable_remove (hashtable, ptr_buffer);
}
}
/*
* Callback for modifier "input_text_for_buffer".
*/
@ -233,57 +272,13 @@ typing_timer_cb (const void *pointer,
void *data,
int remaining_calls)
{
time_t time_now;
int delay_pause;
const char *ptr_input, *ptr_input_for_buffer;
struct t_typing_buffer *ptr_typing_buffer, *ptr_next_typing_buffer;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) remaining_calls;
delay_pause = weechat_config_integer (typing_config_look_delay_pause);
time_now = time (NULL);
ptr_typing_buffer = typing_buffers;
while (ptr_typing_buffer)
{
ptr_next_typing_buffer = ptr_typing_buffer->next_buffer;
if (ptr_typing_buffer->status == TYPING_BUFFER_STATUS_TYPING)
{
ptr_input = weechat_buffer_get_string (ptr_typing_buffer->buffer,
"input");
ptr_input_for_buffer = weechat_string_input_for_buffer (ptr_input);
if (ptr_input_for_buffer)
{
/* check if typing is paused */
if (ptr_typing_buffer->last_typed < time_now - delay_pause)
{
ptr_typing_buffer->status = TYPING_BUFFER_STATUS_PAUSED;
typing_send_signal (ptr_typing_buffer, "typing_paused");
typing_buffer_free (ptr_typing_buffer);
}
else
{
typing_send_signal (ptr_typing_buffer, "typing_active");
}
}
else
{
typing_send_signal (ptr_typing_buffer, "typing_cleared");
typing_buffer_free (ptr_typing_buffer);
}
}
else if (ptr_typing_buffer->status == TYPING_BUFFER_STATUS_CLEARED)
{
typing_send_signal (ptr_typing_buffer, "typing_cleared");
typing_buffer_free (ptr_typing_buffer);
}
ptr_typing_buffer = ptr_next_typing_buffer;
}
weechat_hashtable_map (typing_status_self,
&typing_status_self_map_cb, NULL);
return WEECHAT_RC_OK;
}
@ -369,5 +364,7 @@ weechat_plugin_end (struct t_weechat_plugin *plugin)
typing_config_write ();
typing_config_free ();
typing_status_end ();
return WEECHAT_RC_OK;
}