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