http://ftp.x.org/contrib/applications/xscreensaver-2.34.tar.gz
[xscreensaver] / driver / xscreensaver-command.c
index 4c7a02855bd4dbaeb3d0ffdbdf531a9727c98ded..d5d3ba9c93b4c912a847e95f5fdeeb39035a3771 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
 
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
@@ -36,7 +38,8 @@
 #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 */
@@ -46,6 +49,8 @@
 ERROR! you must not include vroot.h in this file
 #endif
 
+
+
 static Window
 find_screensaver_window (Display *dpy, char **version)
 {
@@ -86,15 +91,27 @@ 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)
@@ -105,7 +122,7 @@ xscreensaver_command (Display *dpy, Atom command)
        {
          fprintf (stderr, "%s: version property not set on window 0x%x?\n",
                   progname, (unsigned int) window);
-         exit (1);
+         return -1;
        }
 
       XGetClassHint(dpy, window, &hint);
@@ -113,7 +130,7 @@ xscreensaver_command (Display *dpy, Atom command)
        {
          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);
@@ -128,12 +145,8 @@ xscreensaver_command (Display *dpy, Atom command)
          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,
@@ -173,30 +186,177 @@ xscreensaver_command (Display *dpy, Atom command)
              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;
@@ -239,6 +399,12 @@ usage: %s -<option>\n\
                 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\
@@ -278,6 +444,7 @@ main (int argc, char **argv)
   int i;
   char *dpyname = 0;
   Atom *cmd = 0;
+  long arg = 0L;
 
   progname = argv[0];
   screensaver_version = (char *) malloc (5);
@@ -298,6 +465,7 @@ main (int argc, char **argv)
       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;
@@ -307,6 +475,15 @@ main (int argc, char **argv)
       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 ();
@@ -324,12 +501,14 @@ main (int argc, char **argv)
   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);
@@ -338,16 +517,14 @@ main (int argc, char **argv)
   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 */