]> git.0d.be Git - empathy.git/blob - libempathy/empathy-idle.c
Merge branch 'master' into tp-tube
[empathy.git] / libempathy / empathy-idle.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
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.
9  *
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.
14  *
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
18  * 
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <glib/gi18n-lib.h>
27 #include <dbus/dbus-glib.h>
28
29 #include <telepathy-glib/dbus.h>
30 #include <telepathy-glib/util.h>
31 #include <libmissioncontrol/mc-enum-types.h>
32
33 #include "empathy-idle.h"
34 #include "empathy-utils.h" 
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
37 #include "empathy-debug.h"
38
39 /* Number of seconds before entering extended autoaway. */
40 #define EXT_AWAY_TIME (30*60)
41
42 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIdle)
43 typedef struct {
44         MissionControl *mc;
45         DBusGProxy     *gs_proxy;
46         DBusGProxy     *nm_proxy;
47
48         McPresence      state;
49         gchar          *status;
50         McPresence      flash_state;
51         gboolean        auto_away;
52         gboolean        use_nm;
53
54         McPresence      away_saved_state;
55         McPresence      nm_saved_state;
56         gchar          *nm_saved_status;
57
58         gboolean        is_idle;
59         gboolean        nm_connected;
60         guint           ext_away_timeout;
61 } EmpathyIdlePriv;
62
63 typedef enum {
64         NM_STATE_UNKNOWN,
65         NM_STATE_ASLEEP,
66         NM_STATE_CONNECTING,
67         NM_STATE_CONNECTED,
68         NM_STATE_DISCONNECTED
69 } NMState;
70
71 enum {
72         PROP_0,
73         PROP_STATE,
74         PROP_STATUS,
75         PROP_FLASH_STATE,
76         PROP_AUTO_AWAY,
77         PROP_USE_NM
78 };
79
80 G_DEFINE_TYPE (EmpathyIdle, empathy_idle, G_TYPE_OBJECT);
81
82 static EmpathyIdle * idle_singleton = NULL;
83
84 static void
85 idle_presence_changed_cb (MissionControl *mc,
86                           McPresence      state,
87                           gchar          *status,
88                           EmpathyIdle    *idle)
89 {
90         EmpathyIdlePriv *priv;
91
92         priv = GET_PRIV (idle);
93
94         DEBUG ("Presence changed to '%s' (%d)", status, state);
95
96         g_free (priv->status);
97         priv->state = state;
98         priv->status = NULL;
99         if (!EMP_STR_EMPTY (status)) {
100                 priv->status = g_strdup (status);
101         }
102
103         g_object_notify (G_OBJECT (idle), "state");
104         g_object_notify (G_OBJECT (idle), "status");
105 }
106
107 static gboolean
108 idle_ext_away_cb (EmpathyIdle *idle)
109 {
110         EmpathyIdlePriv *priv;
111
112         priv = GET_PRIV (idle);
113
114         DEBUG ("Going to extended autoaway");
115         empathy_idle_set_state (idle, MC_PRESENCE_EXTENDED_AWAY);
116         priv->ext_away_timeout = 0;
117
118         return FALSE;
119 }
120
121 static void
122 idle_ext_away_stop (EmpathyIdle *idle)
123 {
124         EmpathyIdlePriv *priv;
125
126         priv = GET_PRIV (idle);
127
128         if (priv->ext_away_timeout) {
129                 g_source_remove (priv->ext_away_timeout);
130                 priv->ext_away_timeout = 0;
131         }
132 }
133
134 static void
135 idle_ext_away_start (EmpathyIdle *idle)
136 {
137         EmpathyIdlePriv *priv;
138
139         priv = GET_PRIV (idle);
140
141         if (priv->ext_away_timeout != 0) {
142                 return;
143         }
144         priv->ext_away_timeout = g_timeout_add_seconds (EXT_AWAY_TIME,
145                                                         (GSourceFunc) idle_ext_away_cb,
146                                                         idle);
147 }
148
149 static void
150 idle_session_idle_changed_cb (DBusGProxy  *gs_proxy,
151                               gboolean     is_idle,
152                               EmpathyIdle *idle)
153 {
154         EmpathyIdlePriv *priv;
155
156         priv = GET_PRIV (idle);
157
158         DEBUG ("Session idle state changed, %s -> %s",
159                 priv->is_idle ? "yes" : "no",
160                 is_idle ? "yes" : "no");
161
162         if (!priv->auto_away ||
163             (priv->nm_saved_state == MC_PRESENCE_UNSET &&
164              (priv->state <= MC_PRESENCE_OFFLINE ||
165               priv->state == MC_PRESENCE_HIDDEN))) {
166                 /* We don't want to go auto away OR we explicitely asked to be
167                  * offline, nothing to do here */
168                 priv->is_idle = is_idle;
169                 return;
170         }
171
172         if (is_idle && !priv->is_idle) {
173                 McPresence new_state;
174                 /* We are now idle */
175
176                 idle_ext_away_start (idle);
177
178                 if (priv->nm_saved_state != MC_PRESENCE_UNSET) {
179                         /* We are disconnected, when coming back from away
180                          * we want to restore the presence before the
181                          * disconnection. */
182                         priv->away_saved_state = priv->nm_saved_state;
183                 } else {
184                         priv->away_saved_state = priv->state;
185                 }
186
187                 new_state = MC_PRESENCE_AWAY;
188                 if (priv->state == MC_PRESENCE_EXTENDED_AWAY) {
189                         new_state = MC_PRESENCE_EXTENDED_AWAY;
190                 }
191
192                 DEBUG ("Going to autoaway. Saved state=%d, new state=%d",
193                         priv->away_saved_state, new_state);
194                 empathy_idle_set_state (idle, new_state);
195         } else if (!is_idle && priv->is_idle) {
196                 const gchar *new_status;
197                 /* We are no more idle, restore state */
198
199                 idle_ext_away_stop (idle);
200
201                 if (priv->away_saved_state == MC_PRESENCE_AWAY ||
202                     priv->away_saved_state == MC_PRESENCE_EXTENDED_AWAY) {
203                         priv->away_saved_state = MC_PRESENCE_AVAILABLE;
204                         new_status = NULL;
205                 } else {
206                         new_status = priv->status;
207                 }
208
209                 DEBUG ("Restoring state to %d, reset status to %s",
210                         priv->away_saved_state, new_status);
211
212                 empathy_idle_set_presence (idle,
213                                            priv->away_saved_state,
214                                            new_status);
215
216                 priv->away_saved_state = MC_PRESENCE_UNSET;
217         }
218
219         priv->is_idle = is_idle;
220 }
221
222 static void
223 idle_nm_state_change_cb (DBusGProxy  *proxy,
224                          guint        state,
225                          EmpathyIdle *idle)
226 {
227         EmpathyIdlePriv *priv;
228         gboolean         old_nm_connected;
229         gboolean         new_nm_connected;
230
231         priv = GET_PRIV (idle);
232
233         if (!priv->use_nm) {
234                 return;
235         }
236
237         old_nm_connected = priv->nm_connected;
238         new_nm_connected = !(state == NM_STATE_CONNECTING ||
239                              state == NM_STATE_DISCONNECTED);
240         priv->nm_connected = TRUE; /* To be sure _set_state will work */
241
242         DEBUG ("New network state %d", state);
243
244         if (old_nm_connected && !new_nm_connected) {
245                 /* We are no more connected */
246                 DEBUG ("Disconnected: Save state %d (%s)",
247                                 priv->state, priv->status);
248                 priv->nm_saved_state = priv->state;
249                 g_free (priv->nm_saved_status);
250                 priv->nm_saved_status = g_strdup (priv->status);
251                 empathy_idle_set_state (idle, MC_PRESENCE_OFFLINE);
252         }
253         else if (!old_nm_connected && new_nm_connected) {
254                 /* We are now connected */
255                 DEBUG ("Reconnected: Restore state %d (%s)",
256                                 priv->nm_saved_state, priv->nm_saved_status);
257                 empathy_idle_set_presence (idle,
258                                 priv->nm_saved_state,
259                                 priv->nm_saved_status);
260                 priv->nm_saved_state = MC_PRESENCE_UNSET;
261                 g_free (priv->nm_saved_status);
262                 priv->nm_saved_status = NULL;
263         }
264
265         priv->nm_connected = new_nm_connected;
266 }
267
268 static void
269 idle_finalize (GObject *object)
270 {
271         EmpathyIdlePriv *priv;
272
273         priv = GET_PRIV (object);
274
275         g_free (priv->status);
276         g_object_unref (priv->mc);
277
278         if (priv->gs_proxy) {
279                 g_object_unref (priv->gs_proxy);
280         }
281
282         idle_ext_away_stop (EMPATHY_IDLE (object));
283 }
284
285 static GObject *
286 idle_constructor (GType type,
287                   guint n_props,
288                   GObjectConstructParam *props)
289 {
290         GObject *retval;
291
292         if (idle_singleton) {
293                 retval = g_object_ref (idle_singleton);
294         } else {
295                 retval = G_OBJECT_CLASS (empathy_idle_parent_class)->constructor
296                         (type, n_props, props);
297
298                 idle_singleton = EMPATHY_IDLE (retval);
299                 g_object_add_weak_pointer (retval, (gpointer) &idle_singleton);
300         }
301
302         return retval;
303 }
304
305 static void
306 idle_get_property (GObject    *object,
307                    guint       param_id,
308                    GValue     *value,
309                    GParamSpec *pspec)
310 {
311         EmpathyIdlePriv *priv;
312         EmpathyIdle     *idle;
313
314         priv = GET_PRIV (object);
315         idle = EMPATHY_IDLE (object);
316
317         switch (param_id) {
318         case PROP_STATE:
319                 g_value_set_enum (value, empathy_idle_get_state (idle));
320                 break;
321         case PROP_STATUS:
322                 g_value_set_string (value, empathy_idle_get_status (idle));
323                 break;
324         case PROP_FLASH_STATE:
325                 g_value_set_enum (value, empathy_idle_get_flash_state (idle));
326                 break;
327         case PROP_AUTO_AWAY:
328                 g_value_set_boolean (value, empathy_idle_get_auto_away (idle));
329                 break;
330         case PROP_USE_NM:
331                 g_value_set_boolean (value, empathy_idle_get_use_nm (idle));
332                 break;
333         default:
334                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
335                 break;
336         };
337 }
338
339 static void
340 idle_set_property (GObject      *object,
341                    guint         param_id,
342                    const GValue *value,
343                    GParamSpec   *pspec)
344 {
345         EmpathyIdlePriv *priv;
346         EmpathyIdle     *idle;
347
348         priv = GET_PRIV (object);
349         idle = EMPATHY_IDLE (object);
350
351         switch (param_id) {
352         case PROP_STATE:
353                 empathy_idle_set_state (idle, g_value_get_enum (value));
354                 break;
355         case PROP_STATUS:
356                 empathy_idle_set_status (idle, g_value_get_string (value));
357                 break;
358         case PROP_FLASH_STATE:
359                 empathy_idle_set_flash_state (idle, g_value_get_enum (value));
360                 break;
361         case PROP_AUTO_AWAY:
362                 empathy_idle_set_auto_away (idle, g_value_get_boolean (value));
363                 break;
364         case PROP_USE_NM:
365                 empathy_idle_set_use_nm (idle, g_value_get_boolean (value));
366                 break;
367         default:
368                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
369                 break;
370         };
371 }
372
373 static void
374 empathy_idle_class_init (EmpathyIdleClass *klass)
375 {
376         GObjectClass *object_class = G_OBJECT_CLASS (klass);
377
378         object_class->finalize = idle_finalize;
379         object_class->constructor = idle_constructor;
380         object_class->get_property = idle_get_property;
381         object_class->set_property = idle_set_property;
382
383         g_object_class_install_property (object_class,
384                                          PROP_STATE,
385                                          g_param_spec_enum ("state",
386                                                             "state",
387                                                             "state",
388                                                             MC_TYPE_PRESENCE,
389                                                             MC_PRESENCE_AVAILABLE,
390                                                             G_PARAM_READWRITE));
391         g_object_class_install_property (object_class,
392                                          PROP_STATUS,
393                                          g_param_spec_string ("status",
394                                                               "status",
395                                                               "status",
396                                                               NULL,
397                                                               G_PARAM_READWRITE));
398         g_object_class_install_property (object_class,
399                                          PROP_FLASH_STATE,
400                                          g_param_spec_enum ("flash-state",
401                                                             "flash-state",
402                                                             "flash-state",
403                                                             MC_TYPE_PRESENCE,
404                                                             MC_PRESENCE_UNSET,
405                                                             G_PARAM_READWRITE));
406
407          g_object_class_install_property (object_class,
408                                           PROP_AUTO_AWAY,
409                                           g_param_spec_boolean ("auto-away",
410                                                                 "Automatic set presence to away",
411                                                                 "Should it set presence to away if inactive",
412                                                                 FALSE,
413                                                                 G_PARAM_READWRITE));
414
415          g_object_class_install_property (object_class,
416                                           PROP_USE_NM,
417                                           g_param_spec_boolean ("use-nm",
418                                                                 "Use Network Manager",
419                                                                 "Set presence according to Network Manager",
420                                                                 FALSE,
421                                                                 G_PARAM_READWRITE));
422
423         g_type_class_add_private (object_class, sizeof (EmpathyIdlePriv));
424 }
425
426 static void
427 empathy_idle_init (EmpathyIdle *idle)
428 {
429         DBusGConnection *system_bus;
430         GError          *error = NULL;
431         EmpathyIdlePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (idle,
432                 EMPATHY_TYPE_IDLE, EmpathyIdlePriv);
433
434         idle->priv = priv;
435         priv->is_idle = FALSE;
436         priv->mc = empathy_mission_control_dup_singleton ();
437         priv->state = mission_control_get_presence_actual (priv->mc, &error);
438         if (error) {
439                 DEBUG ("Error getting actual presence: %s", error->message);
440
441                 priv->state = MC_PRESENCE_UNSET;
442                 g_clear_error (&error);
443         }
444         priv->status = mission_control_get_presence_message_actual (priv->mc, &error);
445         if (error || EMP_STR_EMPTY (priv->status)) {
446                 g_free (priv->status);
447                 priv->status = NULL;
448
449                 if (error) {
450                         DEBUG ("Error getting actual presence message: %s", error->message);
451                         g_clear_error (&error);
452                 }
453         }
454
455         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
456                                      "PresenceChanged",
457                                      G_CALLBACK (idle_presence_changed_cb),
458                                      idle, NULL);
459
460         priv->gs_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
461                                                     "org.gnome.ScreenSaver",
462                                                     "/org/gnome/ScreenSaver",
463                                                     "org.gnome.ScreenSaver");
464         if (priv->gs_proxy) {
465                 dbus_g_proxy_add_signal (priv->gs_proxy, "SessionIdleChanged",
466                                          G_TYPE_BOOLEAN,
467                                          G_TYPE_INVALID);
468                 dbus_g_proxy_connect_signal (priv->gs_proxy, "SessionIdleChanged",
469                                              G_CALLBACK (idle_session_idle_changed_cb),
470                                              idle, NULL);
471         } else {
472                 DEBUG ("Failed to get gs proxy");
473         }
474
475
476         system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
477         if (!system_bus) {
478                 DEBUG ("Failed to get system bus: %s",
479                         error ? error->message : "No error given");
480         } else {
481                 priv->nm_proxy = dbus_g_proxy_new_for_name (system_bus,
482                                                             "org.freedesktop.NetworkManager",
483                                                             "/org/freedesktop/NetworkManager",
484                                                             "org.freedesktop.NetworkManager");
485         }
486         if (priv->nm_proxy) {
487                 dbus_g_proxy_add_signal (priv->nm_proxy, "StateChange",
488                                          G_TYPE_UINT, G_TYPE_INVALID);
489                 dbus_g_proxy_connect_signal (priv->nm_proxy, "StateChange",
490                                              G_CALLBACK (idle_nm_state_change_cb),
491                                              idle, NULL);
492         } else {
493                 DEBUG ("Failed to get nm proxy");
494         }
495
496         priv->nm_connected = TRUE;
497 }
498
499 EmpathyIdle *
500 empathy_idle_dup_singleton (void)
501 {
502         return g_object_new (EMPATHY_TYPE_IDLE, NULL);
503 }
504
505 McPresence
506 empathy_idle_get_state (EmpathyIdle *idle)
507 {
508         EmpathyIdlePriv *priv;
509
510         priv = GET_PRIV (idle);
511
512         return priv->state;
513 }
514
515 void
516 empathy_idle_set_state (EmpathyIdle *idle,
517                         McPresence   state)
518 {
519         EmpathyIdlePriv *priv;
520
521         priv = GET_PRIV (idle);
522
523         empathy_idle_set_presence (idle, state, priv->status);
524 }
525
526 const gchar *
527 empathy_idle_get_status (EmpathyIdle *idle)
528 {
529         EmpathyIdlePriv *priv;
530
531         priv = GET_PRIV (idle);
532
533         if (!priv->status) {
534                 return empathy_presence_get_default_message (priv->state);
535         }
536
537         return priv->status;
538 }
539
540 void
541 empathy_idle_set_status (EmpathyIdle *idle,
542                          const gchar *status)
543 {
544         EmpathyIdlePriv *priv;
545
546         priv = GET_PRIV (idle);
547
548         empathy_idle_set_presence (idle, priv->state, status);
549 }
550
551 McPresence
552 empathy_idle_get_flash_state (EmpathyIdle *idle)
553 {
554         EmpathyIdlePriv *priv;
555
556         priv = GET_PRIV (idle);
557
558         return priv->flash_state;
559 }
560
561 void
562 empathy_idle_set_flash_state (EmpathyIdle *idle,
563                               McPresence   state)
564 {
565         EmpathyIdlePriv *priv;
566
567         priv = GET_PRIV (idle);
568
569         priv->flash_state = state;
570
571         if (state == MC_PRESENCE_UNSET) {
572         }
573
574         g_object_notify (G_OBJECT (idle), "flash-state");
575 }
576
577 void
578 empathy_idle_set_presence (EmpathyIdle *idle,
579                            McPresence   state,
580                            const gchar *status)
581 {
582         EmpathyIdlePriv *priv;
583         const gchar     *default_status;
584
585         priv = GET_PRIV (idle);
586
587         DEBUG ("Changing presence to %s (%d)", status, state);
588
589         /* Do not set translated default messages */
590         default_status = empathy_presence_get_default_message (state);
591         if (!tp_strdiff (status, default_status)) {
592                 status = NULL;
593         }
594
595         if (!priv->nm_connected) {
596                 DEBUG ("NM not connected");
597
598                 priv->nm_saved_state = state;
599                 if (tp_strdiff (priv->status, status)) {
600                         g_free (priv->status);
601                         priv->status = NULL;
602                         if (!EMP_STR_EMPTY (status)) {
603                                 priv->status = g_strdup (status);
604                         }
605                         g_object_notify (G_OBJECT (idle), "status");
606                 }
607
608                 return;
609         }
610
611         mission_control_set_presence (priv->mc, state, status, NULL, NULL);
612 }
613
614 gboolean
615 empathy_idle_get_auto_away (EmpathyIdle *idle)
616 {
617         EmpathyIdlePriv *priv = GET_PRIV (idle);
618
619         return priv->auto_away;
620 }
621
622 void
623 empathy_idle_set_auto_away (EmpathyIdle *idle,
624                             gboolean     auto_away)
625 {
626         EmpathyIdlePriv *priv = GET_PRIV (idle);
627
628         priv->auto_away = auto_away;
629
630         g_object_notify (G_OBJECT (idle), "auto-away");
631 }
632
633 gboolean
634 empathy_idle_get_use_nm (EmpathyIdle *idle)
635 {
636         EmpathyIdlePriv *priv = GET_PRIV (idle);
637
638         return priv->use_nm;
639 }
640
641 void
642 empathy_idle_set_use_nm (EmpathyIdle *idle,
643                          gboolean     use_nm)
644 {
645         EmpathyIdlePriv *priv = GET_PRIV (idle);
646
647         if (!priv->nm_proxy || use_nm == priv->use_nm) {
648                 return;
649         }
650
651         priv->use_nm = use_nm;
652
653         if (use_nm) {
654                 guint   nm_status;
655                 GError *error = NULL;
656
657                 dbus_g_proxy_call (priv->nm_proxy, "state",
658                                    &error,
659                                    G_TYPE_INVALID,
660                                    G_TYPE_UINT, &nm_status,
661                                    G_TYPE_INVALID);
662
663                 if (error) {
664                         DEBUG ("Couldn't get NM state: %s", error->message);
665                         g_clear_error (&error);
666                         nm_status = NM_STATE_ASLEEP;
667                 }
668                 
669                 idle_nm_state_change_cb (priv->nm_proxy, nm_status, idle);
670         } else {
671                 priv->nm_connected = TRUE;
672                 if (priv->nm_saved_state != MC_PRESENCE_UNSET) {
673                         empathy_idle_set_state (idle, priv->nm_saved_state);
674                 }
675                 priv->nm_saved_state = MC_PRESENCE_UNSET;
676         }
677
678         g_object_notify (G_OBJECT (idle), "use-nm");
679 }
680