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