--- a/libpurple/meson.build Fri Mar 03 01:22:08 2023 -0600
+++ b/libpurple/meson.build Fri Mar 03 01:23:31 2023 -0600
@@ -194,6 +194,7 @@
'purplesqlitehistoryadapter.h',
'purplewhiteboardmanager.h',
@@ -271,6 +272,7 @@
--- a/libpurple/purpleconversationmember.c Fri Mar 03 01:22:08 2023 -0600
+++ b/libpurple/purpleconversationmember.c Fri Mar 03 01:23:31 2023 -0600
@@ -18,17 +18,23 @@
#include "purpleconversationmember.h"
+#include "purpleenums.h" struct _PurpleConversationMember {
PurpleContactInfo *contact_info;
+ PurpleTypingState typing_state; static GParamSpec *properties[N_PROPERTIES] = {NULL, };
@@ -53,6 +59,20 @@
/******************************************************************************
+ *****************************************************************************/ +purple_conversation_member_reset_typing_state(gpointer data) { + PurpleConversationMember *member = data; + purple_conversation_member_set_typing_state(member, + PURPLE_TYPING_STATE_NONE, + return G_SOURCE_REMOVE; +/****************************************************************************** *****************************************************************************/
@@ -70,6 +90,10 @@
g_value_set_object(value,
purple_conversation_member_get_tags(member));
+ case PROP_TYPING_STATE: + g_value_set_enum(value, + purple_conversation_member_get_typing_state(member)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -87,6 +111,11 @@
purple_conversation_member_set_contact_info(member,
g_value_get_object(value));
+ case PROP_TYPING_STATE: + purple_conversation_member_set_typing_state(member, + g_value_get_enum(value), G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
@@ -99,6 +128,11 @@
g_clear_object(&member->contact_info);
+ if(member->typing_timeout != 0) { + g_source_remove(member->typing_timeout); + member->typing_timeout = 0; G_OBJECT_CLASS(purple_conversation_member_parent_class)->dispose(obj);
@@ -151,6 +185,20 @@
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ * PurpleConversationMember:typing-state: + * The [enum@Purple.TypingState] for this member. + properties[PROP_TYPING_STATE] = g_param_spec_enum( + "typing-state", "typing-state", + "The typing state for this member", + PURPLE_TYPE_TYPING_STATE, + PURPLE_TYPING_STATE_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
@@ -180,3 +228,45 @@
+purple_conversation_member_get_typing_state(PurpleConversationMember *member) + g_return_val_if_fail(PURPLE_IS_CONVERSATION_MEMBER(member), + PURPLE_TYPING_STATE_NONE); + return member->typing_state; +purple_conversation_member_set_typing_state(PurpleConversationMember *member, + PurpleTypingState state, + g_return_if_fail(PURPLE_IS_CONVERSATION_MEMBER(member)); + /* Remove an existing timeout if necessary. */ + if(member->typing_timeout != 0) { + g_source_remove(member->typing_timeout); + member->typing_timeout = 0; + /* If the state has changed, notify. */ + if(state != member->typing_state) { + member->typing_state = state; + g_object_notify_by_pspec(G_OBJECT(member), + properties[PROP_TYPING_STATE]); + /* If we got a timeout, add it. */ + source = g_timeout_add_seconds(seconds, + purple_conversation_member_reset_typing_state, + member->typing_timeout = source; --- a/libpurple/purpleconversationmember.h Fri Mar 03 01:22:08 2023 -0600
+++ b/libpurple/purpleconversationmember.h Fri Mar 03 01:23:31 2023 -0600
@@ -28,6 +28,7 @@
#include <libpurple/purplecontactinfo.h>
#include <libpurple/purpletags.h>
+#include <libpurple/purpletyping.h> @@ -89,6 +90,33 @@
PurpleTags *purple_conversation_member_get_tags(PurpleConversationMember *conversation_member);
+ * purple_conversation_member_get_typing_state: + * @member: The instance. + * Gets the current [enum@Purple.TypingState] for @conversation_member. + * Returns: The current typing state for @conversation_member. +PurpleTypingState purple_conversation_member_get_typing_state(PurpleConversationMember *member); + * purple_conversation_member_set_typing_state: + * @member: The instance. + * @state: The new typing state. + * @seconds: The number of seconds before resetting the state. + * Sets the typing state of @conversation_member to @state. + * If @seconds is greater than %0, a timeout will be added for @seconds to + * reset the state to none. +void purple_conversation_member_set_typing_state(PurpleConversationMember *member, PurpleTypingState state, guint seconds); #endif /* PURPLE_CONVERSATION_MEMBER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purpletyping.h Fri Mar 03 01:23:31 2023 -0600
@@ -0,0 +1,44 @@
+ * Purple - Internet Messaging Library + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * This program 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 2 of the License, or + * (at your option) any later version. + * This program 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 this program; if not, see <https://www.gnu.org/licenses/>. +#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION) +# error "only <pidgin.h> may be included directly" + * @PURPLE_TYPING_STATE_NONE: The user is not currently typing and has nothing + * @PURPLE_TYPING_STATE_TYPING: The user is currently typing. + * @PURPLE_TYPING_STATE_PAUSED: The user has typed some text, but stopped + * Defines the state of a user composing a message. + PURPLE_TYPING_STATE_NONE, + PURPLE_TYPING_STATE_TYPING, + PURPLE_TYPING_STATE_PAUSED, +#endif /* PURPLE_TYPING_H */ --- a/libpurple/tests/test_conversation_member.c Fri Mar 03 01:22:08 2023 -0600
+++ b/libpurple/tests/test_conversation_member.c Fri Mar 03 01:23:31 2023 -0600
@@ -44,6 +44,7 @@
PurpleContactInfo *info1 = NULL;
PurpleConversationMember *member = NULL;
+ PurpleTypingState typing_state = PURPLE_TYPING_STATE_NONE; info = purple_contact_info_new("abc123");
@@ -54,17 +55,20 @@
PURPLE_TYPE_CONVERSATION_MEMBER,
+ "typing-state", PURPLE_TYPING_STATE_TYPING, /* Now use g_object_get to read all of the properties. */
+ "typing-state", &typing_state, /* Compare all the things. */
g_assert_true(info1 == info);
g_assert_true(PURPLE_IS_TAGS(tags));
+ g_assert_cmpint(typing_state, ==, PURPLE_TYPING_STATE_TYPING); /* Free/unref all the things. */
@@ -75,6 +79,79 @@
/******************************************************************************
+ *****************************************************************************/ +test_purple_conversation_manager_timeout_notify(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + GMainLoop *loop = data; + static guint count = 0; + /* Increment count each time we're called. We're expecting to be called + * twice, so after that quit the main loop. + g_main_loop_quit(loop); +test_purple_conversation_manager_timeout_fail_safe(gpointer data) { + GMainLoop *loop = data; + g_warning("fail safe triggered"); + g_main_loop_quit(loop); + return G_SOURCE_REMOVE; +test_purple_conversation_member_typing_state_timeout(void) { + PurpleContactInfo *info = NULL; + PurpleConversationMember *member = NULL; + PurpleTypingState typing_state = PURPLE_TYPING_STATE_TYPING; + GMainLoop *loop = NULL; + /* Create the main loop as we'll need it to let the timeout fire. */ + loop = g_main_loop_new(NULL, FALSE); + /* Create the member and add a notify callback on the typing-state property + * so we can check it and exit the main loop. + info = purple_contact_info_new(NULL); + member = purple_conversation_member_new(info); + g_signal_connect(member, "notify::typing-state", + G_CALLBACK(test_purple_conversation_manager_timeout_notify), + /* Set the state to typing with a timeout of 1 second. */ + purple_conversation_member_set_typing_state(member, + PURPLE_TYPING_STATE_TYPING, 1); + /* Add a fail safe timeout at 2 seconds to make sure the test won't get + * stuck waiting forever. + g_timeout_add_seconds(2, + test_purple_conversation_manager_timeout_fail_safe, + /* Run the main loop and let the timeouts fire. */ + /* Verify that our state got reset back to PURPLE_TYPING_STATE_NONE. */ + typing_state = purple_conversation_member_get_typing_state(member); + g_assert_cmpint(typing_state, ==, PURPLE_TYPING_STATE_NONE); + /* Clean everything up. */ + g_clear_object(&member); +/****************************************************************************** *****************************************************************************/
@@ -86,5 +163,8 @@
g_test_add_func("/conversation-member/properties",
test_purple_conversation_member_properties);
+ g_test_add_func("/conversation-member/typing-state/timeout", + test_purple_conversation_member_typing_state_timeout);