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
20 #include <sys/types.h>
22 #ifdef HAVE_SYS_SELECT_H
23 # include <sys/select.h>
24 #endif /* HAVE_SYS_SELECT_H */
30 #include <X11/Xproto.h> /* for CARD32 */
32 #include <X11/Xatom.h>
33 #include <X11/Xutil.h> /* for XGetClassHint() */
39 ERROR! you must not include vroot.h in this file
42 extern char *progname;
43 extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE;
44 extern Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_EXIT;
45 extern Atom XA_VROOT, XA_SELECT, XA_DEMO, XA_BLANK, XA_LOCK;
48 static XErrorHandler old_handler = 0;
49 static Bool got_badwindow = False;
51 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
53 if (error->error_code == BadWindow)
60 fprintf (stderr, "%s: ", progname);
61 if (!old_handler) abort();
62 return (*old_handler) (dpy, error);
69 find_screensaver_window (Display *dpy, char **version)
72 Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
73 Window root2, parent, *kids;
76 if (version) *version = 0;
78 if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
84 if (! (kids && nkids))
86 for (i = 0; i < nkids; i++)
90 unsigned long nitems, bytesafter;
94 /* We're walking the list of root-level windows and trying to find
95 the one that has a particular property on it. We need to trap
96 BadWindows errors while doing this, because it's possible that
97 some random window might get deleted in the meantime. (That
98 window won't have been the one we're looking for.)
101 if (old_handler) abort();
102 got_badwindow = False;
103 old_handler = XSetErrorHandler (BadWindow_ehandler);
104 status = XGetWindowProperty (dpy, kids[i],
105 XA_SCREENSAVER_VERSION,
106 0, 200, False, XA_STRING,
107 &type, &format, &nitems, &bytesafter,
108 (unsigned char **) &v);
110 XSetErrorHandler (old_handler);
116 got_badwindow = False;
119 if (status == Success && type != None)
131 send_xscreensaver_command (Display *dpy, Atom command, long arg,
135 Window window = find_screensaver_window (dpy, &v);
136 XWindowAttributes xgwa;
139 *window_ret = window;
143 if (command == XA_EXIT)
146 /* Don't print this if xscreensaver is already dead. */
147 fprintf (stderr, "%s: no screensaver is running on display %s\n",
148 progname, DisplayString (dpy));
152 /* Select for property change events, so that we can read the response. */
153 XGetWindowAttributes (dpy, window, &xgwa);
154 XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
156 if (command == XA_SCREENSAVER_STATUS ||
157 command == XA_SCREENSAVER_VERSION)
160 memset (&hint, 0, sizeof(hint));
163 fprintf (stderr, "%s: version property not set on window 0x%x?\n",
164 progname, (unsigned int) window);
168 XGetClassHint(dpy, window, &hint);
171 fprintf (stderr, "%s: class hints not set on window 0x%x?\n",
172 progname, (unsigned int) window);
176 fprintf (stdout, "%s %s", hint.res_class, v);
178 if (command != XA_SCREENSAVER_STATUS)
180 fprintf (stdout, "\n");
186 unsigned long nitems, bytesafter;
189 if (XGetWindowProperty (dpy,
191 XA_SCREENSAVER_STATUS,
192 0, 999, False, XA_INTEGER,
193 &type, &format, &nitems, &bytesafter,
194 (unsigned char **) &data)
203 if (type != XA_INTEGER || nitems < 3)
206 if (data) free (data);
207 fprintf (stdout, "\n");
209 fprintf (stderr, "%s: bad status format on root window.\n",
214 blanked = (Atom) data[0];
215 tt = (time_t) data[1];
217 if (tt <= (time_t) 666000000L) /* early 1991 */
220 if (blanked == XA_BLANK)
221 fputs (": screen blanked since ", stdout);
222 else if (blanked == XA_LOCK)
223 fputs (": screen locked since ", stdout);
224 else if (blanked == 0)
225 /* suggestions for a better way to phrase this are welcome. */
226 fputs (": screen non-blanked since ", stdout);
228 /* `blanked' has an unknown value - fail. */
232 if (s[strlen(s)-1] == '\n')
237 int nhacks = nitems - 2;
240 for (i = 0; i < nhacks; i++)
247 if (any && nhacks == 1)
248 fprintf (stdout, " (hack #%d)\n", data[2]);
251 fprintf (stdout, " (hacks: ");
252 for (i = 0; i < nhacks; i++)
254 fprintf (stdout, "#%d", data[2 + i]);
256 fputs (", ", stdout);
258 fputs (")\n", stdout);
261 fputs ("\n", stdout);
264 if (data) free (data);
268 if (data) free (data);
269 fprintf (stdout, "\n");
271 fprintf (stderr, "%s: no saver status on root window.\n",
277 /* No need to read a response for these commands. */
288 else if (arg == 0 && command == XA_SELECT)
290 else if (arg != 0 && command == XA_DEMO)
292 arg1 = 300; /* version number of the XA_DEMO protocol, */
293 arg2 = arg; /* since it didn't use to take an argument. */
296 event.xany.type = ClientMessage;
297 event.xclient.display = dpy;
298 event.xclient.window = window;
299 event.xclient.message_type = XA_SCREENSAVER;
300 event.xclient.format = 32;
301 memset (&event.xclient.data, 0, sizeof(event.xclient.data));
302 event.xclient.data.l[0] = (long) command;
303 event.xclient.data.l[1] = arg1;
304 event.xclient.data.l[2] = arg2;
305 if (! XSendEvent (dpy, window, False, 0L, &event))
307 fprintf (stderr, "%s: XSendEvent(dpy, 0x%x ...) failed.\n",
308 progname, (unsigned int) window);
318 xscreensaver_command_response (Display *dpy, Window window,
319 Bool verbose_p, Bool exiting_p)
321 int fd = ConnectionNumber (dpy);
331 memset(&tv, 0, sizeof(tv));
333 status = select (fd+1, &fds, 0, &fds, &tv);
338 sprintf (buf, "%s: waiting for reply", progname);
342 else if (status == 0)
344 fprintf (stderr, "%s: no response to command.\n", progname);
350 XNextEvent (dpy, &event);
351 if (event.xany.type == PropertyNotify &&
352 event.xproperty.state == PropertyNewValue &&
353 event.xproperty.atom == XA_SCREENSAVER_RESPONSE)
358 unsigned long nitems, bytesafter;
362 if (old_handler) abort();
363 old_handler = XSetErrorHandler (BadWindow_ehandler);
364 st2 = XGetWindowProperty (dpy, window,
365 XA_SCREENSAVER_RESPONSE,
368 &type, &format, &nitems, &bytesafter,
369 (unsigned char **) &msg);
371 XSetErrorHandler (old_handler);
380 "%s: xscreensaver window unexpectedly deleted.\n",
385 if (st2 == Success && type != None)
387 if (type != XA_STRING || format != 8)
390 "%s: unrecognized response property.\n",
392 if (msg) XFree (msg);
395 else if (!msg || (msg[0] != '+' && msg[0] != '-'))
398 "%s: unrecognized response message.\n",
400 if (msg) XFree (msg);
405 int ret = (msg[0] == '+' ? 0 : -1);
406 if (verbose_p || ret != 0)
407 fprintf ((ret < 0 ? stderr : stdout),
422 xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p)
425 int status = send_xscreensaver_command (dpy, command, arg, &w);
427 status = xscreensaver_command_response (dpy, w, verbose_p,
428 (command == XA_EXIT));
432 return (status < 0 ? status : 0);
437 server_xscreensaver_version (Display *dpy,
442 Window window = find_screensaver_window (dpy, 0);
446 unsigned long nitems, bytesafter;
461 XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 1,
462 False, XA_STRING, &type, &format, &nitems,
463 &bytesafter, (unsigned char **) &v);
466 *version_ret = strdup (v);
471 if (user_ret || host_ret)
474 const char *user = 0;
475 const char *host = 0;
477 XGetWindowProperty (dpy, window, XA_SCREENSAVER_ID, 0, 512,
478 False, XA_STRING, &type, &format, &nitems,
479 &bytesafter, (unsigned char **) &id);
482 const char *old_tag = " on host ";
483 const char *s = strstr (id, old_tag);
486 /* found ID of the form "1234 on host xyz". */
488 host = s + strlen (old_tag);
492 char *o = 0, *p = 0, *c = 0;
493 o = strchr (id, '(');
494 if (o) p = strchr (o, '@');
495 if (p) c = strchr (p, ')');
498 /* found ID of the form "1234 (user@host)". */
508 if (user && *user && *user != '?')
509 *user_ret = strdup (user);
513 if (host && *host && *host != '?')
514 *host_ret = strdup (host);