1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright 2013-2021 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Adapted from gnome-session/gnome-session/gs-idle-monitor.c and
20 * from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c
21 */
22
23 #include "config.h"
24
25 #include "backends/meta-idle-manager.h"
26
27 #include "backends/meta-idle-monitor-private.h"
28 #include "clutter/clutter.h"
29 #include "meta/main.h"
30 #include "meta/meta-context.h"
31 #include "meta/meta-idle-monitor.h"
32 #include "meta/util.h"
33
34 #include "meta-dbus-idle-monitor.h"
35
36 typedef struct _MetaIdleManager
37 {
38 MetaBackend *backend;
39 guint dbus_name_id;
40
41 GHashTable *device_monitors;
42 } MetaIdleManager;
43
44 static gboolean
handle_get_idletime(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,MetaIdleMonitor * monitor)45 handle_get_idletime (MetaDBusIdleMonitor *skeleton,
46 GDBusMethodInvocation *invocation,
47 MetaIdleMonitor *monitor)
48 {
49 guint64 idletime;
50
51 idletime = meta_idle_monitor_get_idletime (monitor);
52 meta_dbus_idle_monitor_complete_get_idletime (skeleton, invocation, idletime);
53
54 return TRUE;
55 }
56
57 static gboolean
handle_reset_idletime(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,MetaIdleMonitor * monitor)58 handle_reset_idletime (MetaDBusIdleMonitor *skeleton,
59 GDBusMethodInvocation *invocation,
60 MetaIdleMonitor *monitor)
61 {
62 if (!g_getenv ("MUTTER_DEBUG_RESET_IDLETIME"))
63 {
64 g_dbus_method_invocation_return_error_literal (invocation,
65 G_DBUS_ERROR,
66 G_DBUS_ERROR_UNKNOWN_METHOD,
67 "This method is for testing purposes only. MUTTER_DEBUG_RESET_IDLETIME must be set to use it");
68 return TRUE;
69 }
70
71 meta_idle_manager_reset_idle_time (meta_idle_monitor_get_manager (monitor));
72 meta_dbus_idle_monitor_complete_reset_idletime (skeleton, invocation);
73
74 return TRUE;
75 }
76
77 typedef struct {
78 MetaDBusIdleMonitor *dbus_monitor;
79 MetaIdleMonitor *monitor;
80 char *dbus_name;
81 guint watch_id;
82 guint name_watcher_id;
83 } DBusWatch;
84
85 static void
destroy_dbus_watch(gpointer data)86 destroy_dbus_watch (gpointer data)
87 {
88 DBusWatch *watch = data;
89
90 g_object_unref (watch->dbus_monitor);
91 g_object_unref (watch->monitor);
92 g_free (watch->dbus_name);
93 g_bus_unwatch_name (watch->name_watcher_id);
94
95 g_free (watch);
96 }
97
98 static void
dbus_idle_callback(MetaIdleMonitor * monitor,guint watch_id,gpointer user_data)99 dbus_idle_callback (MetaIdleMonitor *monitor,
100 guint watch_id,
101 gpointer user_data)
102 {
103 DBusWatch *watch = user_data;
104 GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (watch->dbus_monitor);
105
106 g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (skeleton),
107 watch->dbus_name,
108 g_dbus_interface_skeleton_get_object_path (skeleton),
109 "org.gnome.Mutter.IdleMonitor",
110 "WatchFired",
111 g_variant_new ("(u)", watch_id),
112 NULL);
113 }
114
115 static void
name_vanished_callback(GDBusConnection * connection,const char * name,gpointer user_data)116 name_vanished_callback (GDBusConnection *connection,
117 const char *name,
118 gpointer user_data)
119 {
120 DBusWatch *watch = user_data;
121
122 meta_idle_monitor_remove_watch (watch->monitor, watch->watch_id);
123 }
124
125 static DBusWatch *
make_dbus_watch(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,MetaIdleMonitor * monitor)126 make_dbus_watch (MetaDBusIdleMonitor *skeleton,
127 GDBusMethodInvocation *invocation,
128 MetaIdleMonitor *monitor)
129 {
130 DBusWatch *watch;
131
132 watch = g_new0 (DBusWatch, 1);
133 watch->dbus_monitor = g_object_ref (skeleton);
134 watch->monitor = g_object_ref (monitor);
135 watch->dbus_name = g_strdup (g_dbus_method_invocation_get_sender (invocation));
136 watch->name_watcher_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (invocation),
137 watch->dbus_name,
138 G_BUS_NAME_WATCHER_FLAGS_NONE,
139 NULL, /* appeared */
140 name_vanished_callback,
141 watch, NULL);
142
143 return watch;
144 }
145
146 static gboolean
handle_add_idle_watch(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,guint64 interval,MetaIdleMonitor * monitor)147 handle_add_idle_watch (MetaDBusIdleMonitor *skeleton,
148 GDBusMethodInvocation *invocation,
149 guint64 interval,
150 MetaIdleMonitor *monitor)
151 {
152 DBusWatch *watch;
153
154 watch = make_dbus_watch (skeleton, invocation, monitor);
155 watch->watch_id = meta_idle_monitor_add_idle_watch (monitor, interval,
156 dbus_idle_callback, watch, destroy_dbus_watch);
157
158 meta_dbus_idle_monitor_complete_add_idle_watch (skeleton, invocation, watch->watch_id);
159
160 return TRUE;
161 }
162
163 static gboolean
handle_add_user_active_watch(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,MetaIdleMonitor * monitor)164 handle_add_user_active_watch (MetaDBusIdleMonitor *skeleton,
165 GDBusMethodInvocation *invocation,
166 MetaIdleMonitor *monitor)
167 {
168 DBusWatch *watch;
169
170 watch = make_dbus_watch (skeleton, invocation, monitor);
171 watch->watch_id = meta_idle_monitor_add_user_active_watch (monitor,
172 dbus_idle_callback, watch,
173 destroy_dbus_watch);
174
175 meta_dbus_idle_monitor_complete_add_user_active_watch (skeleton, invocation, watch->watch_id);
176
177 return TRUE;
178 }
179
180 static gboolean
handle_remove_watch(MetaDBusIdleMonitor * skeleton,GDBusMethodInvocation * invocation,guint id,MetaIdleMonitor * monitor)181 handle_remove_watch (MetaDBusIdleMonitor *skeleton,
182 GDBusMethodInvocation *invocation,
183 guint id,
184 MetaIdleMonitor *monitor)
185 {
186 meta_idle_monitor_remove_watch (monitor, id);
187 meta_dbus_idle_monitor_complete_remove_watch (skeleton, invocation);
188
189 return TRUE;
190 }
191
192 static void
create_monitor_skeleton(GDBusObjectManagerServer * manager,MetaIdleMonitor * monitor,const char * path)193 create_monitor_skeleton (GDBusObjectManagerServer *manager,
194 MetaIdleMonitor *monitor,
195 const char *path)
196 {
197 MetaDBusIdleMonitor *skeleton;
198 MetaDBusObjectSkeleton *object;
199
200 skeleton = meta_dbus_idle_monitor_skeleton_new ();
201 g_signal_connect (skeleton, "handle-add-idle-watch",
202 G_CALLBACK (handle_add_idle_watch), monitor);
203 g_signal_connect (skeleton, "handle-add-user-active-watch",
204 G_CALLBACK (handle_add_user_active_watch), monitor);
205 g_signal_connect (skeleton, "handle-remove-watch",
206 G_CALLBACK (handle_remove_watch), monitor);
207 g_signal_connect (skeleton, "handle-reset-idletime",
208 G_CALLBACK (handle_reset_idletime), monitor);
209 g_signal_connect (skeleton, "handle-get-idletime",
210 G_CALLBACK (handle_get_idletime), monitor);
211
212 object = meta_dbus_object_skeleton_new (path);
213 meta_dbus_object_skeleton_set_idle_monitor (object, skeleton);
214
215 g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object));
216
217 g_object_unref (skeleton);
218 g_object_unref (object);
219 }
220
221 static void
on_bus_acquired(GDBusConnection * connection,const char * name,gpointer user_data)222 on_bus_acquired (GDBusConnection *connection,
223 const char *name,
224 gpointer user_data)
225 {
226 MetaIdleManager *manager = user_data;
227 GDBusObjectManagerServer *object_manager;
228 MetaIdleMonitor *monitor;
229 char *path;
230
231 object_manager = g_dbus_object_manager_server_new ("/org/gnome/Mutter/IdleMonitor");
232
233 /* We never clear the core monitor, as that's supposed to cumulate idle times from
234 all devices */
235 monitor = meta_idle_manager_get_core_monitor (manager);
236 path = g_strdup ("/org/gnome/Mutter/IdleMonitor/Core");
237 create_monitor_skeleton (object_manager, monitor, path);
238 g_free (path);
239
240 g_dbus_object_manager_server_set_connection (object_manager, connection);
241 }
242
243 static void
on_name_acquired(GDBusConnection * connection,const char * name,gpointer user_data)244 on_name_acquired (GDBusConnection *connection,
245 const char *name,
246 gpointer user_data)
247 {
248 meta_verbose ("Acquired name %s", name);
249 }
250
251 static void
on_name_lost(GDBusConnection * connection,const char * name,gpointer user_data)252 on_name_lost (GDBusConnection *connection,
253 const char *name,
254 gpointer user_data)
255 {
256 meta_verbose ("Lost or failed to acquire name %s", name);
257 }
258
259 MetaIdleMonitor *
meta_idle_manager_get_monitor(MetaIdleManager * idle_manager,ClutterInputDevice * device)260 meta_idle_manager_get_monitor (MetaIdleManager *idle_manager,
261 ClutterInputDevice *device)
262 {
263 return g_hash_table_lookup (idle_manager->device_monitors, device);
264 }
265
266 MetaIdleMonitor *
meta_idle_manager_get_core_monitor(MetaIdleManager * idle_manager)267 meta_idle_manager_get_core_monitor (MetaIdleManager *idle_manager)
268 {
269 MetaBackend *backend = meta_get_backend ();
270 ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
271 ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
272
273 return meta_backend_get_idle_monitor (backend,
274 clutter_seat_get_pointer (seat));
275 }
276
277 void
meta_idle_manager_reset_idle_time(MetaIdleManager * idle_manager)278 meta_idle_manager_reset_idle_time (MetaIdleManager *idle_manager)
279 {
280 MetaIdleMonitor *core_monitor;
281
282 core_monitor = meta_idle_manager_get_core_monitor (idle_manager);
283 meta_idle_monitor_reset_idletime (core_monitor);
284 }
285
286 static void
create_device_monitor(MetaIdleManager * idle_manager,ClutterInputDevice * device)287 create_device_monitor (MetaIdleManager *idle_manager,
288 ClutterInputDevice *device)
289 {
290 MetaIdleMonitor *idle_monitor;
291
292 if (g_hash_table_contains (idle_manager->device_monitors, device))
293 return;
294
295 idle_monitor = meta_idle_monitor_new (idle_manager, device);
296 g_hash_table_insert (idle_manager->device_monitors, device, idle_monitor);
297 }
298
299 static void
on_device_added(ClutterSeat * seat,ClutterInputDevice * device,gpointer user_data)300 on_device_added (ClutterSeat *seat,
301 ClutterInputDevice *device,
302 gpointer user_data)
303 {
304 MetaIdleManager *idle_manager = user_data;
305
306 create_device_monitor (idle_manager, device);
307 }
308
309 static void
on_device_removed(ClutterSeat * seat,ClutterInputDevice * device,gpointer user_data)310 on_device_removed (ClutterSeat *seat,
311 ClutterInputDevice *device,
312 gpointer user_data)
313 {
314 MetaIdleManager *idle_manager = user_data;
315
316 g_hash_table_remove (idle_manager->device_monitors, device);
317 }
318
319 static void
create_device_monitors(MetaIdleManager * idle_manager,ClutterSeat * seat)320 create_device_monitors (MetaIdleManager *idle_manager,
321 ClutterSeat *seat)
322 {
323 GList *l, *devices;
324
325 create_device_monitor (idle_manager, clutter_seat_get_pointer (seat));
326 create_device_monitor (idle_manager, clutter_seat_get_keyboard (seat));
327
328 devices = clutter_seat_list_devices (seat);
329 for (l = devices; l; l = l->next)
330 {
331 ClutterInputDevice *device = l->data;
332
333 create_device_monitor (idle_manager, device);
334 }
335
336 g_list_free (devices);
337 }
338
339 MetaIdleManager *
meta_idle_manager_new(MetaBackend * backend)340 meta_idle_manager_new (MetaBackend *backend)
341 {
342 MetaContext *context = meta_backend_get_context (backend);
343 ClutterSeat *seat = meta_backend_get_default_seat (backend);
344 MetaIdleManager *idle_manager;
345
346 idle_manager = g_new0 (MetaIdleManager, 1);
347 idle_manager->backend = backend;
348
349 idle_manager->dbus_name_id =
350 g_bus_own_name (G_BUS_TYPE_SESSION,
351 "org.gnome.Mutter.IdleMonitor",
352 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
353 (meta_context_is_replacing (context) ?
354 G_BUS_NAME_OWNER_FLAGS_REPLACE :
355 G_BUS_NAME_OWNER_FLAGS_NONE),
356 on_bus_acquired,
357 on_name_acquired,
358 on_name_lost,
359 idle_manager,
360 NULL);
361
362 idle_manager->device_monitors =
363 g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref);
364 g_signal_connect (seat, "device-added",
365 G_CALLBACK (on_device_added), idle_manager);
366 g_signal_connect_after (seat, "device-removed",
367 G_CALLBACK (on_device_removed), idle_manager);
368 create_device_monitors (idle_manager, seat);
369
370 return idle_manager;
371 }
372
373 void
meta_idle_manager_free(MetaIdleManager * idle_manager)374 meta_idle_manager_free (MetaIdleManager *idle_manager)
375 {
376 g_clear_pointer (&idle_manager->device_monitors, g_hash_table_destroy);
377 g_bus_unown_name (idle_manager->dbus_name_id);
378 g_free (idle_manager);
379 }
380