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