1 /* -*- mode: C; c-file-style: "linux"; indent-tabs-mode: t -*-
2 *
3 * Adapted from gnome-session/gnome-session/gs-idle-monitor.c
4 *
5 * Copyright (C) 2012 Red Hat, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <time.h>
24 #include <string.h>
25
26 #include <glib.h>
27 #include <glib/gi18n-lib.h>
28 #include <gdk/gdk.h>
29 #if defined(GDK_WINDOWING_WAYLAND)
30 #include <gdk/gdkwayland.h>
31 #endif
32
33 #define GNOME_DESKTOP_USE_UNSTABLE_API
34 #include "gnome-idle-monitor.h"
35 #include "meta-dbus-idle-monitor.h"
36
37 G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer));
38
39 struct _GnomeIdleMonitorPrivate
40 {
41 GCancellable *cancellable;
42 MetaDBusIdleMonitor *proxy;
43 MetaDBusObjectManagerClient *om;
44 int name_watch_id;
45 GHashTable *watches;
46 GHashTable *watches_by_upstream_id;
47 };
48
49 typedef struct
50 {
51 int ref_count;
52 gboolean dead;
53 GnomeIdleMonitor *monitor;
54 guint id;
55 guint upstream_id;
56 GnomeIdleMonitorWatchFunc callback;
57 gpointer user_data;
58 GDestroyNotify notify;
59 guint64 timeout_msec;
60 } GnomeIdleMonitorWatch;
61
62 static void gnome_idle_monitor_initable_iface_init (GInitableIface *iface);
63 static void gnome_idle_monitor_remove_watch_internal (GnomeIdleMonitor *monitor,
64 guint id);
65
66 static void add_idle_watch (GnomeIdleMonitor *, GnomeIdleMonitorWatch *);
67 static void add_active_watch (GnomeIdleMonitor *, GnomeIdleMonitorWatch *);
68
G_DEFINE_TYPE_WITH_CODE(GnomeIdleMonitor,gnome_idle_monitor,G_TYPE_OBJECT,G_ADD_PRIVATE (GnomeIdleMonitor)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gnome_idle_monitor_initable_iface_init))69 G_DEFINE_TYPE_WITH_CODE (GnomeIdleMonitor, gnome_idle_monitor, G_TYPE_OBJECT,
70 G_ADD_PRIVATE (GnomeIdleMonitor)
71 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
72 gnome_idle_monitor_initable_iface_init))
73
74 #define IDLE_MONITOR_PATH "/org/gnome/Mutter/IdleMonitor/Core"
75
76 static void
77 on_watch_fired (MetaDBusIdleMonitor *proxy,
78 guint upstream_id,
79 GnomeIdleMonitor *monitor)
80 {
81 GnomeIdleMonitorWatch *watch;
82
83 watch = g_hash_table_lookup (monitor->priv->watches_by_upstream_id, GINT_TO_POINTER (upstream_id));
84 if (!watch)
85 return;
86
87 g_object_ref (monitor);
88
89 if (watch->callback) {
90 watch->callback (watch->monitor,
91 watch->id,
92 watch->user_data);
93 }
94
95 if (watch->timeout_msec == 0)
96 gnome_idle_monitor_remove_watch_internal (monitor, watch->id);
97
98 g_object_unref (monitor);
99 }
100
101 static guint32
get_next_watch_serial(void)102 get_next_watch_serial (void)
103 {
104 static guint32 serial = 0;
105 g_atomic_int_inc (&serial);
106 return serial;
107 }
108
109 static void
idle_monitor_watch_unref(GnomeIdleMonitorWatch * watch)110 idle_monitor_watch_unref (GnomeIdleMonitorWatch *watch)
111 {
112 watch->ref_count--;
113 if (watch->ref_count)
114 return;
115
116 if (watch->notify != NULL)
117 watch->notify (watch->user_data);
118
119
120 if (watch->upstream_id != 0)
121 g_hash_table_remove (watch->monitor->priv->watches_by_upstream_id,
122 GINT_TO_POINTER (watch->upstream_id));
123
124 g_slice_free (GnomeIdleMonitorWatch, watch);
125 }
126
127 static GnomeIdleMonitorWatch *
idle_monitor_watch_ref(GnomeIdleMonitorWatch * watch)128 idle_monitor_watch_ref (GnomeIdleMonitorWatch *watch)
129 {
130 g_assert (watch->ref_count > 0);
131
132 watch->ref_count++;
133 return watch;
134 }
135
136 static void
idle_monitor_watch_destroy(GnomeIdleMonitorWatch * watch)137 idle_monitor_watch_destroy (GnomeIdleMonitorWatch *watch)
138 {
139 watch->dead = TRUE;
140 idle_monitor_watch_unref (watch);
141 }
142
143 static void
gnome_idle_monitor_dispose(GObject * object)144 gnome_idle_monitor_dispose (GObject *object)
145 {
146 GnomeIdleMonitor *monitor;
147
148 monitor = GNOME_IDLE_MONITOR (object);
149
150 if (monitor->priv->cancellable)
151 g_cancellable_cancel (monitor->priv->cancellable);
152 g_clear_object (&monitor->priv->cancellable);
153
154 if (monitor->priv->name_watch_id) {
155 g_bus_unwatch_name (monitor->priv->name_watch_id);
156 monitor->priv->name_watch_id = 0;
157 }
158
159 g_clear_object (&monitor->priv->proxy);
160 g_clear_object (&monitor->priv->om);
161 g_clear_pointer (&monitor->priv->watches, g_hash_table_destroy);
162 g_clear_pointer (&monitor->priv->watches_by_upstream_id, g_hash_table_destroy);
163
164 G_OBJECT_CLASS (gnome_idle_monitor_parent_class)->dispose (object);
165 }
166
167 static void
add_known_watch(gpointer key,gpointer value,gpointer user_data)168 add_known_watch (gpointer key,
169 gpointer value,
170 gpointer user_data)
171 {
172 GnomeIdleMonitor *monitor = user_data;
173 GnomeIdleMonitorWatch *watch = value;
174
175 if (watch->timeout_msec == 0)
176 add_active_watch (monitor, watch);
177 else
178 add_idle_watch (monitor, watch);
179 }
180
181 static void
connect_proxy(GDBusObject * object,GnomeIdleMonitor * monitor)182 connect_proxy (GDBusObject *object,
183 GnomeIdleMonitor *monitor)
184 {
185 MetaDBusIdleMonitor *proxy;
186
187 proxy = meta_dbus_object_get_idle_monitor (META_DBUS_OBJECT (object));
188 if (!proxy) {
189 g_critical ("Unable to get idle monitor from object at %s",
190 g_dbus_object_get_object_path (object));
191 return;
192 }
193
194 monitor->priv->proxy = proxy;
195 g_signal_connect_object (proxy, "watch-fired", G_CALLBACK (on_watch_fired), monitor, 0);
196 g_hash_table_foreach (monitor->priv->watches, add_known_watch, monitor);
197 }
198
199 static void
on_object_added(GDBusObjectManager * manager,GDBusObject * object,gpointer user_data)200 on_object_added (GDBusObjectManager *manager,
201 GDBusObject *object,
202 gpointer user_data)
203 {
204 GnomeIdleMonitor *monitor = user_data;
205
206 if (!g_str_equal (IDLE_MONITOR_PATH, g_dbus_object_get_object_path (object)))
207 return;
208
209 connect_proxy (object, monitor);
210
211 g_signal_handlers_disconnect_by_func (manager, on_object_added, user_data);
212 }
213
214 static void
get_proxy(GnomeIdleMonitor * monitor)215 get_proxy (GnomeIdleMonitor *monitor)
216 {
217 GDBusObject *object;
218
219 object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (monitor->priv->om),
220 IDLE_MONITOR_PATH);
221 if (object) {
222 connect_proxy (object, monitor);
223 g_object_unref (object);
224 return;
225 }
226
227 g_signal_connect_object (monitor->priv->om, "object-added",
228 G_CALLBACK (on_object_added), monitor, 0);
229 }
230
231 static void
on_object_manager_ready(GObject * source,GAsyncResult * res,gpointer user_data)232 on_object_manager_ready (GObject *source,
233 GAsyncResult *res,
234 gpointer user_data)
235 {
236 GnomeIdleMonitor *monitor = user_data;
237 GDBusObjectManager *om;
238 GError *error = NULL;
239
240 om = meta_dbus_object_manager_client_new_finish (res, &error);
241 if (!om) {
242 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
243 g_warning ("Failed to acquire idle monitor object manager: %s", error->message);
244 g_error_free (error);
245 return;
246 }
247
248 monitor->priv->om = META_DBUS_OBJECT_MANAGER_CLIENT (om);
249 get_proxy (monitor);
250 }
251
252 static void
on_name_appeared(GDBusConnection * connection,const char * name,const char * name_owner,gpointer user_data)253 on_name_appeared (GDBusConnection *connection,
254 const char *name,
255 const char *name_owner,
256 gpointer user_data)
257 {
258 GnomeIdleMonitor *monitor = user_data;
259
260 meta_dbus_object_manager_client_new (connection,
261 G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
262 name_owner,
263 "/org/gnome/Mutter/IdleMonitor",
264 monitor->priv->cancellable,
265 on_object_manager_ready,
266 monitor);
267 }
268
269 static void
clear_watch(gpointer key,gpointer value,gpointer user_data)270 clear_watch (gpointer key,
271 gpointer value,
272 gpointer user_data)
273 {
274 GnomeIdleMonitorWatch *watch = value;
275 GnomeIdleMonitor *monitor = user_data;
276
277 g_hash_table_remove (monitor->priv->watches_by_upstream_id, GINT_TO_POINTER (watch->upstream_id));
278 watch->upstream_id = 0;
279 }
280
281 static void
on_name_vanished(GDBusConnection * connection,const char * name,gpointer user_data)282 on_name_vanished (GDBusConnection *connection,
283 const char *name,
284 gpointer user_data)
285 {
286 GnomeIdleMonitor *monitor = user_data;
287
288 g_hash_table_foreach (monitor->priv->watches, clear_watch, monitor);
289 g_clear_object (&monitor->priv->proxy);
290 g_clear_object (&monitor->priv->om);
291 }
292
293 static gboolean
gnome_idle_monitor_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)294 gnome_idle_monitor_initable_init (GInitable *initable,
295 GCancellable *cancellable,
296 GError **error)
297 {
298 GnomeIdleMonitor *monitor;
299
300 monitor = GNOME_IDLE_MONITOR (initable);
301
302 monitor->priv->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
303 "org.gnome.Mutter.IdleMonitor",
304 G_BUS_NAME_WATCHER_FLAGS_NONE,
305 on_name_appeared,
306 on_name_vanished,
307 monitor, NULL);
308
309 return TRUE;
310 }
311
312 static void
gnome_idle_monitor_initable_iface_init(GInitableIface * iface)313 gnome_idle_monitor_initable_iface_init (GInitableIface *iface)
314 {
315 iface->init = gnome_idle_monitor_initable_init;
316 }
317
318 static void
gnome_idle_monitor_class_init(GnomeIdleMonitorClass * klass)319 gnome_idle_monitor_class_init (GnomeIdleMonitorClass *klass)
320 {
321 GObjectClass *object_class = G_OBJECT_CLASS (klass);
322
323 object_class->dispose = gnome_idle_monitor_dispose;
324 }
325
326 static void
gnome_idle_monitor_init(GnomeIdleMonitor * monitor)327 gnome_idle_monitor_init (GnomeIdleMonitor *monitor)
328 {
329 monitor->priv = gnome_idle_monitor_get_instance_private (monitor);
330
331 monitor->priv->watches = g_hash_table_new_full (NULL,
332 NULL,
333 NULL,
334 (GDestroyNotify)idle_monitor_watch_destroy);
335 monitor->priv->watches_by_upstream_id = g_hash_table_new (NULL, NULL);
336
337 monitor->priv->cancellable = g_cancellable_new ();
338 }
339
340 /**
341 * gnome_idle_monitor_new:
342 *
343 * Returns: a new #GnomeIdleMonitor that tracks the server-global
344 * idletime for all devices.
345 */
346 GnomeIdleMonitor *
gnome_idle_monitor_new(void)347 gnome_idle_monitor_new (void)
348 {
349 return GNOME_IDLE_MONITOR (g_initable_new (GNOME_TYPE_IDLE_MONITOR, NULL, NULL, NULL));
350 }
351
352 static GnomeIdleMonitorWatch *
make_watch(GnomeIdleMonitor * monitor,guint64 timeout_msec,GnomeIdleMonitorWatchFunc callback,gpointer user_data,GDestroyNotify notify)353 make_watch (GnomeIdleMonitor *monitor,
354 guint64 timeout_msec,
355 GnomeIdleMonitorWatchFunc callback,
356 gpointer user_data,
357 GDestroyNotify notify)
358 {
359 GnomeIdleMonitorWatch *watch;
360
361 watch = g_slice_new0 (GnomeIdleMonitorWatch);
362 watch->ref_count = 1;
363 watch->id = get_next_watch_serial ();
364 watch->monitor = monitor;
365 watch->callback = callback;
366 watch->user_data = user_data;
367 watch->notify = notify;
368 watch->timeout_msec = timeout_msec;
369
370 return watch;
371 }
372
373 static void
on_watch_added(GObject * object,GAsyncResult * result,gpointer user_data)374 on_watch_added (GObject *object,
375 GAsyncResult *result,
376 gpointer user_data)
377 {
378 GnomeIdleMonitorWatch *watch = user_data;
379 GnomeIdleMonitor *monitor;
380 GError *error;
381 GVariant *res;
382
383 error = NULL;
384 res = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), result, &error);
385 if (!res) {
386 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
387 g_error_free (error);
388 idle_monitor_watch_unref (watch);
389 return;
390 }
391
392 g_warning ("Failed to acquire idle monitor proxy: %s", error->message);
393 g_error_free (error);
394 idle_monitor_watch_unref (watch);
395 return;
396 }
397
398 if (watch->dead) {
399 idle_monitor_watch_unref (watch);
400 return;
401 }
402
403 monitor = watch->monitor;
404 g_variant_get (res, "(u)", &watch->upstream_id);
405 g_variant_unref (res);
406
407 g_hash_table_insert (monitor->priv->watches_by_upstream_id,
408 GINT_TO_POINTER (watch->upstream_id), watch);
409 idle_monitor_watch_unref (watch);
410 }
411
412 static void
add_idle_watch(GnomeIdleMonitor * monitor,GnomeIdleMonitorWatch * watch)413 add_idle_watch (GnomeIdleMonitor *monitor,
414 GnomeIdleMonitorWatch *watch)
415 {
416 meta_dbus_idle_monitor_call_add_idle_watch (monitor->priv->proxy,
417 watch->timeout_msec,
418 monitor->priv->cancellable,
419 on_watch_added, idle_monitor_watch_ref (watch));
420 }
421
422 static void
add_active_watch(GnomeIdleMonitor * monitor,GnomeIdleMonitorWatch * watch)423 add_active_watch (GnomeIdleMonitor *monitor,
424 GnomeIdleMonitorWatch *watch)
425 {
426 meta_dbus_idle_monitor_call_add_user_active_watch (monitor->priv->proxy,
427 monitor->priv->cancellable,
428 on_watch_added, idle_monitor_watch_ref (watch));
429 }
430
431 /**
432 * gnome_idle_monitor_add_idle_watch:
433 * @monitor: A #GnomeIdleMonitor
434 * @interval_msec: The idletime interval, in milliseconds. It must be
435 * a strictly positive value (> 0).
436 * @callback: (allow-none): The callback to call when the user has
437 * accumulated @interval_msec milliseconds of idle time.
438 * @user_data: (allow-none): The user data to pass to the callback
439 * @notify: A #GDestroyNotify
440 *
441 * Returns: a watch id
442 *
443 * Adds a watch for a specific idle time. The callback will be called
444 * when the user has accumulated @interval_msec milliseconds of idle time.
445 * This function will return an ID that can either be passed to
446 * gnome_idle_monitor_remove_watch(), or can be used to tell idle time
447 * watches apart if you have more than one.
448 *
449 * Also note that this function will only care about positive transitions
450 * (user's idle time exceeding a certain time). If you want to know about
451 * when the user has become active, use
452 * gnome_idle_monitor_add_user_active_watch().
453 */
454 guint
gnome_idle_monitor_add_idle_watch(GnomeIdleMonitor * monitor,guint64 interval_msec,GnomeIdleMonitorWatchFunc callback,gpointer user_data,GDestroyNotify notify)455 gnome_idle_monitor_add_idle_watch (GnomeIdleMonitor *monitor,
456 guint64 interval_msec,
457 GnomeIdleMonitorWatchFunc callback,
458 gpointer user_data,
459 GDestroyNotify notify)
460 {
461 GnomeIdleMonitorWatch *watch;
462
463 g_return_val_if_fail (GNOME_IS_IDLE_MONITOR (monitor), 0);
464 g_return_val_if_fail (interval_msec > 0, 0);
465
466 watch = make_watch (monitor,
467 interval_msec,
468 callback,
469 user_data,
470 notify);
471
472 g_hash_table_insert (monitor->priv->watches,
473 GUINT_TO_POINTER (watch->id),
474 watch);
475
476 if (monitor->priv->proxy)
477 add_idle_watch (monitor, watch);
478
479 return watch->id;
480 }
481
482 /**
483 * gnome_idle_monitor_add_user_active_watch:
484 * @monitor: A #GnomeIdleMonitor
485 * @callback: (allow-none): The callback to call when the user is
486 * active again.
487 * @user_data: (allow-none): The user data to pass to the callback
488 * @notify: A #GDestroyNotify
489 *
490 * Returns: a watch id
491 *
492 * Add a one-time watch to know when the user is active again.
493 * Note that this watch is one-time and will de-activate after the
494 * function is called, for efficiency purposes. It's most convenient
495 * to call this when an idle watch, as added by
496 * gnome_idle_monitor_add_idle_watch(), has triggered.
497 */
498 guint
gnome_idle_monitor_add_user_active_watch(GnomeIdleMonitor * monitor,GnomeIdleMonitorWatchFunc callback,gpointer user_data,GDestroyNotify notify)499 gnome_idle_monitor_add_user_active_watch (GnomeIdleMonitor *monitor,
500 GnomeIdleMonitorWatchFunc callback,
501 gpointer user_data,
502 GDestroyNotify notify)
503 {
504 GnomeIdleMonitorWatch *watch;
505
506 g_return_val_if_fail (GNOME_IS_IDLE_MONITOR (monitor), 0);
507
508 watch = make_watch (monitor,
509 0,
510 callback,
511 user_data,
512 notify);
513
514 g_hash_table_insert (monitor->priv->watches,
515 GUINT_TO_POINTER (watch->id),
516 watch);
517
518 if (monitor->priv->proxy)
519 add_active_watch (monitor, watch);
520
521 return watch->id;
522 }
523
524 /**
525 * gnome_idle_monitor_remove_watch:
526 * @monitor: A #GnomeIdleMonitor
527 * @id: A watch ID
528 *
529 * Removes an idle time watcher, previously added by
530 * gnome_idle_monitor_add_idle_watch() or
531 * gnome_idle_monitor_add_user_active_watch().
532 */
533 void
gnome_idle_monitor_remove_watch(GnomeIdleMonitor * monitor,guint id)534 gnome_idle_monitor_remove_watch (GnomeIdleMonitor *monitor,
535 guint id)
536 {
537 GnomeIdleMonitorWatch *watch;
538
539 g_return_if_fail (GNOME_IS_IDLE_MONITOR (monitor));
540
541 watch = g_hash_table_lookup (monitor->priv->watches, GINT_TO_POINTER (id));
542 if (!watch)
543 return;
544
545 if (watch->upstream_id)
546 meta_dbus_idle_monitor_call_remove_watch (monitor->priv->proxy,
547 watch->upstream_id,
548 NULL, NULL, NULL);
549
550 gnome_idle_monitor_remove_watch_internal (monitor, id);
551 }
552
553 static void
gnome_idle_monitor_remove_watch_internal(GnomeIdleMonitor * monitor,guint id)554 gnome_idle_monitor_remove_watch_internal (GnomeIdleMonitor *monitor,
555 guint id)
556 {
557 g_hash_table_remove (monitor->priv->watches,
558 GUINT_TO_POINTER (id));
559 }
560
561 /**
562 * gnome_idle_monitor_get_idletime:
563 * @monitor: A #GnomeIdleMonitor
564 *
565 * Returns: The current idle time, in milliseconds
566 */
567 guint64
gnome_idle_monitor_get_idletime(GnomeIdleMonitor * monitor)568 gnome_idle_monitor_get_idletime (GnomeIdleMonitor *monitor)
569 {
570 guint64 value;
571
572 value = 0;
573 if (monitor->priv->proxy)
574 meta_dbus_idle_monitor_call_get_idletime_sync (monitor->priv->proxy, &value,
575 NULL, NULL);
576
577 return value;
578 }
579