Add support for blinking when there is an event. Make use of EmpathyIdle
[empathy.git] / libempathy / empathy-idle.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <glib/gi18n.h>
26 #include <dbus/dbus-glib.h>
27
28 #include <libtelepathy/tp-helpers.h>
29
30 #include "empathy-idle.h"
31 #include "gossip-utils.h" 
32 #include "gossip-debug.h"
33
34 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
35                        EMPATHY_TYPE_IDLE, EmpathyIdlePriv))
36
37 #define DEBUG_DOMAIN "Idle"
38
39 /* Number of seconds before entering extended autoaway. */
40 #define EXT_AWAY_TIME (30*60)
41
42 enum {
43         LAST_SIGNAL
44 };
45
46 struct _EmpathyIdlePriv {
47         MissionControl *mc;
48         DBusGProxy     *gs_proxy;
49         gboolean        is_idle;
50         McPresence      state;
51         McPresence      slack_state;
52         gchar          *status;
53         McPresence      saved_state;
54         gchar          *saved_status;
55         guint           ext_away_timeout;
56 };
57
58 static void     empathy_idle_class_init      (EmpathyIdleClass *klass);
59 static void     empathy_idle_init            (EmpathyIdle      *idle);
60 static void     idle_finalize                (GObject          *object);
61 static void     idle_get_property            (GObject          *object,
62                                               guint             param_id,
63                                               GValue           *value,
64                                               GParamSpec       *pspec);
65 static void     idle_set_property            (GObject          *object,
66                                               guint             param_id,
67                                               const GValue     *value,
68                                               GParamSpec       *pspec);
69 static void     idle_presence_changed_cb     (MissionControl   *mc,
70                                               McPresence        state,
71                                               EmpathyIdle      *idle);
72 static void     idle_session_idle_changed_cb (DBusGProxy       *gs_proxy,
73                                               gboolean          is_idle,
74                                               EmpathyIdle      *idle);
75 static void     idle_ext_away_start          (EmpathyIdle      *idle);
76 static void     idle_ext_away_stop           (EmpathyIdle      *idle);
77 static gboolean idle_ext_away_cb             (EmpathyIdle      *idle);
78
79 enum {
80         PROP_0,
81         PROP_STATE,
82         PROP_STATUS,
83         PROP_SLACK_STATE
84 };
85
86 G_DEFINE_TYPE (EmpathyIdle, empathy_idle, G_TYPE_OBJECT)
87
88 static void
89 empathy_idle_class_init (EmpathyIdleClass *klass)
90 {
91         GObjectClass *object_class = G_OBJECT_CLASS (klass);
92
93         object_class->finalize = idle_finalize;
94         object_class->get_property = idle_get_property;
95         object_class->set_property = idle_set_property;
96
97         g_object_class_install_property (object_class,
98                                          PROP_STATE,
99                                          g_param_spec_uint ("state",
100                                                             "state",
101                                                             "state",
102                                                             MC_PRESENCE_UNSET,
103                                                             LAST_MC_PRESENCE,
104                                                             MC_PRESENCE_AVAILABLE,
105                                                             G_PARAM_READWRITE));
106         g_object_class_install_property (object_class,
107                                          PROP_STATUS,
108                                          g_param_spec_string ("status",
109                                                               "status",
110                                                               "status",
111                                                               NULL,
112                                                               G_PARAM_READWRITE));
113         g_object_class_install_property (object_class,
114                                          PROP_SLACK_STATE,
115                                          g_param_spec_uint ("slack-state",
116                                                             "slack-state",
117                                                             "slack-state",
118                                                             MC_PRESENCE_UNSET,
119                                                             LAST_MC_PRESENCE,
120                                                             MC_PRESENCE_UNSET,
121                                                             G_PARAM_READWRITE));
122
123         g_type_class_add_private (object_class, sizeof (EmpathyIdlePriv));
124 }
125
126 static void
127 empathy_idle_init (EmpathyIdle *idle)
128 {
129         EmpathyIdlePriv *priv;
130
131         priv = GET_PRIV (idle);
132
133         priv->is_idle = FALSE;
134         priv->mc = gossip_mission_control_new ();
135         priv->state = mission_control_get_presence_actual (priv->mc, NULL);
136         priv->status = mission_control_get_presence_message_actual (priv->mc, NULL);
137         priv->gs_proxy = dbus_g_proxy_new_for_name (tp_get_bus (),
138                                                     "org.gnome.ScreenSaver",
139                                                     "/org/gnome/ScreenSaver",
140                                                     "org.gnome.ScreenSaver");
141         if (!priv->gs_proxy) {
142                 gossip_debug (DEBUG_DOMAIN, "Failed to get gs proxy");
143                 return;
144         }
145
146         dbus_g_proxy_add_signal (priv->gs_proxy, "SessionIdleChanged",
147                                  G_TYPE_BOOLEAN,
148                                  G_TYPE_INVALID);
149         dbus_g_proxy_connect_signal (priv->gs_proxy, "SessionIdleChanged",
150                                      G_CALLBACK (idle_session_idle_changed_cb),
151                                      idle, NULL);
152         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
153                                      "PresenceStatusActual",
154                                      G_CALLBACK (idle_presence_changed_cb),
155                                      idle, NULL);
156 }
157
158 static void
159 idle_finalize (GObject *object)
160 {
161         EmpathyIdlePriv *priv;
162
163         priv = GET_PRIV (object);
164
165         g_free (priv->status);
166         g_free (priv->saved_status);
167         g_object_unref (priv->mc);
168
169         if (priv->gs_proxy) {
170                 g_object_unref (priv->gs_proxy);
171         }
172
173         idle_ext_away_stop (EMPATHY_IDLE (object));
174 }
175
176 static void
177 idle_get_property (GObject    *object,
178                    guint       param_id,
179                    GValue     *value,
180                    GParamSpec *pspec)
181 {
182         EmpathyIdlePriv *priv;
183         EmpathyIdle     *idle;
184
185         priv = GET_PRIV (object);
186         idle = EMPATHY_IDLE (object);
187
188         switch (param_id) {
189         case PROP_STATE:
190                 g_value_set_uint (value, empathy_idle_get_state (idle));
191                 break;
192         case PROP_STATUS:
193                 g_value_set_string (value, empathy_idle_get_status (idle));
194                 break;
195         case PROP_SLACK_STATE:
196                 g_value_set_uint (value, empathy_idle_get_slack_state (idle));
197                 break;
198         default:
199                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
200                 break;
201         };
202 }
203
204 static void
205 idle_set_property (GObject      *object,
206                    guint         param_id,
207                    const GValue *value,
208                    GParamSpec   *pspec)
209 {
210         EmpathyIdlePriv *priv;
211         EmpathyIdle     *idle;
212
213         priv = GET_PRIV (object);
214         idle = EMPATHY_IDLE (object);
215
216         switch (param_id) {
217         case PROP_STATE:
218                 empathy_idle_set_state (idle, g_value_get_uint (value));
219                 break;
220         case PROP_STATUS:
221                 empathy_idle_set_status (idle, g_value_get_string (value));
222                 break;
223         case PROP_SLACK_STATE:
224                 empathy_idle_set_slack_state (idle, g_value_get_uint (value));
225                 break;
226         default:
227                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
228                 break;
229         };
230 }
231
232 EmpathyIdle *
233 empathy_idle_new (void)
234 {
235         static EmpathyIdle *idle = NULL;
236
237         if (!idle) {
238                 idle = g_object_new (EMPATHY_TYPE_IDLE, NULL);
239                 g_object_add_weak_pointer (G_OBJECT (idle), (gpointer) &idle);
240         } else {
241                 g_object_ref (idle);
242         }
243
244         return idle;
245 }
246
247 McPresence
248 empathy_idle_get_state (EmpathyIdle *idle)
249 {
250         EmpathyIdlePriv *priv;
251
252         priv = GET_PRIV (idle);
253
254         return priv->state;
255 }
256
257 void
258 empathy_idle_set_state (EmpathyIdle *idle,
259                         McPresence   state)
260 {
261         EmpathyIdlePriv *priv;
262
263         priv = GET_PRIV (idle);
264
265         mission_control_set_presence (priv->mc,
266                                       state,
267                                       priv->status,
268                                       NULL, NULL);
269 }
270
271 const gchar *
272 empathy_idle_get_status (EmpathyIdle *idle)
273 {
274         EmpathyIdlePriv *priv;
275
276         priv = GET_PRIV (idle);
277
278         return priv->status;
279 }
280
281 void
282 empathy_idle_set_status (EmpathyIdle *idle,
283                          const gchar *status)
284 {
285         EmpathyIdlePriv *priv;
286
287         priv = GET_PRIV (idle);
288
289         mission_control_set_presence (priv->mc,
290                                       priv->state,
291                                       status,
292                                       NULL, NULL);
293 }
294
295 McPresence
296 empathy_idle_get_slack_state (EmpathyIdle *idle)
297 {
298         EmpathyIdlePriv *priv;
299
300         priv = GET_PRIV (idle);
301
302         return priv->slack_state;
303 }
304
305 void
306 empathy_idle_set_slack_state (EmpathyIdle *idle,
307                               McPresence   state)
308 {
309         EmpathyIdlePriv *priv;
310
311         priv = GET_PRIV (idle);
312
313         priv->slack_state = state;
314
315         g_object_notify (G_OBJECT (idle), "slack-state");
316 }
317
318 static void
319 idle_presence_changed_cb (MissionControl *mc,
320                           McPresence      state,
321                           EmpathyIdle    *idle)
322 {
323         EmpathyIdlePriv *priv;
324
325         priv = GET_PRIV (idle);
326
327         g_free (priv->status);
328         priv->state = state;
329         priv->status = mission_control_get_presence_message_actual (priv->mc, NULL);
330
331         if (G_STR_EMPTY (priv->status)) {
332                 g_free (priv->status);
333                 priv->status = g_strdup (gossip_presence_state_get_default_status (state));
334         }
335
336         g_object_notify (G_OBJECT (idle), "state");
337         g_object_notify (G_OBJECT (idle), "status");
338 }
339
340 static void
341 idle_session_idle_changed_cb (DBusGProxy  *gs_proxy,
342                               gboolean     is_idle,
343                               EmpathyIdle *idle)
344 {
345         EmpathyIdlePriv *priv;
346
347         priv = GET_PRIV (idle);
348
349         gossip_debug (DEBUG_DOMAIN, "Session idle state changed, %s -> %s",
350                       priv->is_idle ? "yes" : "no",
351                       is_idle ? "yes" : "no");
352
353         if (is_idle && !priv->is_idle) {
354                 McPresence new_state;
355                 /* We are now idle, set state to away */
356
357                 if (priv->state <= MC_PRESENCE_OFFLINE ||
358                     priv->state == MC_PRESENCE_HIDDEN) {
359                         /* We are not online so nothing to do here */
360                         return;
361                 } else if (priv->state == MC_PRESENCE_AWAY ||
362                            priv->state == MC_PRESENCE_EXTENDED_AWAY) {
363                         /* User set away manually, when coming back we restore
364                          * default presence. */
365                         new_state = priv->state;
366                         priv->saved_state = MC_PRESENCE_AVAILABLE;
367                         priv->saved_status = NULL;
368                 } else {
369                         new_state = MC_PRESENCE_AWAY;
370                         priv->saved_state = priv->state;
371                         priv->saved_status = g_strdup (priv->status);
372                 }
373
374                 gossip_debug (DEBUG_DOMAIN, "Going to autoaway");
375                 empathy_idle_set_state (idle, new_state);
376
377                 idle_ext_away_start (idle);
378         } else if (!is_idle && priv->is_idle) {
379                 /* We are no more idle, restore state */
380                 idle_ext_away_stop (idle);
381
382                 gossip_debug (DEBUG_DOMAIN, "Restoring state to %d %s",
383                               priv->saved_state,
384                               priv->saved_status);
385
386                 mission_control_set_presence (priv->mc,
387                                               priv->saved_state,
388                                               priv->saved_status,
389                                               NULL, NULL);
390
391                 g_free (priv->saved_status);
392                 priv->saved_status = NULL;
393         }
394
395         priv->is_idle = is_idle;
396 }
397
398 static void
399 idle_ext_away_start (EmpathyIdle *idle)
400 {
401         EmpathyIdlePriv *priv;
402
403         priv = GET_PRIV (idle);
404
405         idle_ext_away_stop (idle);
406         priv->ext_away_timeout = g_timeout_add (EXT_AWAY_TIME * 1000,
407                                                 (GSourceFunc) idle_ext_away_cb,
408                                                 idle);
409 }
410
411 static void
412 idle_ext_away_stop (EmpathyIdle *idle)
413 {
414         EmpathyIdlePriv *priv;
415
416         priv = GET_PRIV (idle);
417
418         if (priv->ext_away_timeout) {
419                 g_source_remove (priv->ext_away_timeout);
420                 priv->ext_away_timeout = 0;
421         }
422 }
423
424 static gboolean
425 idle_ext_away_cb (EmpathyIdle *idle)
426 {
427         EmpathyIdlePriv *priv;
428
429         priv = GET_PRIV (idle);
430
431         gossip_debug (DEBUG_DOMAIN, "Going to extended autoaway");
432         mission_control_set_presence (priv->mc,
433                                       MC_PRESENCE_EXTENDED_AWAY,
434                                       priv->saved_status,
435                                       NULL, NULL);
436
437         priv->ext_away_timeout = 0;
438
439         return FALSE;
440 }
441