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