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