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