1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
4  * Copyright (C) 2012-2021 MATE Developers
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, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  *
21  * Authors: William Jon McCann <mccann@jhu.edu>
22  *
23  */
24 
25 #include "config.h"
26 
27 #include <stdlib.h>
28 #include <locale.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 
32 #define DBUS_API_SUBJECT_TO_CHANGE
33 #include <dbus/dbus.h>
34 #include <dbus/dbus-glib.h>
35 #include <dbus/dbus-glib-lowlevel.h>
36 
37 #define GS_SERVICE   "org.mate.ScreenSaver"
38 #define GS_PATH      "/org/mate/ScreenSaver"
39 #define GS_INTERFACE "org.mate.ScreenSaver"
40 
41 static gboolean do_quit       = FALSE;
42 static gboolean do_lock       = FALSE;
43 static gboolean do_unlock     = FALSE;
44 static gboolean do_cycle      = FALSE;
45 static gboolean do_activate   = FALSE;
46 static gboolean do_deactivate = FALSE;
47 static gboolean do_version    = FALSE;
48 static gboolean do_poke       = FALSE;
49 static gboolean do_inhibit    = FALSE;
50 
51 static gboolean do_query      = FALSE;
52 static gboolean do_time       = FALSE;
53 
54 static char    *inhibit_reason      = NULL;
55 static char    *inhibit_application = NULL;
56 
57 static GOptionEntry entries [] =
58 {
59 	{
60 		"exit", 0, 0, G_OPTION_ARG_NONE, &do_quit,
61 		N_("Causes the screensaver to exit gracefully"), NULL
62 	},
63 	{
64 		"query", 'q', 0, G_OPTION_ARG_NONE, &do_query,
65 		N_("Query the state of the screensaver"), NULL
66 	},
67 	{
68 		"time", 't', 0, G_OPTION_ARG_NONE, &do_time,
69 		N_("Query the length of time the screensaver has been active"), NULL
70 	},
71 	{
72 		"lock", 'l', 0, G_OPTION_ARG_NONE, &do_lock,
73 		N_("Tells the running screensaver process to lock the screen immediately"), NULL
74 	},
75 	{
76 		"unlock", 'u', 0, G_OPTION_ARG_NONE, &do_unlock,
77 		N_("Tells the running screensaver process to unlock the screen immediately"), NULL
78 	},
79 	{
80 		"cycle", 'c', 0, G_OPTION_ARG_NONE, &do_cycle,
81 		N_("If the screensaver is active then switch to another graphics demo"), NULL
82 	},
83 	{
84 		"activate", 'a', 0, G_OPTION_ARG_NONE, &do_activate,
85 		N_("Turn the screensaver on (blank the screen)"), NULL
86 	},
87 	{
88 		"deactivate", 'd', 0, G_OPTION_ARG_NONE, &do_deactivate,
89 		N_("If the screensaver is active then deactivate it (un-blank the screen)"), NULL
90 	},
91 	{
92 		"poke", 'p', 0, G_OPTION_ARG_NONE, &do_poke,
93 		N_("Poke the running screensaver to simulate user activity"), NULL
94 	},
95 	{
96 		"inhibit", 'i', 0, G_OPTION_ARG_NONE, &do_inhibit,
97 		N_("Inhibit the screensaver from activating.  Command blocks while inhibit is active."), NULL
98 	},
99 	{
100 		"application-name", 'n', 0, G_OPTION_ARG_STRING, &inhibit_application,
101 		N_("The calling application that is inhibiting the screensaver"), NULL
102 	},
103 	{
104 		"reason", 'r', 0, G_OPTION_ARG_STRING, &inhibit_reason,
105 		N_("The reason for inhibiting the screensaver"), NULL
106 	},
107 	{
108 		"version", 'V', 0, G_OPTION_ARG_NONE, &do_version,
109 		N_("Version of this application"), NULL
110 	},
111 	{ NULL }
112 };
113 
114 static GMainLoop *loop = NULL;
115 
116 static gboolean
screensaver_is_running(DBusConnection * connection)117 screensaver_is_running (DBusConnection *connection)
118 {
119 	DBusError               error;
120 	gboolean                exists;
121 
122 	g_return_val_if_fail (connection != NULL, FALSE);
123 
124 	dbus_error_init (&error);
125 	exists = dbus_bus_name_has_owner (connection, GS_SERVICE, &error);
126 	if (dbus_error_is_set (&error))
127 		dbus_error_free (&error);
128 
129 	return exists;
130 }
131 
132 static DBusMessage *
screensaver_send_message_inhibit(DBusConnection * connection,const char * application,const char * reason)133 screensaver_send_message_inhibit (DBusConnection *connection,
134                                   const char     *application,
135                                   const char     *reason)
136 {
137 	DBusMessage    *message;
138 	DBusMessage    *reply;
139 	DBusError       error;
140 	DBusMessageIter iter;
141 
142 	g_return_val_if_fail (connection != NULL, NULL);
143 
144 	dbus_error_init (&error);
145 
146 	message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, "Inhibit");
147 	if (message == NULL)
148 	{
149 		g_warning ("Couldn't allocate the dbus message");
150 		return NULL;
151 	}
152 
153 	dbus_message_iter_init_append (message, &iter);
154 	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &application);
155 	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &reason);
156 
157 	reply = dbus_connection_send_with_reply_and_block (connection,
158 	        message,
159 	        -1, &error);
160 	if (dbus_error_is_set (&error))
161 	{
162 		g_warning ("%s raised:\n %s\n\n", error.name, error.message);
163 		reply = NULL;
164 	}
165 
166 	dbus_connection_flush (connection);
167 
168 	dbus_message_unref (message);
169 	dbus_error_free (&error);
170 
171 	return reply;
172 }
173 
174 static DBusMessage *
screensaver_send_message_bool(DBusConnection * connection,const char * name,gboolean value)175 screensaver_send_message_bool (DBusConnection *connection,
176                                const char     *name,
177                                gboolean        value)
178 {
179 	DBusMessage    *message;
180 	DBusMessage    *reply;
181 	DBusError       error;
182 	DBusMessageIter iter;
183 
184 	g_return_val_if_fail (connection != NULL, NULL);
185 	g_return_val_if_fail (name != NULL, NULL);
186 
187 	dbus_error_init (&error);
188 
189 	message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
190 	if (message == NULL)
191 	{
192 		g_warning ("Couldn't allocate the dbus message");
193 		return NULL;
194 	}
195 
196 	dbus_message_iter_init_append (message, &iter);
197 	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);
198 
199 	reply = dbus_connection_send_with_reply_and_block (connection,
200 	        message,
201 	        -1, &error);
202 	if (dbus_error_is_set (&error))
203 	{
204 		g_warning ("%s raised:\n %s\n\n", error.name, error.message);
205 		reply = NULL;
206 	}
207 
208 	dbus_connection_flush (connection);
209 
210 	dbus_message_unref (message);
211 	dbus_error_free (&error);
212 
213 	return reply;
214 }
215 
216 static DBusMessage *
screensaver_send_message_void(DBusConnection * connection,const char * name,gboolean expect_reply)217 screensaver_send_message_void (DBusConnection *connection,
218                                const char     *name,
219                                gboolean        expect_reply)
220 {
221 	DBusMessage *message;
222 	DBusMessage *reply;
223 	DBusError    error;
224 
225 	g_return_val_if_fail (connection != NULL, NULL);
226 	g_return_val_if_fail (name != NULL, NULL);
227 
228 	dbus_error_init (&error);
229 
230 	message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
231 	if (message == NULL)
232 	{
233 		g_warning ("Couldn't allocate the dbus message");
234 		return NULL;
235 	}
236 
237 	if (! expect_reply)
238 	{
239 		if (!dbus_connection_send (connection, message, NULL))
240 			g_warning ("could not send message");
241 		reply = NULL;
242 	}
243 	else
244 	{
245 		reply = dbus_connection_send_with_reply_and_block (connection,
246 		        message,
247 		        -1, &error);
248 		if (dbus_error_is_set (&error))
249 		{
250 			g_warning ("%s raised:\n %s\n\n", error.name, error.message);
251 			reply = NULL;
252 		}
253 	}
254 	dbus_connection_flush (connection);
255 
256 	dbus_message_unref (message);
257 	dbus_error_free (&error);
258 
259 	return reply;
260 }
261 
262 static char **
get_string_from_iter(DBusMessageIter * iter,int * num_elements)263 get_string_from_iter (DBusMessageIter *iter,
264                       int             *num_elements)
265 {
266 	int    count;
267 	char **buffer;
268 
269 	if (num_elements != NULL)
270 	{
271 		*num_elements = 0;
272 	}
273 
274 	count = 0;
275 	buffer = (char **)malloc (sizeof (char *) * 8);
276 
277 	if (buffer == NULL)
278 	{
279 		goto oom;
280 	}
281 
282 	buffer[0] = NULL;
283 	while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRING)
284 	{
285 		const char *value;
286 		char       *str;
287 
288 		if ((count % 8) == 0 && count != 0)
289 		{
290 			buffer = realloc (buffer, sizeof (char *) * (count + 8));
291 			if (buffer == NULL)
292 			{
293 				goto oom;
294 			}
295 		}
296 
297 		dbus_message_iter_get_basic (iter, &value);
298 		str = strdup (value);
299 		if (str == NULL)
300 		{
301 			goto oom;
302 		}
303 
304 		buffer[count] = str;
305 
306 		dbus_message_iter_next (iter);
307 		count++;
308 	}
309 
310 	if ((count % 8) == 0)
311 	{
312 		buffer = realloc (buffer, sizeof (char *) * (count + 1));
313 		if (buffer == NULL)
314 		{
315 			goto oom;
316 		}
317 	}
318 
319 	buffer[count] = NULL;
320 	if (num_elements != NULL)
321 	{
322 		*num_elements = count;
323 	}
324 	return buffer;
325 
326 oom:
327 	g_debug ("%s %d : error allocating memory\n", __FILE__, __LINE__);
328 	return NULL;
329 
330 }
331 
332 static gboolean
do_command(gpointer data)333 do_command (gpointer data)
334 {
335 	DBusConnection *connection = data;
336 	DBusMessage *reply;
337 
338 	if (do_quit)
339 	{
340 		reply = screensaver_send_message_void (connection, "Quit", FALSE);
341 		goto done;
342 	}
343 
344 	if (do_query)
345 	{
346 		DBusMessageIter iter;
347 		DBusMessageIter array;
348 		dbus_bool_t     v;
349 
350 		reply = screensaver_send_message_void (connection, "GetActive", TRUE);
351 		if (! reply)
352 		{
353 			g_message ("Did not receive a reply from the screensaver.");
354 			goto done;
355 		}
356 
357 		dbus_message_iter_init (reply, &iter);
358 		dbus_message_iter_get_basic (&iter, &v);
359 		g_print (_("The screensaver is %s\n"), v ? _("active") : _("inactive"));
360 
361 		dbus_message_unref (reply);
362 
363 		reply = screensaver_send_message_void (connection, "GetInhibitors", TRUE);
364 		if (! reply)
365 		{
366 			g_message ("Did not receive a reply from screensaver.");
367 			goto done;
368 		}
369 
370 		dbus_message_iter_init (reply, &iter);
371 		dbus_message_iter_recurse (&iter, &array);
372 
373 		if (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID)
374 		{
375 			g_print (_("The screensaver is not inhibited\n"));
376 		}
377 		else
378 		{
379 			char **inhibitors;
380 			int    i;
381 			int    num;
382 
383 			g_print (_("The screensaver is being inhibited by:\n"));
384 			inhibitors = get_string_from_iter (&array, &num);
385 			for (i = 0; i < num; i++)
386 			{
387 				g_print ("\t%s\n", inhibitors[i]);
388 			}
389 			g_strfreev (inhibitors);
390 		}
391 
392 		dbus_message_unref (reply);
393 	}
394 
395 	if (do_time)
396 	{
397 		DBusMessageIter iter;
398 		dbus_bool_t     v;
399 		dbus_int32_t    t;
400 
401 		reply = screensaver_send_message_void (connection, "GetActive", TRUE);
402 		if (! reply)
403 		{
404 			g_message ("Did not receive a reply from the screensaver.");
405 			goto done;
406 		}
407 
408 		dbus_message_iter_init (reply, &iter);
409 		dbus_message_iter_get_basic (&iter, &v);
410 		dbus_message_unref (reply);
411 
412 		if (v)
413 		{
414 
415 			reply = screensaver_send_message_void (connection, "GetActiveTime", TRUE);
416 			dbus_message_iter_init (reply, &iter);
417 			dbus_message_iter_get_basic (&iter, &t);
418 			g_print (_("The screensaver has been active for %d seconds.\n"), t);
419 
420 			dbus_message_unref (reply);
421 		}
422 		else
423 		{
424 			g_print (_("The screensaver is not currently active.\n"));
425 		}
426 	}
427 
428 	if (do_lock)
429 	{
430 		reply = screensaver_send_message_void (connection, "Lock", FALSE);
431 	}
432 
433 	if (do_unlock)
434 	{
435 		reply = screensaver_send_message_void (connection, "Unlock", FALSE);
436 	}
437 
438 	if (do_cycle)
439 	{
440 		reply = screensaver_send_message_void (connection, "Cycle", FALSE);
441 	}
442 
443 	if (do_poke)
444 	{
445 		reply = screensaver_send_message_void (connection, "SimulateUserActivity", FALSE);
446 	}
447 
448 	if (do_activate)
449 	{
450 		reply = screensaver_send_message_bool (connection, "SetActive", TRUE);
451 		if (! reply)
452 		{
453 			g_message ("Did not receive a reply from the screensaver.");
454 			goto done;
455 		}
456 		dbus_message_unref (reply);
457 	}
458 
459 	if (do_deactivate)
460 	{
461 		reply = screensaver_send_message_bool (connection, "SetActive", FALSE);
462 		if (! reply)
463 		{
464 			g_message ("Did not receive a reply from the screensaver.");
465 			goto done;
466 		}
467 		dbus_message_unref (reply);
468 	}
469 
470 	if (do_inhibit)
471 	{
472 		reply = screensaver_send_message_inhibit (connection,
473 		        inhibit_application ? inhibit_application : "Unknown",
474 		        inhibit_reason ? inhibit_reason : "Unknown");
475 		if (! reply)
476 		{
477 			g_message ("Did not receive a reply from the screensaver.");
478 			goto done;
479 		}
480 		dbus_message_unref (reply);
481 
482 		return FALSE;
483 	}
484 
485 done:
486 	g_main_loop_quit (loop);
487 
488 	return FALSE;
489 }
490 
491 int
main(int argc,char ** argv)492 main (int    argc,
493       char **argv)
494 {
495 	DBusConnection *connection;
496 	DBusError       dbus_error;
497 	GOptionContext *context;
498 	gboolean        retval;
499 	GError         *error = NULL;
500 
501 #ifdef ENABLE_NLS
502 	bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
503 # ifdef HAVE_BIND_TEXTDOMAIN_CODESET
504 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
505 # endif
506 	textdomain (GETTEXT_PACKAGE);
507 #endif
508 
509 	g_set_prgname (argv[0]);
510 
511 	if (setlocale (LC_ALL, "") == NULL)
512 		g_warning ("Locale not understood by C library, internationalization will not work\n");
513 
514 	context = g_option_context_new (NULL);
515 	g_option_context_add_main_entries (context, entries, NULL);
516 	retval = g_option_context_parse (context, &argc, &argv, &error);
517 
518 	g_option_context_free (context);
519 
520 	if (! retval)
521 	{
522 		g_warning ("%s", error->message);
523 		g_error_free (error);
524 		exit (1);
525 	}
526 
527 	if (do_version)
528 	{
529 		g_print ("%s %s\n", argv [0], VERSION);
530 		exit (1);
531 	}
532 
533 	dbus_error_init (&dbus_error);
534 	connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
535 	if (! connection)
536 	{
537 		g_message ("Failed to connect to the D-BUS daemon: %s", dbus_error.message);
538 		dbus_error_free (&dbus_error);
539 		exit (1);
540 	}
541 
542 	dbus_connection_setup_with_g_main (connection, NULL);
543 
544 	if (! screensaver_is_running (connection))
545 	{
546 		g_message ("Screensaver is not running!");
547 		exit (1);
548 	}
549 
550 	g_idle_add (do_command, connection);
551 
552 	loop = g_main_loop_new (NULL, FALSE);
553 	g_main_loop_run (loop);
554 
555 	return 0;
556 }
557