1 /*
2 * slacker.c - Idleness monitor
3 * Copyright ©2010 Collabora Ltd.
4 * Copyright ©2008-2010 Nokia Corporation
5 * Copyright ©2013 Intel Corporation
6 *
7 * Derived from code in e-book-backend-tp.c in eds-backend-telepathy; thanks!
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23 #include "config.h"
24
25 #include "mcd-slacker.h"
26
27 #include <telepathy-glib/telepathy-glib.h>
28
29 #include "mcd-debug.h"
30
31 struct _McdSlackerPrivate {
32 GDBusProxy *proxy;
33
34 gboolean is_inactive;
35 };
36
37 G_DEFINE_TYPE (McdSlacker, mcd_slacker, G_TYPE_OBJECT)
38
39 enum {
40 SIG_INACTIVITY_CHANGED = 0,
41 N_SIGNALS
42 };
43
44 static guint signals[N_SIGNALS];
45
46 /* GNOME Session Manager interface description:
47 * https://git.gnome.org/browse/gnome-session/tree/gnome-session/org.gnome.SessionManager.Presence.xml
48 */
49 enum {
50 STATUS_AVAILABLE = 0,
51 STATUS_INVISIBLE,
52 STATUS_BUSY,
53 STATUS_IDLE
54 };
55
56 #define SERVICE_NAME "org.gnome.SessionManager"
57 #define SERVICE_OBJECT_PATH "/org/gnome/SessionManager/Presence"
58 #define SERVICE_INTERFACE "org.gnome.SessionManager.Presence"
59 #define SERVICE_PROP_NAME "status"
60 #define SERVICE_SIG_NAME "StatusChanged"
61
62 /**
63 * mcd_slacker_is_inactive:
64 * @self: do some work!
65 *
66 * <!-- -->
67 *
68 * Returns: %TRUE if the device is known to be inactive; false otherwise.
69 */
70 gboolean
mcd_slacker_is_inactive(McdSlacker * self)71 mcd_slacker_is_inactive (McdSlacker *self)
72 {
73 g_return_val_if_fail (MCD_IS_SLACKER (self), FALSE);
74
75 return self->priv->is_inactive;
76 }
77
78 static void
status_changed(McdSlacker * self,GVariant * prop)79 status_changed (McdSlacker *self,
80 GVariant *prop)
81 {
82 gboolean old = self->priv->is_inactive;
83
84 if (g_variant_classify (prop) != G_VARIANT_CLASS_UINT32)
85 {
86 WARNING ("%s.%s property is of type %s and we expected u",
87 SERVICE_INTERFACE, SERVICE_PROP_NAME,
88 g_variant_get_type_string (prop));
89 return;
90 }
91
92 self->priv->is_inactive = (g_variant_get_uint32 (prop) == STATUS_IDLE);
93
94 if (self->priv->is_inactive != old)
95 {
96 DEBUG ("device became %s",
97 self->priv->is_inactive ? "inactive" : "active");
98 g_signal_emit (self, signals[SIG_INACTIVITY_CHANGED], 0,
99 self->priv->is_inactive);
100 }
101 }
102
103 static void
signal_cb(GDBusProxy * proxy,gchar * sender_name,gchar * signal_name,GVariant * parameters,gpointer user_data)104 signal_cb (GDBusProxy *proxy,
105 gchar *sender_name,
106 gchar *signal_name,
107 GVariant *parameters,
108 gpointer user_data)
109 {
110 McdSlacker *self = user_data;
111 GVariant *prop;
112
113 if (tp_strdiff (signal_name, SERVICE_SIG_NAME))
114 return;
115
116 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(u)")))
117 {
118 WARNING ("%s.%s arguments are of type %s and we expected (u)",
119 SERVICE_INTERFACE, SERVICE_PROP_NAME,
120 g_variant_get_type_string (parameters));
121 return;
122 }
123
124 prop = g_variant_get_child_value (parameters, 0);
125 status_changed (self, prop);
126 g_variant_unref (prop);
127 }
128
129 static void
proxy_new_cb(GObject * source,GAsyncResult * result,gpointer user_data)130 proxy_new_cb (GObject *source,
131 GAsyncResult *result,
132 gpointer user_data)
133 {
134 McdSlacker *self = user_data;
135 GVariant *prop;
136 GError *error = NULL;
137
138 self->priv->proxy = g_dbus_proxy_new_finish (result, &error);
139 if (self->priv->proxy == NULL)
140 {
141 DEBUG ("Error while creating slacker proxy: %s", error->message);
142 goto out;
143 }
144
145 g_signal_connect (self->priv->proxy, "g-signal",
146 G_CALLBACK (signal_cb), self);
147
148 prop = g_dbus_proxy_get_cached_property (self->priv->proxy, SERVICE_PROP_NAME);
149
150 if (g_dbus_proxy_get_name_owner (self->priv->proxy) == NULL)
151 {
152 DEBUG ("%s service not found", SERVICE_NAME);
153 }
154 else if (prop == NULL)
155 {
156 DEBUG ("%s.%s property is missing", SERVICE_INTERFACE, SERVICE_PROP_NAME);
157 }
158 else
159 {
160 status_changed (self, prop);
161 g_variant_unref (prop);
162 }
163
164 out:
165 g_object_unref (self);
166 }
167
168 static void
mcd_slacker_init(McdSlacker * self)169 mcd_slacker_init (McdSlacker *self)
170 {
171 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MCD_TYPE_SLACKER,
172 McdSlackerPrivate);
173 }
174
175 static gpointer slacker = NULL;
176
177 static GObject *
mcd_slacker_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)178 mcd_slacker_constructor (
179 GType type,
180 guint n_construct_properties,
181 GObjectConstructParam *construct_properties)
182 {
183 GObject *retval;
184
185 if (slacker == NULL)
186 {
187 slacker = G_OBJECT_CLASS (mcd_slacker_parent_class)->constructor (
188 type, n_construct_properties, construct_properties);
189 retval = slacker;
190 g_object_add_weak_pointer (retval, &slacker);
191 }
192 else
193 {
194 retval = g_object_ref (slacker);
195 }
196
197 return retval;
198 }
199
200 static void
mcd_slacker_constructed(GObject * object)201 mcd_slacker_constructed (GObject *object)
202 {
203 McdSlacker *self = MCD_SLACKER (object);
204
205 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
206 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
207 SERVICE_NAME, SERVICE_OBJECT_PATH, SERVICE_INTERFACE,
208 NULL,
209 proxy_new_cb, g_object_ref (self));
210 }
211
212 static void
mcd_slacker_dispose(GObject * object)213 mcd_slacker_dispose (GObject *object)
214 {
215 McdSlacker *self = MCD_SLACKER (object);
216
217 g_clear_object (&self->priv->proxy);
218
219 ((GObjectClass *) mcd_slacker_parent_class)->dispose (object);
220 }
221
222 static void
mcd_slacker_class_init(McdSlackerClass * klass)223 mcd_slacker_class_init (McdSlackerClass *klass)
224 {
225 GObjectClass *object_class = G_OBJECT_CLASS (klass);
226
227 object_class->constructor = mcd_slacker_constructor;
228 object_class->constructed = mcd_slacker_constructed;
229 object_class->dispose = mcd_slacker_dispose;
230
231 g_type_class_add_private (klass, sizeof (McdSlackerPrivate));
232
233 /**
234 * McdSlacker::inactivity-changed:
235 * @self: what a slacker
236 * @inactive: %TRUE if the device is inactive.
237 *
238 * The ::inactivity-changed is emitted when session becomes idle.
239 */
240 signals[SIG_INACTIVITY_CHANGED] = g_signal_new ("inactivity-changed",
241 MCD_TYPE_SLACKER, G_SIGNAL_RUN_LAST, 0, NULL, NULL,
242 NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
243 }
244
245 McdSlacker *
mcd_slacker_new()246 mcd_slacker_new ()
247 {
248 return g_object_new (MCD_TYPE_SLACKER, NULL);
249 }
250