1 /* xscreensaver-command, Copyright (c) 1991-1998
2 * by Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
22 #include <sys/types.h>
28 #include <X11/Xproto.h> /* for CARD32 */
30 #include <X11/Xatom.h>
31 #include <X11/Xutil.h> /* for XGetClassHint() */
34 #include <X11/Intrinsic.h> /* only needed to get through xscreensaver.h */
39 static char *progname;
41 static Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
42 static Atom XA_SCREENSAVER_TIME, XA_SELECT;
43 #else /* !STANDALONE */
44 # include "xscreensaver.h"
45 #endif /* !STANDALONE */
49 ERROR! you must not include vroot.h in this file
55 find_screensaver_window (Display *dpy, char **version)
58 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
59 Window root2, parent, *kids;
62 if (version) *version = 0;
64 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
70 if (! (kids && nkids))
72 for (i = 0; i < nkids; i++)
76 unsigned long nitems, bytesafter;
79 if (XGetWindowProperty (dpy, kids[i],
80 XA_SCREENSAVER_VERSION,
81 0, 200, False, XA_STRING,
82 &type, &format, &nitems, &bytesafter,
83 (unsigned char **) &v)
92 fprintf (stderr, "%s: no screensaver is running on display %s\n", progname,
99 send_xscreensaver_command (Display *dpy, Atom command, long argument,
103 Window window = find_screensaver_window (dpy, &v);
104 XWindowAttributes xgwa;
107 *window_ret = window;
112 /* Select for property change events, so that we can read the response. */
113 XGetWindowAttributes (dpy, window, &xgwa);
114 XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
116 if (command == XA_SCREENSAVER_TIME ||
117 command == XA_SCREENSAVER_VERSION)
120 memset (&hint, 0, sizeof(hint));
123 fprintf (stderr, "%s: version property not set on window 0x%x?\n",
124 progname, (unsigned int) window);
128 XGetClassHint(dpy, window, &hint);
131 fprintf (stderr, "%s: class hints not set on window 0x%x?\n",
132 progname, (unsigned int) window);
136 fprintf (stdout, "%s %s", hint.res_class, v);
138 if (command != XA_SCREENSAVER_TIME)
140 fprintf (stdout, "\n");
146 unsigned long nitems, bytesafter;
147 unsigned char *data = 0;
148 Bool active_p = False;
150 if (XGetWindowProperty (dpy, window, XA_VROOT,
151 0, 0, False, XA_WINDOW,
152 &type, &format, &nitems, &bytesafter,
158 if (data) free (data);
161 if (XGetWindowProperty (dpy, window,
163 0, 1, False, XA_INTEGER,
164 &type, &format, &nitems, &bytesafter,
167 && type == XA_INTEGER
170 CARD32 time32 = *((CARD32 *)data);
171 time_t tt = (time_t) time32;
174 fprintf (stdout, ": screen blanked since ");
176 /* suggestions for a better way to phrase this are welcome. */
177 fprintf (stdout, ": screen non-blanked since ");
178 fprintf (stdout, "%s", ctime(&tt));
179 if (data) free (data);
183 if (data) free (data);
184 fprintf (stdout, "\n");
186 fprintf (stderr, "%s: no time on window 0x%x (%s %s).\n",
187 progname, (unsigned int) window,
188 hint.res_class, (v ? v : "???"));
193 /* No need to read a response for these commands. */
199 long arg1 = (command == XA_SELECT ? argument : 0L);
200 event.xany.type = ClientMessage;
201 event.xclient.display = dpy;
202 event.xclient.window = window;
203 event.xclient.message_type = XA_SCREENSAVER;
204 event.xclient.format = 32;
205 memset (&event.xclient.data, 0, sizeof(event.xclient.data));
206 event.xclient.data.l[0] = (long) command;
207 event.xclient.data.l[1] = arg1;
208 if (! XSendEvent (dpy, window, False, 0L, &event))
210 fprintf (stderr, "%s: XSendEvent(dpy, 0x%x ...) failed.\n",
211 progname, (unsigned int) window);
220 static XErrorHandler old_handler = 0;
221 static Bool got_badwindow = False;
223 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
225 /* When we notice a window being created, we spawn a timer that waits
226 30 seconds or so, and then selects events on that window. This error
227 handler is used so that we can cope with the fact that the window
228 may have been destroyed <30 seconds after it was created.
230 if (error->error_code == BadWindow)
232 got_badwindow = True;
237 fprintf (stderr, "%s: ", progname);
238 return (*old_handler) (dpy, error);
244 xscreensaver_command_response (Display *dpy, Window window)
246 int fd = ConnectionNumber (dpy);
256 memset(&tv, 0, sizeof(tv));
258 status = select (fd+1, &fds, 0, &fds, &tv);
263 sprintf (buf, "%s: waiting for reply", progname);
267 else if (status == 0)
269 fprintf (stderr, "%s: no response to command.\n", progname);
275 XNextEvent (dpy, &event);
276 switch (event.xany.type) {
278 if (event.xproperty.state == PropertyNewValue &&
279 event.xproperty.atom == XA_SCREENSAVER_RESPONSE)
284 unsigned long nitems, bytesafter;
287 old_handler = XSetErrorHandler (BadWindow_ehandler);
290 st2 = XGetWindowProperty (dpy, window,
291 XA_SCREENSAVER_RESPONSE,
294 &type, &format, &nitems, &bytesafter,
295 (unsigned char **) &msg);
300 "%s: xscreensaver window has been deleted.\n",
305 if (st2 == Success && type != None)
307 if (type != XA_STRING || format != 8)
310 "%s: unrecognized response property.\n",
312 if (msg) XFree (msg);
315 else if (!msg || (msg[0] != '+' && msg[0] != '-'))
318 "%s: unrecognized response message.\n",
320 if (msg) XFree (msg);
325 int ret = (msg[0] == '+' ? 0 : -1);
326 fprintf ((ret < 0 ? stderr : stdout),
338 fprintf (stderr, "%s: got unexpected response event %d.\n",
339 progname, event.xany.type);
349 xscreensaver_command (Display *dpy, Atom command, long argument)
352 int status = send_xscreensaver_command (dpy, command, argument, &w);
354 status = xscreensaver_command_response (dpy, w);
362 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
363 static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_PREFS, XA_LOCK;
365 static char *progname;
366 static char *screensaver_version;
367 static char *usage = "\n\
368 usage: %s -<option>\n\
370 This program provides external control of a running xscreensaver process.\n\
371 Version %s, copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>.\n\
373 The xscreensaver program is a daemon that runs in the background.\n\
374 You control a running xscreensaver process by sending it messages\n\
375 with this program, xscreensaver-command. See the man pages for\n\
376 details. These are the arguments understood by xscreensaver-command:\n\
378 -demo Ask the xscreensaver process to enter interactive demo mode.\n\
380 -prefs Ask the xscreensaver process to bring up the preferences\n\
383 -activate Turn on the screensaver (blank the screen), as if the user\n\
384 had been idle for long enough.\n\
386 -deactivate Turns off the screensaver (un-blank the screen), as if user\n\
387 activity had been detected.\n\
389 -cycle If the screensaver is active (the screen is blanked), then\n\
390 stop the current graphics demo and run a new one (chosen\n\
393 -next Like either -activate or -cycle, depending on which is more\n\
394 appropriate, except that the graphics hack that will be run\n\
395 is the next one in the list, instead of a randomly-chosen\n\
396 one. In other words, repeatedly executing -next will cause\n\
397 the xscreensaver process to invoke each graphics demo\n\
398 sequentially. (Though using the -demo option is probably\n\
399 an easier way to accomplish that.)\n\
401 -prev Like -next, but goes in the other direction.\n\
403 -select <N> Like -activate, but runs the Nth element in the list of\n\
404 hacks. By knowing what is in the `programs' list, and in\n\
405 what order, you can use this to activate the screensaver\n\
406 with a particular graphics demo. (The first element in the\n\
407 list is numbered 1, not 0.)\n\
409 -exit Causes the xscreensaver process to exit gracefully. This is\n\
410 roughly the same as killing the process with `kill', but it\n\
411 is easier, since you don't need to first figure out the pid.\n\
412 (Note that one must *never* kill xscreensaver with -9!)\n\
414 -restart Causes the screensaver process to exit and then restart with\n\
415 the same command line arguments as last time. Do this after\n\
416 you've changed your X resource settings, to cause\n\
417 xscreensaver to notice the changes.\n\
419 -lock Tells the running xscreensaver process to lock the screen\n\
420 immediately. This is like -activate, but forces locking as\n\
421 well, even if locking is not the default.\n\
423 -version Prints the version of xscreensaver that is currently running\n\
424 on the display -- that is, the actual version number of the\n\
425 running xscreensaver background process, rather than the\n\
426 version number of xscreensaver-command.\n\
428 -time Prints the time at which the screensaver last activated or\n\
429 deactivated (roughly, how long the user has been idle or\n\
430 non-idle -- but not quite, since it only tells you when the\n\
431 screen became blanked or un-blanked.)\n\
433 See the man page for more details.\n\
434 For updates, check http://www.jwz.org/xscreensaver/\n\
438 { fprintf (stderr, usage, progname, screensaver_version); exit (1); }
441 main (int argc, char **argv)
450 screensaver_version = (char *) malloc (5);
451 memcpy (screensaver_version, screensaver_id + 17, 4);
452 screensaver_version [4] = 0;
454 for (i = 1; i < argc; i++)
456 const char *s = argv [i];
458 if (s[0] == '-' && s[1] == '-') s++;
461 if (!strncmp (s, "-display", L)) dpyname = argv [++i];
462 else if (cmd) USAGE ()
463 else if (!strncmp (s, "-activate", L)) cmd = &XA_ACTIVATE;
464 else if (!strncmp (s, "-deactivate", L)) cmd = &XA_DEACTIVATE;
465 else if (!strncmp (s, "-cycle", L)) cmd = &XA_CYCLE;
466 else if (!strncmp (s, "-next", L)) cmd = &XA_NEXT;
467 else if (!strncmp (s, "-prev", L)) cmd = &XA_PREV;
468 else if (!strncmp (s, "-select", L)) cmd = &XA_SELECT;
469 else if (!strncmp (s, "-exit", L)) cmd = &XA_EXIT;
470 else if (!strncmp (s, "-restart", L)) cmd = &XA_RESTART;
471 else if (!strncmp (s, "-demo", L)) cmd = &XA_DEMO;
472 else if (!strncmp (s, "-preferences",L)) cmd = &XA_PREFS;
473 else if (!strncmp (s, "-prefs",L)) cmd = &XA_PREFS;
474 else if (!strncmp (s, "-lock", L)) cmd = &XA_LOCK;
475 else if (!strncmp (s, "-version", L)) cmd = &XA_SCREENSAVER_VERSION;
476 else if (!strncmp (s, "-time", L)) cmd = &XA_SCREENSAVER_TIME;
479 if (cmd == &XA_SELECT)
484 (1 != sscanf(argv[i], " %ld %c", &arg, &junk)))
491 if (!dpyname) dpyname = (char *) getenv ("DISPLAY");
492 dpy = XOpenDisplay (dpyname);
495 fprintf (stderr, "%s: can't open display %s\n", progname,
496 (dpyname ? dpyname : "(null)"));
500 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
501 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
502 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
503 XA_SCREENSAVER_TIME = XInternAtom (dpy, "_SCREENSAVER_TIME", False);
504 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
505 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
506 XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
507 XA_RESTART = XInternAtom (dpy, "RESTART", False);
508 XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
509 XA_NEXT = XInternAtom (dpy, "NEXT", False);
510 XA_PREV = XInternAtom (dpy, "PREV", False);
511 XA_SELECT = XInternAtom (dpy, "SELECT", False);
512 XA_EXIT = XInternAtom (dpy, "EXIT", False);
513 XA_DEMO = XInternAtom (dpy, "DEMO", False);
514 XA_PREFS = XInternAtom (dpy, "PREFS", False);
515 XA_LOCK = XInternAtom (dpy, "LOCK", False);
519 if (*cmd == XA_ACTIVATE || *cmd == XA_LOCK ||
520 *cmd == XA_NEXT || *cmd == XA_PREV || *cmd == XA_SELECT)
521 /* People never guess that KeyRelease deactivates the screen saver too,
522 so if we're issuing an activation command, wait a second. */
525 i = xscreensaver_command (dpy, *cmd, arg);
530 #endif /* STANDALONE */