pidgin/pidgin

f755b10c05a3
Update the demo protocol plugin to not depend on conversation names

We're starting to phase out conversation names as they don't work well with
modern stuff. So this goes ahead and fixes the not interactions in the demo
protocol.

I also generated the echo message in the send_message_async rather than in the
callback.

Testing Done:
Ran the turtles and send some messages to Echo on the demo protocol.

Reviewed at https://reviews.imfreedom.org/r/3135/
/*
* Purple - Internet Messaging Library
* Copyright (C) Pidgin Developers <devel@pidgin.im>
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This library 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 library 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 library; if not, see <https://www.gnu.org/licenses/>.
*/
#include "debug.h"
#include "signals.h"
/* must include this to use G_VA_COPY */
#include <string.h>
typedef struct
{
void *instance;
GHashTable *signals;
size_t signal_count;
gulong next_signal_id;
} PurpleInstanceData;
typedef struct
{
gulong id;
PurpleSignalMarshalFunc marshal;
int num_values;
GType *value_types;
GType ret_type;
GList *handlers;
size_t handler_count;
gulong next_handler_id;
} PurpleSignalData;
typedef struct
{
gulong id;
GCallback cb;
void *handle;
void *data;
gboolean use_vargs;
int priority;
} PurpleSignalHandlerData;
static GHashTable *instance_table = NULL;
static void
destroy_instance_data(PurpleInstanceData *instance_data)
{
g_hash_table_destroy(instance_data->signals);
g_free(instance_data);
}
static void
destroy_signal_data(PurpleSignalData *signal_data)
{
g_list_free_full(signal_data->handlers, g_free);
g_free(signal_data->value_types);
g_free(signal_data);
}
gulong
purple_signal_register(void *instance, const char *signal,
PurpleSignalMarshalFunc marshal,
GType ret_type, int num_values, ...)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
va_list args;
g_return_val_if_fail(instance != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(marshal != NULL, 0);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
if (instance_data == NULL)
{
instance_data = g_new0(PurpleInstanceData, 1);
instance_data->instance = instance;
instance_data->next_signal_id = 1;
instance_data->signals =
g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
(GDestroyNotify)destroy_signal_data);
g_hash_table_insert(instance_table, instance, instance_data);
}
signal_data = g_new0(PurpleSignalData, 1);
signal_data->id = instance_data->next_signal_id;
signal_data->marshal = marshal;
signal_data->next_handler_id = 1;
signal_data->ret_type = ret_type;
signal_data->num_values = num_values;
if (num_values > 0)
{
int i;
signal_data->value_types = g_new0(GType, num_values);
va_start(args, num_values);
for (i = 0; i < num_values; i++)
signal_data->value_types[i] = va_arg(args, GType);
va_end(args);
}
g_hash_table_insert(instance_data->signals,
g_strdup(signal), signal_data);
instance_data->next_signal_id++;
instance_data->signal_count++;
return signal_data->id;
}
void
purple_signal_unregister(void *instance, const char *signal)
{
PurpleInstanceData *instance_data;
g_return_if_fail(instance != NULL);
g_return_if_fail(signal != NULL);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
g_return_if_fail(instance_data != NULL);
g_hash_table_remove(instance_data->signals, signal);
instance_data->signal_count--;
if (instance_data->signal_count == 0)
{
/* Unregister the instance. */
g_hash_table_remove(instance_table, instance);
}
}
void
purple_signals_unregister_by_instance(void *instance)
{
g_return_if_fail(instance != NULL);
g_hash_table_remove(instance_table, instance);
/*
* Makes things easier (more annoying?) for developers who don't have
* things registering and unregistering in the right order :)
*/
/* g_return_if_fail(found); */
}
static gint handler_priority(void * a, void * b) {
PurpleSignalHandlerData *ah = (PurpleSignalHandlerData*)a;
PurpleSignalHandlerData *bh = (PurpleSignalHandlerData*)b;
if (ah->priority > bh->priority) return 1;
if (ah->priority < bh->priority) return -1;
return 0;
}
static gulong
signal_connect_common(void *instance, const char *signal, void *handle,
GCallback func, void *data, int priority, gboolean use_vargs)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
g_return_val_if_fail(instance != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(handle != NULL, 0);
g_return_val_if_fail(func != NULL, 0);
/* Get the instance data */
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
if (instance_data == NULL)
{
purple_debug_warning("signals", "Something tried to register a callback "
"for the '%s' signal, but we do not have any signals "
"registered with the given handle\n", signal);
g_return_val_if_reached(0);
}
/* Get the signal data */
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL) {
purple_debug_error("signals", "Signal data for %s not found!", signal);
return 0;
}
/* Create the signal handler data */
handler_data = g_new0(PurpleSignalHandlerData, 1);
handler_data->id = signal_data->next_handler_id;
handler_data->cb = func;
handler_data->handle = handle;
handler_data->data = data;
handler_data->use_vargs = use_vargs;
handler_data->priority = priority;
signal_data->handlers = g_list_insert_sorted(signal_data->handlers, handler_data, (GCompareFunc)handler_priority);
signal_data->handler_count++;
signal_data->next_handler_id++;
return handler_data->id;
}
gulong
purple_signal_connect_priority(void *instance, const char *signal, void *handle,
GCallback func, void *data, int priority)
{
return signal_connect_common(instance, signal, handle, func, data, priority, FALSE);
}
gulong
purple_signal_connect(void *instance, const char *signal, void *handle,
GCallback func, void *data)
{
return signal_connect_common(instance, signal, handle, func, data, PURPLE_SIGNAL_PRIORITY_DEFAULT, FALSE);
}
void
purple_signal_disconnect(void *instance, const char *signal,
void *handle, GCallback func)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
GList *l;
gboolean found = FALSE;
g_return_if_fail(instance != NULL);
g_return_if_fail(signal != NULL);
g_return_if_fail(handle != NULL);
g_return_if_fail(func != NULL);
/* Get the instance data */
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
g_return_if_fail(instance_data != NULL);
/* Get the signal data */
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL) {
purple_debug_error("signals", "Signal data for %s not found!", signal);
return;
}
/* Find the handler data. */
for (l = signal_data->handlers; l != NULL; l = l->next)
{
handler_data = (PurpleSignalHandlerData *)l->data;
if (handler_data->handle == handle && handler_data->cb == func)
{
g_free(handler_data);
signal_data->handlers = g_list_delete_link(signal_data->handlers,
l);
signal_data->handler_count--;
found = TRUE;
break;
}
}
/* See note somewhere about this actually helping developers.. */
g_return_if_fail(found);
}
/*
* TODO: Make this all more efficient by storing a list of handlers, keyed
* to a handle.
*/
static void
disconnect_handle_from_signals(G_GNUC_UNUSED const char *signal,
PurpleSignalData *signal_data, void *handle)
{
GList *l, *l_next;
PurpleSignalHandlerData *handler_data;
for (l = signal_data->handlers; l != NULL; l = l_next)
{
handler_data = (PurpleSignalHandlerData *)l->data;
l_next = l->next;
if (handler_data->handle == handle)
{
g_free(handler_data);
signal_data->handler_count--;
signal_data->handlers = g_list_delete_link(signal_data->handlers,
l);
}
}
}
static void
disconnect_handle_from_instance(G_GNUC_UNUSED gpointer instance,
PurpleInstanceData *instance_data,
void *handle)
{
g_hash_table_foreach(instance_data->signals,
(GHFunc)disconnect_handle_from_signals, handle);
}
void
purple_signals_disconnect_by_handle(void *handle)
{
g_return_if_fail(handle != NULL);
g_hash_table_foreach(instance_table,
(GHFunc)disconnect_handle_from_instance, handle);
}
void
purple_signal_emit(void *instance, const char *signal, ...)
{
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
GList *l, *l_next;
va_list args;
va_list tmp;
g_return_if_fail(instance != NULL);
g_return_if_fail(signal != NULL);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
g_return_if_fail(instance_data != NULL);
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL) {
purple_debug_error("signals", "Signal data for %s not found!", signal);
return;
}
va_start(args, signal);
for (l = signal_data->handlers; l != NULL; l = l_next)
{
l_next = l->next;
handler_data = (PurpleSignalHandlerData *)l->data;
/* This is necessary because a va_list may only be
* evaluated once */
G_VA_COPY(tmp, args);
if (handler_data->use_vargs)
{
((void (*)(va_list, void *))handler_data->cb)(tmp,
handler_data->data);
}
else
{
signal_data->marshal(handler_data->cb, tmp,
handler_data->data, NULL);
}
va_end(tmp);
}
va_end(args);
}
void *
purple_signal_emit_return_1(void *instance, const char *signal, ...) {
PurpleInstanceData *instance_data;
PurpleSignalData *signal_data;
PurpleSignalHandlerData *handler_data;
GList *l, *l_next;
va_list args;
va_list tmp;
void *ret_val = NULL;
g_return_val_if_fail(instance != NULL, NULL);
g_return_val_if_fail(signal != NULL, NULL);
instance_data =
(PurpleInstanceData *)g_hash_table_lookup(instance_table, instance);
g_return_val_if_fail(instance_data != NULL, NULL);
signal_data =
(PurpleSignalData *)g_hash_table_lookup(instance_data->signals, signal);
if (signal_data == NULL) {
purple_debug_error("signals", "Signal data for %s not found!", signal);
return 0;
}
va_start(args, signal);
for (l = signal_data->handlers; l != NULL; l = l_next)
{
l_next = l->next;
handler_data = (PurpleSignalHandlerData *)l->data;
G_VA_COPY(tmp, args);
if (handler_data->use_vargs)
{
ret_val = ((void *(*)(va_list, void *))handler_data->cb)(
tmp, handler_data->data);
}
else
{
signal_data->marshal(handler_data->cb, tmp,
handler_data->data, &ret_val);
}
va_end(tmp);
if(ret_val != NULL) {
break;
}
}
va_end(args);
return ret_val;
}
void
purple_signals_init(void)
{
g_return_if_fail(instance_table == NULL);
instance_table =
g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)destroy_instance_data);
}
void
purple_signals_uninit(void)
{
g_return_if_fail(instance_table != NULL);
g_clear_pointer(&instance_table, g_hash_table_destroy);
}
/**************************************************************************
* Marshallers
**************************************************************************/
void
purple_marshal_VOID(GCallback cb, G_GNUC_UNUSED va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
((void (*)(void *))cb)(data);
}
void
purple_marshal_VOID__POINTER(GCallback cb, va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
void *arg1 = va_arg(args, void *);
((void (*)(void *, void *))cb)(arg1, data);
}
void
purple_marshal_VOID__POINTER_UINT(GCallback cb, va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
void *arg1 = va_arg(args, void *);
guint arg2 = va_arg(args, guint);
((void (*)(void *, guint, void *))cb)(arg1, arg2, data);
}
void
purple_marshal_VOID__POINTER_INT_INT(GCallback cb, va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
void *arg1 = va_arg(args, void *);
gint arg2 = va_arg(args, gint);
gint arg3 = va_arg(args, gint);
((void (*)(void *, gint, gint, void *))cb)(arg1, arg2, arg3, data);
}
void
purple_marshal_VOID__POINTER_INT_POINTER(GCallback cb, va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
void *arg1 = va_arg(args, void *);
gint arg2 = va_arg(args, gint);
void *arg3 = va_arg(args, void *);
((void (*)(void *, gint, void *, void *))cb)(arg1, arg2, arg3, data);
}
void
purple_marshal_VOID__POINTER_POINTER(GCallback cb, va_list args, void *data,
G_GNUC_UNUSED void **return_val)
{
void *arg1 = va_arg(args, void *);
void *arg2 = va_arg(args, void *);
((void (*)(void *, void *, void *))cb)(arg1, arg2, data);
}
void
purple_marshal_BOOLEAN__POINTER(GCallback cb, va_list args, void *data,
void **return_val)
{
gboolean ret_val;
void *arg1 = va_arg(args, void *);
ret_val = ((gboolean (*)(void *, void *))cb)(arg1, data);
if (return_val != NULL)
*return_val = GINT_TO_POINTER(ret_val);
}
void
purple_marshal_BOOLEAN__POINTER_POINTER(GCallback cb, va_list args,
void *data, void **return_val)
{
gboolean ret_val;
void *arg1 = va_arg(args, void *);
void *arg2 = va_arg(args, void *);
ret_val = ((gboolean (*)(void *, void *, void *))cb)(arg1, arg2, data);
if (return_val != NULL)
*return_val = GINT_TO_POINTER(ret_val);
}
void
purple_marshal_BOOLEAN__POINTER_POINTER_POINTER(GCallback cb, va_list args,
void *data, void **return_val)
{
gboolean ret_val;
void *arg1 = va_arg(args, void *);
void *arg2 = va_arg(args, void *);
void *arg3 = va_arg(args, void *);
ret_val = ((gboolean (*)(void *, void *, void *, void *))cb)(arg1, arg2,
arg3, data);
if (return_val != NULL)
*return_val = GINT_TO_POINTER(ret_val);
}