1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2
3 Copyright (C) 2004-2006 Bastien Nocera <hadess@hadess.net>
4 Copyright © 2010 Christian Persch
5 Copyright © 2010 Carlos Garcia Campos
6
7 The Mate Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The Mate Library 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 GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the Mate Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301 USA.
21
22 Authors: Bastien Nocera <hadess@hadess.net>
23 Christian Persch
24 Carlos Garcia Campos
25 */
26
27 #include "config.h"
28
29 #include <gtk/gtk.h>
30 #include <gdk/gdk.h>
31
32 #ifdef GDK_WINDOWING_X11
33 #include <gdk/gdkx.h>
34 #include <X11/keysym.h>
35
36 #ifdef HAVE_XTEST
37 #include <X11/extensions/XTest.h>
38 #endif /* HAVE_XTEST */
39 #endif /* GDK_WINDOWING_X11 */
40
41 #include "totem-scrsaver.h"
42
43 #define GS_SERVICE "org.gnome.SessionManager"
44 #define GS_PATH "/org/gnome/SessionManager"
45 #define GS_INTERFACE "org.gnome.SessionManager"
46
47 #define GSM_INHIBITOR_FLAG_IDLE 1 << 3
48 #define XSCREENSAVER_MIN_TIMEOUT 60
49
50 enum {
51 PROP_0,
52 PROP_REASON
53 };
54
55 static void totem_scrsaver_finalize (GObject *object);
56
57 struct TotemScrsaverPrivate {
58 /* Whether the screensaver is disabled */
59 gboolean disabled;
60 /* The reason for the inhibition */
61 char *reason;
62
63 GDBusProxy *gs_proxy;
64 gboolean have_screensaver_dbus;
65 guint32 cookie;
66 gboolean old_dbus_api;
67
68 /* To save the screensaver info */
69 int timeout;
70 int interval;
71 int prefer_blanking;
72 int allow_exposures;
73
74 /* For use with XTest */
75 int keycode1, keycode2;
76 int *keycode;
77 gboolean have_xtest;
78 };
79
G_DEFINE_TYPE_WITH_PRIVATE(TotemScrsaver,totem_scrsaver,G_TYPE_OBJECT)80 G_DEFINE_TYPE_WITH_PRIVATE(TotemScrsaver, totem_scrsaver, G_TYPE_OBJECT)
81
82 static gboolean
83 screensaver_is_running_dbus (TotemScrsaver *scr)
84 {
85 return scr->priv->have_screensaver_dbus;
86 }
87
88 static void
on_inhibit_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)89 on_inhibit_cb (GObject *source_object,
90 GAsyncResult *res,
91 gpointer user_data)
92 {
93 GDBusProxy *proxy = G_DBUS_PROXY (source_object);
94 TotemScrsaver *scr = TOTEM_SCRSAVER (user_data);
95 GVariant *value;
96 GError *error = NULL;
97
98 value = g_dbus_proxy_call_finish (proxy, res, &error);
99 if (!value) {
100 if (!scr->priv->old_dbus_api &&
101 g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
102 g_return_if_fail (scr->priv->reason != NULL);
103 /* try the old API */
104 scr->priv->old_dbus_api = TRUE;
105 g_dbus_proxy_call (proxy,
106 "InhibitActivation",
107 g_variant_new ("(s)",
108 scr->priv->reason),
109 G_DBUS_CALL_FLAGS_NO_AUTO_START,
110 -1,
111 NULL,
112 on_inhibit_cb,
113 scr);
114 } else {
115 g_warning ("Problem inhibiting the screensaver: %s", error->message);
116 }
117 g_error_free (error);
118
119 return;
120 }
121
122 /* save the cookie */
123 if (g_variant_is_of_type (value, G_VARIANT_TYPE ("(u)")))
124 g_variant_get (value, "(u)", &scr->priv->cookie);
125 else
126 scr->priv->cookie = 0;
127 g_variant_unref (value);
128 }
129
130 static void
on_uninhibit_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)131 on_uninhibit_cb (GObject *source_object,
132 GAsyncResult *res,
133 gpointer user_data)
134 {
135 GDBusProxy *proxy = G_DBUS_PROXY (source_object);
136 TotemScrsaver *scr = TOTEM_SCRSAVER (user_data);
137 GVariant *value;
138 GError *error = NULL;
139
140 value = g_dbus_proxy_call_finish (proxy, res, &error);
141 if (!value) {
142 if (!scr->priv->old_dbus_api &&
143 g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) {
144 /* try the old API */
145 scr->priv->old_dbus_api = TRUE;
146 g_dbus_proxy_call (proxy,
147 "AllowActivation",
148 g_variant_new ("()"),
149 G_DBUS_CALL_FLAGS_NO_AUTO_START,
150 -1,
151 NULL,
152 on_uninhibit_cb,
153 scr);
154 } else {
155 g_warning ("Problem uninhibiting the screensaver: %s", error->message);
156 }
157 g_error_free (error);
158
159 return;
160 }
161
162 /* clear the cookie */
163 scr->priv->cookie = 0;
164 g_variant_unref (value);
165 }
166
167 static void
screensaver_inhibit_dbus(TotemScrsaver * scr,gboolean inhibit)168 screensaver_inhibit_dbus (TotemScrsaver *scr,
169 gboolean inhibit)
170 {
171 TotemScrsaverPrivate *priv = scr->priv;
172
173 if (!priv->have_screensaver_dbus)
174 return;
175
176 scr->priv->old_dbus_api = FALSE;
177
178 if (inhibit) {
179 g_return_if_fail (scr->priv->reason != NULL);
180 g_dbus_proxy_call (priv->gs_proxy,
181 "Inhibit",
182 g_variant_new ("(susu)",
183 g_get_application_name (), 0, scr->priv->reason, GSM_INHIBITOR_FLAG_IDLE),
184 G_DBUS_CALL_FLAGS_NO_AUTO_START,
185 -1,
186 NULL,
187 on_inhibit_cb,
188 scr);
189 } else {
190 g_dbus_proxy_call (priv->gs_proxy,
191 "Uninhibit",
192 g_variant_new ("(u)", priv->cookie),
193 G_DBUS_CALL_FLAGS_NO_AUTO_START,
194 -1,
195 NULL,
196 on_uninhibit_cb,
197 scr);
198 }
199 }
200
201 static void
screensaver_enable_dbus(TotemScrsaver * scr)202 screensaver_enable_dbus (TotemScrsaver *scr)
203 {
204 screensaver_inhibit_dbus (scr, FALSE);
205 }
206
207 static void
screensaver_disable_dbus(TotemScrsaver * scr)208 screensaver_disable_dbus (TotemScrsaver *scr)
209 {
210 screensaver_inhibit_dbus (scr, TRUE);
211 }
212
213 static void
screensaver_update_dbus_presence(TotemScrsaver * scr)214 screensaver_update_dbus_presence (TotemScrsaver *scr)
215 {
216 TotemScrsaverPrivate *priv = scr->priv;
217 gchar *name_owner;
218
219 name_owner = g_dbus_proxy_get_name_owner (priv->gs_proxy);
220 if (name_owner) {
221 priv->have_screensaver_dbus = TRUE;
222 g_free (name_owner);
223 } else {
224 priv->have_screensaver_dbus = FALSE;
225 }
226 }
227
228 static void
screensaver_dbus_owner_changed_cb(GObject * object,GParamSpec * pspec,gpointer user_data)229 screensaver_dbus_owner_changed_cb (GObject *object,
230 GParamSpec *pspec,
231 gpointer user_data)
232 {
233 TotemScrsaver *scr = TOTEM_SCRSAVER (user_data);
234
235 screensaver_update_dbus_presence (scr);
236 }
237
238 static void
screensaver_dbus_proxy_new_cb(GObject * source,GAsyncResult * result,gpointer user_data)239 screensaver_dbus_proxy_new_cb (GObject *source,
240 GAsyncResult *result,
241 gpointer user_data)
242 {
243 TotemScrsaver *scr = TOTEM_SCRSAVER (user_data);
244 TotemScrsaverPrivate *priv = scr->priv;
245
246 priv->gs_proxy = g_dbus_proxy_new_for_bus_finish (result, NULL);
247 if (!priv->gs_proxy)
248 return;
249
250 screensaver_update_dbus_presence (scr);
251
252 g_signal_connect (priv->gs_proxy, "notify::g-name-owner",
253 G_CALLBACK (screensaver_dbus_owner_changed_cb),
254 scr);
255 }
256
257 static void
screensaver_init_dbus(TotemScrsaver * scr)258 screensaver_init_dbus (TotemScrsaver *scr)
259 {
260 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
261 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
262 NULL,
263 GS_SERVICE,
264 GS_PATH,
265 GS_INTERFACE,
266 NULL,
267 screensaver_dbus_proxy_new_cb,
268 scr);
269 }
270
271 static void
screensaver_finalize_dbus(TotemScrsaver * scr)272 screensaver_finalize_dbus (TotemScrsaver *scr)
273 {
274 if (scr->priv->gs_proxy) {
275 g_object_unref (scr->priv->gs_proxy);
276 }
277 }
278
279 #ifdef GDK_WINDOWING_X11
280 static void
screensaver_enable_x11(TotemScrsaver * scr)281 screensaver_enable_x11 (TotemScrsaver *scr)
282 {
283 Display *xdisplay;
284
285 #ifdef HAVE_XTEST
286 if (scr->priv->have_xtest != FALSE)
287 {
288 g_source_remove_by_user_data (scr);
289 return;
290 }
291 #endif /* HAVE_XTEST */
292
293 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
294 XLockDisplay (xdisplay);
295 XSetScreenSaver (xdisplay,
296 scr->priv->timeout,
297 scr->priv->interval,
298 scr->priv->prefer_blanking,
299 scr->priv->allow_exposures);
300 XUnlockDisplay (xdisplay);
301 }
302
303 #ifdef HAVE_XTEST
304 static gboolean
fake_event(TotemScrsaver * scr)305 fake_event (TotemScrsaver *scr)
306 {
307 if (scr->priv->disabled)
308 {
309 Display *display;
310
311 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
312 XLockDisplay (xdisplay);
313 XTestFakeKeyEvent (xdisplay, *scr->priv->keycode,
314 True, CurrentTime);
315 XTestFakeKeyEvent (xdisplay, *scr->priv->keycode,
316 False, CurrentTime);
317 XUnlockDisplay (xdisplay);
318 /* Swap the keycode */
319 if (scr->priv->keycode == &scr->priv->keycode1)
320 scr->priv->keycode = &scr->priv->keycode2;
321 else
322 scr->priv->keycode = &scr->priv->keycode1;
323 }
324
325 return TRUE;
326 }
327 #endif /* HAVE_XTEST */
328
329 static void
screensaver_disable_x11(TotemScrsaver * scr)330 screensaver_disable_x11 (TotemScrsaver *scr)
331 {
332 Display *xdisplay;
333
334 xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
335
336 #ifdef HAVE_XTEST
337 if (scr->priv->have_xtest != FALSE)
338 {
339
340 XLockDisplay (xdisplay);
341 XGetScreenSaver(xdisplay, &scr->priv->timeout,
342 &scr->priv->interval,
343 &scr->priv->prefer_blanking,
344 &scr->priv->allow_exposures);
345 XUnlockDisplay (xdisplay);
346
347 if (scr->priv->timeout != 0) {
348 g_timeout_add_seconds (scr->priv->timeout / 2,
349 (GSourceFunc) fake_event, scr);
350 } else {
351 g_timeout_add_seconds (XSCREENSAVER_MIN_TIMEOUT / 2,
352 (GSourceFunc) fake_event, scr);
353 }
354
355 return;
356 }
357 #endif /* HAVE_XTEST */
358
359 XLockDisplay (xdisplay);
360 XGetScreenSaver(xdisplay, &scr->priv->timeout,
361 &scr->priv->interval,
362 &scr->priv->prefer_blanking,
363 &scr->priv->allow_exposures);
364 XSetScreenSaver(xdisplay, 0, 0,
365 DontPreferBlanking, DontAllowExposures);
366 XUnlockDisplay (xdisplay);
367 }
368
369 static void
screensaver_init_x11(TotemScrsaver * scr)370 screensaver_init_x11 (TotemScrsaver *scr)
371 {
372 #ifdef HAVE_XTEST
373 Display *display;
374 int a, b, c, d;
375
376 display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
377 XLockDisplay (xdisplay);
378 scr->priv->have_xtest = (XTestQueryExtension (xdisplay, &a, &b, &c, &d) == True);
379 if (scr->priv->have_xtest != FALSE)
380 {
381 scr->priv->keycode1 = XKeysymToKeycode (xdisplay, XK_Alt_L);
382 if (scr->priv->keycode1 == 0) {
383 g_warning ("scr->priv->keycode1 not existant");
384 }
385 scr->priv->keycode2 = XKeysymToKeycode (xdisplay, XK_Alt_R);
386 if (scr->priv->keycode2 == 0) {
387 scr->priv->keycode2 = XKeysymToKeycode (xdisplay, XK_Alt_L);
388 if (scr->priv->keycode2 == 0) {
389 g_warning ("scr->priv->keycode2 not existant");
390 }
391 }
392 scr->priv->keycode = &scr->priv->keycode1;
393 }
394 XUnlockDisplay (xdisplay);
395 #endif /* HAVE_XTEST */
396 }
397
398 static void
screensaver_finalize_x11(TotemScrsaver * scr)399 screensaver_finalize_x11 (TotemScrsaver *scr)
400 {
401 g_source_remove_by_user_data (scr);
402 }
403 #endif
404
405 static void
totem_scrsaver_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)406 totem_scrsaver_get_property (GObject *object,
407 guint property_id,
408 GValue *value,
409 GParamSpec *pspec)
410 {
411 TotemScrsaver *scr;
412
413 scr = TOTEM_SCRSAVER (object);
414
415 switch (property_id)
416 {
417 case PROP_REASON:
418 g_value_set_string (value, scr->priv->reason);
419 break;
420 default:
421 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
422 }
423 }
424
425 static void
totem_scrsaver_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)426 totem_scrsaver_set_property (GObject *object,
427 guint property_id,
428 const GValue *value,
429 GParamSpec *pspec)
430 {
431 TotemScrsaver *scr;
432
433 scr = TOTEM_SCRSAVER (object);
434
435 switch (property_id)
436 {
437 case PROP_REASON:
438 g_free (scr->priv->reason);
439 scr->priv->reason = g_value_dup_string (value);
440 break;
441 default:
442 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
443 }
444 }
445
446 static void
totem_scrsaver_class_init(TotemScrsaverClass * klass)447 totem_scrsaver_class_init (TotemScrsaverClass *klass)
448 {
449 GObjectClass *object_class = G_OBJECT_CLASS (klass);
450
451 object_class->set_property = totem_scrsaver_set_property;
452 object_class->get_property = totem_scrsaver_get_property;
453 object_class->finalize = totem_scrsaver_finalize;
454
455 g_object_class_install_property (object_class, PROP_REASON,
456 g_param_spec_string ("reason", NULL, NULL,
457 NULL, G_PARAM_READWRITE));
458
459 }
460
461 /**
462 * totem_scrsaver_new:
463 *
464 * Creates a #TotemScrsaver object.
465 * If the MATE screen saver is running, it uses its DBUS interface to
466 * inhibit the screensaver; otherwise it falls back to using the X
467 * screensaver functionality for this.
468 *
469 * Returns: a newly created #TotemScrsaver
470 */
471 TotemScrsaver *
totem_scrsaver_new(void)472 totem_scrsaver_new (void)
473 {
474 return TOTEM_SCRSAVER (g_object_new (TOTEM_TYPE_SCRSAVER, NULL));
475 }
476
477 static void
totem_scrsaver_init(TotemScrsaver * scr)478 totem_scrsaver_init (TotemScrsaver *scr)
479 {
480 scr->priv = totem_scrsaver_get_instance_private (scr);
481
482 screensaver_init_dbus (scr);
483 #ifdef GDK_WINDOWING_X11
484 screensaver_init_x11 (scr);
485 #else
486 #warning Unimplemented
487 #endif
488 }
489
490 void
totem_scrsaver_disable(TotemScrsaver * scr)491 totem_scrsaver_disable (TotemScrsaver *scr)
492 {
493 g_return_if_fail (TOTEM_SCRSAVER (scr));
494
495 if (scr->priv->disabled != FALSE)
496 return;
497
498 scr->priv->disabled = TRUE;
499
500 if (screensaver_is_running_dbus (scr) != FALSE)
501 screensaver_disable_dbus (scr);
502 else
503 #ifdef GDK_WINDOWING_X11
504 screensaver_disable_x11 (scr);
505 #else
506 #warning Unimplemented
507 {}
508 #endif
509 }
510
511 void
totem_scrsaver_enable(TotemScrsaver * scr)512 totem_scrsaver_enable (TotemScrsaver *scr)
513 {
514 g_return_if_fail (TOTEM_SCRSAVER (scr));
515
516 if (scr->priv->disabled == FALSE)
517 return;
518
519 scr->priv->disabled = FALSE;
520
521 if (screensaver_is_running_dbus (scr) != FALSE)
522 screensaver_enable_dbus (scr);
523 else
524 #ifdef GDK_WINDOWING_X11
525 screensaver_enable_x11 (scr);
526 #else
527 #warning Unimplemented
528 {}
529 #endif
530 }
531
532 void
totem_scrsaver_set_state(TotemScrsaver * scr,gboolean enable)533 totem_scrsaver_set_state (TotemScrsaver *scr, gboolean enable)
534 {
535 g_return_if_fail (TOTEM_SCRSAVER (scr));
536
537 if (scr->priv->disabled == !enable)
538 return;
539
540 if (enable == FALSE)
541 totem_scrsaver_disable (scr);
542 else
543 totem_scrsaver_enable (scr);
544 }
545
546 static void
totem_scrsaver_finalize(GObject * object)547 totem_scrsaver_finalize (GObject *object)
548 {
549 TotemScrsaver *scr = TOTEM_SCRSAVER (object);
550
551 g_free (scr->priv->reason);
552
553 screensaver_finalize_dbus (scr);
554 #ifdef GDK_WINDOWING_X11
555 screensaver_finalize_x11 (scr);
556 #else
557 #warning Unimplemented
558 {}
559 #endif
560
561 G_OBJECT_CLASS (totem_scrsaver_parent_class)->finalize (object);
562 }
563