Propagate the notify signal for the Presence object on a ContactInfo

Fri, 03 Mar 2023 04:49:01 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 03 Mar 2023 04:49:01 -0600
changeset 42115
4f06e6f47a78
parent 42114
d3fe2b899c89
child 42116
d80a59dc4c91

Propagate the notify signal for the Presence object on a ContactInfo

Testing Done:
Ran the unit tests under valgrind and verified now no leaks.
Also checked the docs and made sure the signal was documented properly.

Reviewed at https://reviews.imfreedom.org/r/2306/

libpurple/purplecontactinfo.c file | annotate | diff | comparison | revisions
libpurple/tests/test_contact_info.c file | annotate | diff | comparison | revisions
--- a/libpurple/purplecontactinfo.c	Fri Mar 03 01:24:36 2023 -0600
+++ b/libpurple/purplecontactinfo.c	Fri Mar 03 04:49:01 2023 -0600
@@ -59,6 +59,12 @@
 };
 static GParamSpec *properties[N_PROPERTIES] = {NULL, };
 
+enum {
+	SIG_PRESENCE_CHANGED,
+	N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {0, };
+
 G_DEFINE_TYPE_WITH_PRIVATE(PurpleContactInfo, purple_contact_info,
                            G_TYPE_OBJECT)
 
@@ -126,6 +132,19 @@
 	purple_contact_info_update_name_for_display(data);
 }
 
+/*
+ * This is a notify callback on the presence for a contact info, it is used
+ * to emit the presence-changed signal.
+ */
+static void
+purple_contact_info_presence_notify_cb(GObject *source, GParamSpec *pspec,
+                                       gpointer data)
+{
+	g_signal_emit(data, signals[SIG_PRESENCE_CHANGED],
+	              g_param_spec_get_name_quark(pspec),
+	              source, pspec);
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -271,7 +290,11 @@
 	priv = purple_contact_info_get_instance_private(info);
 
 	priv->tags = purple_tags_new();
+
 	priv->presence = g_object_new(PURPLE_TYPE_PRESENCE, NULL);
+	g_signal_connect_object(priv->presence, "notify",
+	                        G_CALLBACK(purple_contact_info_presence_notify_cb),
+	                        info, 0);
 }
 
 static void
@@ -451,6 +474,34 @@
 		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+	/**
+	 * PurpleContactInfo::presence-changed:
+	 * @info: The instance.
+	 * @presence: The presence that was changed.
+	 * @pspec: The [class@GObject.ParamSpec] for the property that changed.
+	 *
+	 * This is a propagation of the notify signal from @presence. This means
+	 * that your callback will be called when anything in the presence changes.
+	 *
+	 * This also supports details, so you can specify the signal name as
+	 * something like `presence-changed::message` and your callback will only
+	 * be called when the message property of @presence has been changed.
+	 *
+	 * Since: 3.0.0
+	 */
+	signals[SIG_PRESENCE_CHANGED] = g_signal_new_class_handler(
+		"presence-changed",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		2,
+		PURPLE_TYPE_PRESENCE,
+		G_TYPE_PARAM);
 }
 
 /******************************************************************************
--- a/libpurple/tests/test_contact_info.c	Fri Mar 03 01:24:36 2023 -0600
+++ b/libpurple/tests/test_contact_info.c	Fri Mar 03 04:49:01 2023 -0600
@@ -382,6 +382,57 @@
 }
 
 /******************************************************************************
+ * presence-changed signal tests
+ *****************************************************************************/
+static void
+test_purple_contact_info_presence_changed_callback(PurpleContactInfo *info,
+                                                   PurplePresence *presence,
+                                                   GParamSpec *pspec,
+                                                   gpointer data)
+{
+	guint *counter = data;
+
+	g_assert_true(PURPLE_IS_CONTACT_INFO(info));
+	g_assert_true(PURPLE_IS_PRESENCE(presence));
+	g_assert_true(G_IS_PARAM_SPEC(pspec));
+
+	*counter = *counter + 1;
+}
+
+static void
+test_purple_contact_info_presence_changed_signal(void) {
+	PurpleContactInfo *info = NULL;
+	PurplePresence *presence = NULL;
+	guint counter = 0;
+
+	/* Create the info and add our callbacks, one for everything and another
+	 * for just idle to make sure detail works.
+	 */
+	info = purple_contact_info_new(NULL);
+	g_signal_connect(info, "presence-changed",
+	                 G_CALLBACK(test_purple_contact_info_presence_changed_callback),
+	                 &counter);
+	g_signal_connect(info, "presence-changed::idle",
+	                 G_CALLBACK(test_purple_contact_info_presence_changed_callback),
+	                 &counter);
+
+	/* Grab the presence and start changing stuff. */
+	presence = purple_contact_info_get_presence(info);
+	g_assert_true(PURPLE_IS_PRESENCE(presence));
+
+	/* Set the presence as idle with no time, which should call our callback
+	 * three times, twice for the non-detailed callback, and once for the
+	 * detailed callback.
+	 */
+	g_assert_cmpint(counter, ==, 0);
+	purple_presence_set_idle(presence, TRUE, NULL);
+	g_assert_cmpint(counter, ==, 3);
+
+	/* Cleanup. */
+	g_clear_object(&info);
+}
+
+/******************************************************************************
  * Main
  *****************************************************************************/
 gint
@@ -432,5 +483,8 @@
 	g_test_add_func("/contact-info/matches/none",
 	                test_purple_contact_info_matches_none);
 
+	g_test_add_func("/contact-info/presence-changed-signal",
+	                test_purple_contact_info_presence_changed_signal);
+
 	return g_test_run();
 }

mercurial