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