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