1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007-2008 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
26 #include <glib/gi18n-lib.h>
27 #include <dbus/dbus-glib.h>
29 #include <telepathy-glib/dbus.h>
30 #include <telepathy-glib/util.h>
31 #include <libmissioncontrol/mc-enum-types.h>
33 #include "empathy-idle.h"
34 #include "empathy-utils.h"
35 #include "empathy-connectivity.h"
37 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
38 #include "empathy-debug.h"
40 /* Number of seconds before entering extended autoaway. */
41 #define EXT_AWAY_TIME (30*60)
43 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIdle)
47 EmpathyConnectivity *connectivity;
48 gulong state_change_signal_id;
50 TpConnectionPresenceType state;
52 TpConnectionPresenceType flash_state;
55 TpConnectionPresenceType away_saved_state;
56 TpConnectionPresenceType saved_state;
60 guint ext_away_timeout;
64 SESSION_STATUS_AVAILABLE,
65 SESSION_STATUS_INVISIBLE,
68 SESSION_STATUS_UNKNOWN
79 G_DEFINE_TYPE (EmpathyIdle, empathy_idle, G_TYPE_OBJECT);
81 static EmpathyIdle * idle_singleton = NULL;
84 idle_presence_changed_cb (MissionControl *mc,
85 TpConnectionPresenceType state,
89 EmpathyIdlePriv *priv;
91 priv = GET_PRIV (idle);
93 if (state == TP_CONNECTION_PRESENCE_TYPE_UNSET)
94 /* Assume our presence is offline if MC reports UNSET */
95 state = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
97 DEBUG ("Presence changed to '%s' (%d)", status, state);
99 g_free (priv->status);
102 if (!EMP_STR_EMPTY (status)) {
103 priv->status = g_strdup (status);
106 g_object_notify (G_OBJECT (idle), "state");
107 g_object_notify (G_OBJECT (idle), "status");
111 idle_ext_away_cb (EmpathyIdle *idle)
113 EmpathyIdlePriv *priv;
115 priv = GET_PRIV (idle);
117 DEBUG ("Going to extended autoaway");
118 empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY);
119 priv->ext_away_timeout = 0;
125 idle_ext_away_stop (EmpathyIdle *idle)
127 EmpathyIdlePriv *priv;
129 priv = GET_PRIV (idle);
131 if (priv->ext_away_timeout) {
132 g_source_remove (priv->ext_away_timeout);
133 priv->ext_away_timeout = 0;
138 idle_ext_away_start (EmpathyIdle *idle)
140 EmpathyIdlePriv *priv;
142 priv = GET_PRIV (idle);
144 if (priv->ext_away_timeout != 0) {
147 priv->ext_away_timeout = g_timeout_add_seconds (EXT_AWAY_TIME,
148 (GSourceFunc) idle_ext_away_cb,
153 idle_session_status_changed_cb (DBusGProxy *gs_proxy,
154 SessionStatus status,
157 EmpathyIdlePriv *priv;
160 priv = GET_PRIV (idle);
162 is_idle = (status == SESSION_STATUS_IDLE);
164 DEBUG ("Session idle state changed, %s -> %s",
165 priv->is_idle ? "yes" : "no",
166 is_idle ? "yes" : "no");
168 if (!priv->auto_away ||
169 (priv->saved_state == TP_CONNECTION_PRESENCE_TYPE_UNSET &&
170 (priv->state <= TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
171 priv->state == TP_CONNECTION_PRESENCE_TYPE_HIDDEN))) {
172 /* We don't want to go auto away OR we explicitely asked to be
173 * offline, nothing to do here */
174 priv->is_idle = is_idle;
178 if (is_idle && !priv->is_idle) {
179 TpConnectionPresenceType new_state;
180 /* We are now idle */
182 idle_ext_away_start (idle);
184 if (priv->saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
185 /* We are disconnected, when coming back from away
186 * we want to restore the presence before the
188 priv->away_saved_state = priv->saved_state;
190 priv->away_saved_state = priv->state;
193 new_state = TP_CONNECTION_PRESENCE_TYPE_AWAY;
194 if (priv->state == TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY) {
195 new_state = TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY;
198 DEBUG ("Going to autoaway. Saved state=%d, new state=%d",
199 priv->away_saved_state, new_state);
200 empathy_idle_set_state (idle, new_state);
201 } else if (!is_idle && priv->is_idle) {
202 const gchar *new_status;
203 /* We are no more idle, restore state */
205 idle_ext_away_stop (idle);
207 if (priv->away_saved_state == TP_CONNECTION_PRESENCE_TYPE_AWAY ||
208 priv->away_saved_state == TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY) {
209 priv->away_saved_state = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
212 new_status = priv->status;
215 DEBUG ("Restoring state to %d, reset status to %s",
216 priv->away_saved_state, new_status);
218 empathy_idle_set_presence (idle,
219 priv->away_saved_state,
222 priv->away_saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
225 priv->is_idle = is_idle;
229 idle_state_change_cb (EmpathyConnectivity *connectivity,
233 EmpathyIdlePriv *priv;
235 priv = GET_PRIV (idle);
238 /* We are no longer connected */
239 DEBUG ("Disconnected: Save state %d (%s)",
240 priv->state, priv->status);
241 priv->saved_state = priv->state;
242 g_free (priv->saved_status);
243 priv->saved_status = g_strdup (priv->status);
244 empathy_idle_set_state (idle, TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
247 && priv->saved_state != TP_CONNECTION_PRESENCE_TYPE_UNSET) {
248 /* We are now connected */
249 DEBUG ("Reconnected: Restore state %d (%s)",
250 priv->saved_state, priv->saved_status);
251 empathy_idle_set_presence (idle,
254 priv->saved_state = TP_CONNECTION_PRESENCE_TYPE_UNSET;
255 g_free (priv->saved_status);
256 priv->saved_status = NULL;
261 idle_finalize (GObject *object)
263 EmpathyIdlePriv *priv;
265 priv = GET_PRIV (object);
267 g_free (priv->status);
268 g_object_unref (priv->mc);
270 if (priv->gs_proxy) {
271 g_object_unref (priv->gs_proxy);
274 g_signal_handler_disconnect (priv->connectivity,
275 priv->state_change_signal_id);
276 priv->state_change_signal_id = 0;
278 g_object_unref (priv->connectivity);
280 idle_ext_away_stop (EMPATHY_IDLE (object));
284 idle_constructor (GType type,
286 GObjectConstructParam *props)
290 if (idle_singleton) {
291 retval = g_object_ref (idle_singleton);
293 retval = G_OBJECT_CLASS (empathy_idle_parent_class)->constructor
294 (type, n_props, props);
296 idle_singleton = EMPATHY_IDLE (retval);
297 g_object_add_weak_pointer (retval, (gpointer) &idle_singleton);
304 idle_get_property (GObject *object,
309 EmpathyIdlePriv *priv;
312 priv = GET_PRIV (object);
313 idle = EMPATHY_IDLE (object);
317 g_value_set_enum (value, empathy_idle_get_state (idle));
320 g_value_set_string (value, empathy_idle_get_status (idle));
322 case PROP_FLASH_STATE:
323 g_value_set_enum (value, empathy_idle_get_flash_state (idle));
326 g_value_set_boolean (value, empathy_idle_get_auto_away (idle));
329 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
335 idle_set_property (GObject *object,
340 EmpathyIdlePriv *priv;
343 priv = GET_PRIV (object);
344 idle = EMPATHY_IDLE (object);
348 empathy_idle_set_state (idle, g_value_get_enum (value));
351 empathy_idle_set_status (idle, g_value_get_string (value));
353 case PROP_FLASH_STATE:
354 empathy_idle_set_flash_state (idle, g_value_get_enum (value));
357 empathy_idle_set_auto_away (idle, g_value_get_boolean (value));
360 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
366 empathy_idle_class_init (EmpathyIdleClass *klass)
368 GObjectClass *object_class = G_OBJECT_CLASS (klass);
370 object_class->finalize = idle_finalize;
371 object_class->constructor = idle_constructor;
372 object_class->get_property = idle_get_property;
373 object_class->set_property = idle_set_property;
375 g_object_class_install_property (object_class,
377 g_param_spec_uint ("state",
380 0, NUM_TP_CONNECTION_PRESENCE_TYPES,
381 TP_CONNECTION_PRESENCE_TYPE_UNSET,
383 g_object_class_install_property (object_class,
385 g_param_spec_string ("status",
390 g_object_class_install_property (object_class,
392 g_param_spec_uint ("flash-state",
395 0, NUM_TP_CONNECTION_PRESENCE_TYPES,
396 TP_CONNECTION_PRESENCE_TYPE_UNSET,
399 g_object_class_install_property (object_class,
401 g_param_spec_boolean ("auto-away",
402 "Automatic set presence to away",
403 "Should it set presence to away if inactive",
407 g_type_class_add_private (object_class, sizeof (EmpathyIdlePriv));
410 static TpConnectionPresenceType
411 empathy_idle_get_actual_presence (EmpathyIdle *idle, GError **error)
414 EmpathyIdlePriv *priv = GET_PRIV (idle);
416 presence = mission_control_get_presence_actual (priv->mc, error);
419 case MC_PRESENCE_OFFLINE:
420 return TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
421 case MC_PRESENCE_AVAILABLE:
422 return TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
423 case MC_PRESENCE_AWAY:
424 return TP_CONNECTION_PRESENCE_TYPE_AWAY;
425 case MC_PRESENCE_EXTENDED_AWAY:
426 return TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY;
427 case MC_PRESENCE_HIDDEN:
428 return TP_CONNECTION_PRESENCE_TYPE_HIDDEN;
429 case MC_PRESENCE_DO_NOT_DISTURB:
430 return TP_CONNECTION_PRESENCE_TYPE_BUSY;
432 return TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
437 empathy_idle_init (EmpathyIdle *idle)
439 GError *error = NULL;
440 EmpathyIdlePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (idle,
441 EMPATHY_TYPE_IDLE, EmpathyIdlePriv);
444 priv->is_idle = FALSE;
445 priv->mc = empathy_mission_control_dup_singleton ();
446 priv->state = empathy_idle_get_actual_presence (idle, &error);
448 DEBUG ("Error getting actual presence: %s", error->message);
450 /* Fallback to OFFLINE as that's what mission_control_get_presence_actual
451 does. This also ensure to always display the status icon (there is no
452 unset presence icon). */
453 priv->state = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
454 g_clear_error (&error);
456 priv->status = mission_control_get_presence_message_actual (priv->mc, &error);
457 if (error || EMP_STR_EMPTY (priv->status)) {
458 g_free (priv->status);
462 DEBUG ("Error getting actual presence message: %s", error->message);
463 g_clear_error (&error);
467 dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
469 G_CALLBACK (idle_presence_changed_cb),
472 priv->gs_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
473 "org.gnome.SessionManager",
474 "/org/gnome/SessionManager/Presence",
475 "org.gnome.SessionManager.Presence");
476 if (priv->gs_proxy) {
477 dbus_g_proxy_add_signal (priv->gs_proxy, "StatusChanged",
478 G_TYPE_UINT, G_TYPE_INVALID);
479 dbus_g_proxy_connect_signal (priv->gs_proxy, "StatusChanged",
480 G_CALLBACK (idle_session_status_changed_cb),
483 DEBUG ("Failed to get gs proxy");
486 priv->connectivity = empathy_connectivity_dup_singleton ();
487 priv->state_change_signal_id = g_signal_connect (priv->connectivity,
488 "state-change", G_CALLBACK (idle_state_change_cb), idle);
492 empathy_idle_dup_singleton (void)
494 return g_object_new (EMPATHY_TYPE_IDLE, NULL);
497 TpConnectionPresenceType
498 empathy_idle_get_state (EmpathyIdle *idle)
500 EmpathyIdlePriv *priv;
502 priv = GET_PRIV (idle);
508 empathy_idle_set_state (EmpathyIdle *idle,
509 TpConnectionPresenceType state)
511 EmpathyIdlePriv *priv;
513 priv = GET_PRIV (idle);
515 empathy_idle_set_presence (idle, state, priv->status);
519 empathy_idle_get_status (EmpathyIdle *idle)
521 EmpathyIdlePriv *priv;
523 priv = GET_PRIV (idle);
526 return empathy_presence_get_default_message (priv->state);
533 empathy_idle_set_status (EmpathyIdle *idle,
536 EmpathyIdlePriv *priv;
538 priv = GET_PRIV (idle);
540 empathy_idle_set_presence (idle, priv->state, status);
543 TpConnectionPresenceType
544 empathy_idle_get_flash_state (EmpathyIdle *idle)
546 EmpathyIdlePriv *priv;
548 priv = GET_PRIV (idle);
550 return priv->flash_state;
554 empathy_idle_set_flash_state (EmpathyIdle *idle,
555 TpConnectionPresenceType state)
557 EmpathyIdlePriv *priv;
559 priv = GET_PRIV (idle);
561 priv->flash_state = state;
563 if (state == TP_CONNECTION_PRESENCE_TYPE_UNSET) {
566 g_object_notify (G_OBJECT (idle), "flash-state");
570 empathy_idle_do_set_presence (EmpathyIdle *idle,
571 TpConnectionPresenceType state,
574 McPresence mc_state = MC_PRESENCE_UNSET;
575 EmpathyIdlePriv *priv = GET_PRIV (idle);
578 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
579 mc_state = MC_PRESENCE_OFFLINE;
581 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
582 mc_state = MC_PRESENCE_AVAILABLE;
584 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
585 mc_state = MC_PRESENCE_AWAY;
587 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
588 mc_state = MC_PRESENCE_EXTENDED_AWAY;
590 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
591 mc_state = MC_PRESENCE_HIDDEN;
593 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
594 mc_state = MC_PRESENCE_DO_NOT_DISTURB;
597 g_assert_not_reached ();
600 mission_control_set_presence (priv->mc, mc_state, status, NULL, NULL);
604 empathy_idle_set_presence (EmpathyIdle *idle,
605 TpConnectionPresenceType state,
608 EmpathyIdlePriv *priv;
609 const gchar *default_status;
611 priv = GET_PRIV (idle);
613 DEBUG ("Changing presence to %s (%d)", status, state);
615 /* Do not set translated default messages */
616 default_status = empathy_presence_get_default_message (state);
617 if (!tp_strdiff (status, default_status)) {
621 if (!empathy_connectivity_is_online (priv->connectivity)) {
622 DEBUG ("Empathy is not online");
624 if (tp_strdiff (priv->status, status)) {
625 g_free (priv->status);
627 if (!EMP_STR_EMPTY (status)) {
628 priv->status = g_strdup (status);
630 g_object_notify (G_OBJECT (idle), "status");
634 empathy_idle_do_set_presence (idle, state, status);
638 empathy_idle_get_auto_away (EmpathyIdle *idle)
640 EmpathyIdlePriv *priv = GET_PRIV (idle);
642 return priv->auto_away;
646 empathy_idle_set_auto_away (EmpathyIdle *idle,
649 EmpathyIdlePriv *priv = GET_PRIV (idle);
651 priv->auto_away = auto_away;
653 g_object_notify (G_OBJECT (idle), "auto-away");