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