From 61423aa5c4d54d8cd612d2007c3ab90935bbb227 Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Sun, 8 Mar 2015 16:08:02 +0200 Subject: [PATCH] purple: work around broken idle indication Move libpurple-ism from core to libpurple backend where it really belongs. This obsoletes sipe_core_status_idle(). --- src/purple/purple-plugin.c | 5 ++ src/purple/purple-private.h | 8 +++ src/purple/purple-status.c | 116 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 109 insertions(+), 20 deletions(-) diff --git a/src/purple/purple-plugin.c b/src/purple/purple-plugin.c index 41bab905..68c91602 100644 --- a/src/purple/purple-plugin.c +++ b/src/purple/purple-plugin.c @@ -539,6 +539,11 @@ static void sipe_purple_close(PurpleConnection *gc) if (purple_private->roomlist_map) g_hash_table_destroy(purple_private->roomlist_map); sipe_purple_chat_destroy_rejoin(purple_private); + + if (purple_private->deferred_status_timeout) + purple_timeout_remove(purple_private->deferred_status_timeout); + g_free(purple_private->deferred_status_note); + g_free(purple_private); purple_connection_set_protocol_data(gc, NULL); } diff --git a/src/purple/purple-private.h b/src/purple/purple-private.h index 679f6976..268595b0 100644 --- a/src/purple/purple-private.h +++ b/src/purple/purple-private.h @@ -52,7 +52,15 @@ struct sipe_backend_private { GList *rejoin_chats; GSList *transports; GSList *dns_queries; + + /* work around broken libpurple idle notification */ + gchar *deferred_status_note; + guint deferred_status_activity; + guint deferred_status_timeout; + + /* flags */ gboolean status_changed_by_core; /* status changed by core */ + gboolean user_is_not_idle; /* user came back online */ }; struct sipe_backend_fd { diff --git a/src/purple/purple-status.c b/src/purple/purple-status.c index 7ca69597..4eabe9ba 100644 --- a/src/purple/purple-status.c +++ b/src/purple/purple-status.c @@ -109,39 +109,115 @@ void sipe_backend_status_and_note(struct sipe_core_public *sipe_public, purple_savedstatus_activate(saved_status); } -void sipe_purple_set_status(PurpleAccount *account, - PurpleStatus *status) +/** + * Work around broken libpurple idle notification + * + * (1) user changes the status + * sipe_purple_set_status() + * -> user changed state + * + * (2) client detects that user is idle + * sipe_purple_set_status() + * sipe_purple_set_idle( != 0 ) + * -> machine changed state + * + * (3) client detects that user is no longer idle + * sipe_purple_set_idle(0) + * sipe_purple_set_status() + * -> user changed state + * + * (4) core sends a status change + * sipe_backend_status_and_note() + * purple_savedstatus_activate() + * sipe_purple_set_status() + * -> status change must be ignored + * + * Cases (1) and (2) can only be differentiated by deferring the update. + */ +static void sipe_purple_status_deferred_update(struct sipe_backend_private *purple_private, + gboolean changed_by_user) { - struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; - struct sipe_backend_private *purple_private = sipe_public->backend_private; - const gchar *status_id = purple_status_get_id(status); + gchar *note = purple_private->deferred_status_note; - if (purple_private->status_changed_by_core) { - SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: '%s' triggered by core - ignoring", status_id); - purple_private->status_changed_by_core = FALSE; - return; - } + purple_private->deferred_status_note = NULL; + purple_private->deferred_status_timeout = 0; - SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: '%s'", status_id); + sipe_core_status_set(purple_private->public, + changed_by_user, + purple_private->deferred_status_activity, + note); + g_free(note); +} - if (!purple_status_is_active(status)) - return; +static gboolean sipe_purple_status_timeout(gpointer data) +{ + /* timeout expired -> no idle indication -> state changed by user */ + sipe_purple_status_deferred_update(data, TRUE); + return(FALSE); +} - if (purple_account_get_connection(account)) { +void sipe_purple_set_status(PurpleAccount *account, + PurpleStatus *status) +{ + if (purple_account_get_connection(account) && + purple_status_is_active(status)) { + struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC; + struct sipe_backend_private *purple_private = sipe_public->backend_private; + const gchar *status_id = purple_status_get_id(status); + guint activity = sipe_purple_token_to_activity(status_id); const gchar *note = purple_status_get_attr_string(status, SIPE_PURPLE_STATUS_ATTR_ID_MESSAGE); - sipe_core_status_set(sipe_public, - TRUE, - sipe_purple_token_to_activity(status_id), - note); + + SIPE_DEBUG_INFO("sipe_purple_set_status[CB]: '%s'", + status_id); + + if (purple_private->status_changed_by_core) { + SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: triggered by core - ignoring"); + + } else if (purple_private->user_is_not_idle) { + sipe_core_status_set(sipe_public, + TRUE, + activity, + note); + + } else { + if (purple_private->deferred_status_timeout) + purple_timeout_remove(purple_private->deferred_status_timeout); + g_free(purple_private->deferred_status_note); + + SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_set_status[CB]: defer status update"); + + purple_private->deferred_status_note = g_strdup(note); + purple_private->deferred_status_activity = activity; + purple_private->deferred_status_timeout = purple_timeout_add(100, /* milliseconds */ + sipe_purple_status_timeout, + purple_private); + } + + /* reset flags */ + purple_private->status_changed_by_core = FALSE; + purple_private->user_is_not_idle = FALSE; } } void sipe_purple_set_idle(PurpleConnection *gc, int interval) { - SIPE_DEBUG_INFO("sipe_purple_set_idle[CB]: interval=%d", interval); - if (gc) sipe_core_status_idle(PURPLE_GC_TO_SIPE_CORE_PUBLIC); + if (gc) { + struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC; + struct sipe_backend_private *purple_private = sipe_public->backend_private; + + purple_private->user_is_not_idle = interval == 0; + + SIPE_DEBUG_INFO("sipe_purple_set_idle[CB]: user is %sidle", + purple_private->user_is_not_idle ? "not " : ""); + + if (!purple_private->user_is_not_idle) { + /* timeout not expired -> state changed by machine */ + purple_timeout_remove(purple_private->deferred_status_timeout); + sipe_purple_status_deferred_update(purple_private, FALSE); + } + } } /* -- 2.11.4.GIT