#include <stdio.h>
#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#ifdef STANDALONE
static char *progname;
static Atom XA_VROOT;
- static Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_TIME;
+ static Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
+ static Atom XA_SCREENSAVER_TIME, XA_SELECT;
#else /* !STANDALONE */
# include "xscreensaver.h"
#endif /* !STANDALONE */
ERROR! you must not include vroot.h in this file
#endif
+
+
static Window
find_screensaver_window (Display *dpy, char **version)
{
}
fprintf (stderr, "%s: no screensaver is running on display %s\n", progname,
DisplayString (dpy));
- exit (1);
+ return 0;
}
-void
-xscreensaver_command (Display *dpy, Atom command)
+static int
+send_xscreensaver_command (Display *dpy, Atom command, long argument,
+ Window *window_ret)
{
char *v = 0;
Window window = find_screensaver_window (dpy, &v);
+ XWindowAttributes xgwa;
+
+ if (window_ret)
+ *window_ret = window;
+
+ if (!window)
+ return -1;
+
+ /* Select for property change events, so that we can read the response. */
+ XGetWindowAttributes (dpy, window, &xgwa);
+ XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
if (command == XA_SCREENSAVER_TIME ||
command == XA_SCREENSAVER_VERSION)
{
fprintf (stderr, "%s: version property not set on window 0x%x?\n",
progname, (unsigned int) window);
- exit (1);
+ return -1;
}
XGetClassHint(dpy, window, &hint);
{
fprintf (stderr, "%s: class hints not set on window 0x%x?\n",
progname, (unsigned int) window);
- exit (1);
+ return -1;
}
fprintf (stdout, "%s %s", hint.res_class, v);
int format;
unsigned long nitems, bytesafter;
unsigned char *data = 0;
- XWindowAttributes xgwa;
Bool active_p = False;
- xgwa.map_state = IsViewable;
- XGetWindowAttributes (dpy, window, &xgwa);
-
if (XGetWindowProperty (dpy, window, XA_VROOT,
0, 0, False, XA_WINDOW,
&type, &format, &nitems, &bytesafter,
fprintf (stderr, "%s: no time on window 0x%x (%s %s).\n",
progname, (unsigned int) window,
hint.res_class, (v ? v : "???"));
- exit (1);
+ return -1;
}
}
+
+ /* No need to read a response for these commands. */
+ return 1;
}
else
{
XEvent event;
+ long arg1 = (command == XA_SELECT ? argument : 0L);
event.xany.type = ClientMessage;
event.xclient.display = dpy;
event.xclient.window = window;
event.xclient.message_type = XA_SCREENSAVER;
event.xclient.format = 32;
+ memset (&event.xclient.data, 0, sizeof(event.xclient.data));
event.xclient.data.l[0] = (long) command;
+ event.xclient.data.l[1] = arg1;
if (! XSendEvent (dpy, window, False, 0L, &event))
{
fprintf (stderr, "%s: XSendEvent(dpy, 0x%x ...) failed.\n",
progname, (unsigned int) window);
- exit (1);
+ return -1;
}
}
XSync (dpy, 0);
+ return 0;
+}
+
+
+static XErrorHandler old_handler = 0;
+static Bool got_badwindow = False;
+static int
+BadWindow_ehandler (Display *dpy, XErrorEvent *error)
+{
+ /* When we notice a window being created, we spawn a timer that waits
+ 30 seconds or so, and then selects events on that window. This error
+ handler is used so that we can cope with the fact that the window
+ may have been destroyed <30 seconds after it was created.
+ */
+ if (error->error_code == BadWindow)
+ {
+ got_badwindow = True;
+ return 0;
+ }
+ else
+ {
+ fprintf (stderr, "%s: ", progname);
+ return (*old_handler) (dpy, error);
+ }
+}
+
+
+static int
+xscreensaver_command_response (Display *dpy, Window window)
+{
+ int fd = ConnectionNumber (dpy);
+ int timeout = 10;
+ int status;
+ fd_set fds;
+ struct timeval tv;
+
+ while (1)
+ {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = timeout;
+ status = select (fd+1, &fds, 0, &fds, &tv);
+
+ if (status < 0)
+ {
+ char buf[1024];
+ sprintf (buf, "%s: waiting for reply", progname);
+ perror (buf);
+ return status;
+ }
+ else if (status == 0)
+ {
+ fprintf (stderr, "%s: no response to command.\n", progname);
+ return -1;
+ }
+ else
+ {
+ XEvent event;
+ XNextEvent (dpy, &event);
+ switch (event.xany.type) {
+ case PropertyNotify:
+ if (event.xproperty.state == PropertyNewValue &&
+ event.xproperty.atom == XA_SCREENSAVER_RESPONSE)
+ {
+ Status st2;
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ char *msg = 0;
+
+ old_handler = XSetErrorHandler (BadWindow_ehandler);
+ XSync (dpy, False);
+
+ st2 = XGetWindowProperty (dpy, window,
+ XA_SCREENSAVER_RESPONSE,
+ 0, 1024, True,
+ AnyPropertyType,
+ &type, &format, &nitems, &bytesafter,
+ (unsigned char **) &msg);
+
+ if (got_badwindow)
+ {
+ fprintf (stdout,
+ "%s: xscreensaver window has been deleted.\n",
+ progname);
+ return 0;
+ }
+
+ if (st2 == Success && type != None)
+ {
+ if (type != XA_STRING || format != 8)
+ {
+ fprintf (stderr,
+ "%s: unrecognized response property.\n",
+ progname);
+ if (msg) XFree (msg);
+ return -1;
+ }
+ else if (!msg || (msg[0] != '+' && msg[0] != '-'))
+ {
+ fprintf (stderr,
+ "%s: unrecognized response message.\n",
+ progname);
+ if (msg) XFree (msg);
+ return -1;
+ }
+ else
+ {
+ int ret = (msg[0] == '+' ? 0 : -1);
+ fprintf ((ret < 0 ? stderr : stdout),
+ "%s: %s\n",
+ progname,
+ msg+1);
+ XFree (msg);
+ return ret;
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf (stderr, "%s: got unexpected response event %d.\n",
+ progname, event.xany.type);
+ return -1;
+ break;
+ }
+ }
+ }
}
+int
+xscreensaver_command (Display *dpy, Atom command, long argument)
+{
+ Window w = 0;
+ int status = send_xscreensaver_command (dpy, command, argument, &w);
+ if (status == 0)
+ status = xscreensaver_command_response (dpy, w);
+ fflush (stdout);
+ fflush (stderr);
+ return status;
+}
+
\f
#ifdef STANDALONE
static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
an easier way to accomplish that.)\n\
\n\
-prev Like -next, but goes in the other direction.\n\
+\n\
+ -select <N> Like -activate, but runs the Nth element in the list of\n\
+ hacks. By knowing what is in the `programs' list, and in\n\
+ what order, you can use this to activate the screensaver\n\
+ with a particular graphics demo. (The first element in the\n\
+ list is numbered 1, not 0.)\n\
\n\
-exit Causes the xscreensaver process to exit gracefully. This is\n\
roughly the same as killing the process with `kill', but it\n\
int i;
char *dpyname = 0;
Atom *cmd = 0;
+ long arg = 0L;
progname = argv[0];
screensaver_version = (char *) malloc (5);
else if (!strncmp (s, "-cycle", L)) cmd = &XA_CYCLE;
else if (!strncmp (s, "-next", L)) cmd = &XA_NEXT;
else if (!strncmp (s, "-prev", L)) cmd = &XA_PREV;
+ else if (!strncmp (s, "-select", L)) cmd = &XA_SELECT;
else if (!strncmp (s, "-exit", L)) cmd = &XA_EXIT;
else if (!strncmp (s, "-restart", L)) cmd = &XA_RESTART;
else if (!strncmp (s, "-demo", L)) cmd = &XA_DEMO;
else if (!strncmp (s, "-version", L)) cmd = &XA_SCREENSAVER_VERSION;
else if (!strncmp (s, "-time", L)) cmd = &XA_SCREENSAVER_TIME;
else USAGE ();
+
+ if (cmd == &XA_SELECT)
+ {
+ char junk;
+ i++;
+ if (i >= argc ||
+ (1 != sscanf(argv[i], " %ld %c", &arg, &junk)))
+ USAGE ();
+ }
}
if (!cmd)
USAGE ();
XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
XA_SCREENSAVER_TIME = XInternAtom (dpy, "_SCREENSAVER_TIME", False);
+ XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
XA_RESTART = XInternAtom (dpy, "RESTART", False);
XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
XA_NEXT = XInternAtom (dpy, "NEXT", False);
XA_PREV = XInternAtom (dpy, "PREV", False);
+ XA_SELECT = XInternAtom (dpy, "SELECT", False);
XA_EXIT = XInternAtom (dpy, "EXIT", False);
XA_DEMO = XInternAtom (dpy, "DEMO", False);
XA_PREFS = XInternAtom (dpy, "PREFS", False);
XSync (dpy, 0);
if (*cmd == XA_ACTIVATE || *cmd == XA_LOCK ||
- *cmd == XA_NEXT || *cmd == XA_PREV)
+ *cmd == XA_NEXT || *cmd == XA_PREV || *cmd == XA_SELECT)
/* People never guess that KeyRelease deactivates the screen saver too,
so if we're issuing an activation command, wait a second. */
sleep (1);
- xscreensaver_command(dpy, *cmd);
-
- fflush (stdout);
- fflush (stderr);
- exit (0);
+ i = xscreensaver_command (dpy, *cmd, arg);
+ if (i < 0) exit (i);
+ else exit (0);
}
#endif /* STANDALONE */