http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.01.tar.gz
[xscreensaver] / driver / xscreensaver-command.c
1 /* xscreensaver-command, Copyright (c) 1991-2002
2  *  by Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <time.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 #include <X11/Xproto.h>         /* for CARD32 */
28 #include <X11/Xlib.h>
29 #include <X11/Xatom.h>
30 #include <X11/Xutil.h>          /* for XGetClassHint() */
31 #include <X11/Xos.h>
32
33 #include <X11/Intrinsic.h>      /* only needed to get through xscreensaver.h */
34
35 #include "remote.h"
36 #include "version.h"
37
38 #ifdef _VROOT_H_
39 ERROR! you must not include vroot.h in this file
40 #endif
41
42 char *progname;
43
44 Atom XA_VROOT;
45 Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE;
46 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO, XA_EXIT;
47 Atom XA_BLANK, XA_LOCK;
48 static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV;
49 static Atom XA_RESTART, XA_PREFS, XA_THROTTLE, XA_UNTHROTTLE;
50
51 static char *screensaver_version;
52 static char *usage = "\n\
53 usage: %s -<option>\n\
54 \n\
55   This program provides external control of a running xscreensaver process.\n\
56   Version %s, copyright (c) 1991-2002 Jamie Zawinski <jwz@jwz.org>.\n\
57 \n\
58   The xscreensaver program is a daemon that runs in the background.\n\
59   You control a running xscreensaver process by sending it messages\n\
60   with this program, xscreensaver-command.  See the man pages for\n\
61   details.  These are the arguments understood by xscreensaver-command:\n\
62 \n\
63   -demo         Ask the xscreensaver process to enter interactive demo mode.\n\
64 \n\
65   -prefs        Ask the xscreensaver process to bring up the preferences\n\
66                 panel.\n\
67 \n\
68   -activate     Turn on the screensaver (blank the screen), as if the user\n\
69                 had been idle for long enough.\n\
70 \n\
71   -deactivate   Turns off the screensaver (un-blank the screen), as if user\n\
72                 activity had been detected.\n\
73 \n\
74   -cycle        If the screensaver is active (the screen is blanked), then\n\
75                 stop the current graphics demo and run a new one (chosen\n\
76                 randomly.)\n\
77 \n\
78   -next         Like either -activate or -cycle, depending on which is more\n\
79                 appropriate, except that the graphics hack that will be run\n\
80                 is the next one in the list, instead of a randomly-chosen\n\
81                 one.  In other words, repeatedly executing -next will cause\n\
82                 the xscreensaver process to invoke each graphics demo\n\
83                 sequentially.  (Though using the -demo option is probably\n\
84                 an easier way to accomplish that.)\n\
85 \n\
86   -prev         Like -next, but goes in the other direction.\n\
87 \n\
88   -select <N>   Like -activate, but runs the Nth element in the list of\n\
89                 hacks.  By knowing what is in the `programs' list, and in\n\
90                 what order, you can use this to activate the screensaver\n\
91                 with a particular graphics demo.  (The first element in the\n\
92                 list is numbered 1, not 0.)\n\
93 \n\
94   -exit         Causes the xscreensaver process to exit gracefully.  This is\n\
95                 roughly the same as killing the process with `kill', but it\n\
96                 is easier, since you don't need to first figure out the pid.\n\
97                 (Note that one must *never* kill xscreensaver with -9!)\n\
98 \n\
99   -restart      Causes the screensaver process to exit and then restart with\n\
100                 the same command line arguments as last time.  Do this after\n\
101                 you've changed your X resource settings, to cause\n\
102                 xscreensaver to notice the changes.\n\
103 \n\
104   -lock         Tells the running xscreensaver process to lock the screen\n\
105                 immediately.  This is like -activate, but forces locking as\n\
106                 well, even if locking is not the default.  If the saver is\n\
107                 already active, this causes it to be locked as well.\n\
108 \n\
109   -throttle     Temporarily switch to ``blank screen'' mode, and don't run\n\
110                 any display modes at all, until the screensaver is next\n\
111                 de-activated.  This is useful if you're using a machine\n\
112                 remotely, and you find that some display modes are using too\n\
113                 much CPU.\n\
114 \n\
115   -unthrottle   Turn `-throttle' off and resume normal behavior.\n\
116 \n\
117   -version      Prints the version of xscreensaver that is currently running\n\
118                 on the display -- that is, the actual version number of the\n\
119                 running xscreensaver background process, rather than the\n\
120                 version number of xscreensaver-command.\n\
121 \n\
122   -time         Prints the time at which the screensaver last activated or\n\
123                 deactivated (roughly, how long the user has been idle or\n\
124                 non-idle -- but not quite, since it only tells you when the\n\
125                 screen became blanked or un-blanked.)\n\
126 \n\
127   -watch        Prints a line each time the screensaver changes state: when\n\
128                 the screen blanks, locks, unblanks, or when the running hack\n\
129                 is changed.  This option never returns; it is intended for\n\
130                 by shell scripts that want to react to the screensaver in\n\
131                 some way.\n\
132 \n\
133   See the man page for more details.\n\
134   For updates, check http://www.jwz.org/xscreensaver/\n\
135 \n";
136
137 #define USAGE() do { \
138  fprintf (stderr, usage, progname, screensaver_version); exit (1); \
139  } while(0)
140
141 static int watch (Display *);
142
143 int
144 main (int argc, char **argv)
145 {
146   Display *dpy;
147   int i;
148   char *dpyname = 0;
149   Atom *cmd = 0;
150   long arg = 0L;
151   char *s;
152
153   progname = argv[0];
154   s = strrchr (progname, '/');
155   if (s) progname = s+1;
156
157   screensaver_version = (char *) malloc (5);
158   memcpy (screensaver_version, screensaver_id + 17, 4);
159   screensaver_version [4] = 0;
160
161   for (i = 1; i < argc; i++)
162     {
163       const char *s = argv [i];
164       int L;
165       if (s[0] == '-' && s[1] == '-') s++;
166       L = strlen (s);
167       if (L < 2) USAGE ();
168       if (!strncmp (s, "-display", L))          dpyname = argv [++i];
169       else if (cmd) USAGE();
170       else if (!strncmp (s, "-activate", L))   cmd = &XA_ACTIVATE;
171       else if (!strncmp (s, "-deactivate", L)) cmd = &XA_DEACTIVATE;
172       else if (!strncmp (s, "-cycle", L))      cmd = &XA_CYCLE;
173       else if (!strncmp (s, "-next", L))       cmd = &XA_NEXT;
174       else if (!strncmp (s, "-prev", L))       cmd = &XA_PREV;
175       else if (!strncmp (s, "-select", L))     cmd = &XA_SELECT;
176       else if (!strncmp (s, "-exit", L))       cmd = &XA_EXIT;
177       else if (!strncmp (s, "-restart", L))    cmd = &XA_RESTART;
178       else if (!strncmp (s, "-demo", L))       cmd = &XA_DEMO;
179       else if (!strncmp (s, "-preferences",L)) cmd = &XA_PREFS;
180       else if (!strncmp (s, "-prefs",L))       cmd = &XA_PREFS;
181       else if (!strncmp (s, "-lock", L))       cmd = &XA_LOCK;
182       else if (!strncmp (s, "-throttle", L))   cmd = &XA_THROTTLE;
183       else if (!strncmp (s, "-unthrottle", L)) cmd = &XA_UNTHROTTLE;
184       else if (!strncmp (s, "-version", L))    cmd = &XA_SCREENSAVER_VERSION;
185       else if (!strncmp (s, "-time", L))       cmd = &XA_SCREENSAVER_STATUS;
186       else if (!strncmp (s, "-watch", L))      cmd = (Atom *) &watch;
187       else USAGE ();
188
189       if (cmd == &XA_SELECT || cmd == &XA_DEMO)
190         {
191           long a;
192           char c;
193           if (i+1 < argc && (1 == sscanf(argv[i+1], " %ld %c", &a, &c)))
194             {
195               arg = a;
196               i++;
197             }
198         }
199     }
200
201   if (!cmd)
202     USAGE ();
203
204   if (arg < 0)
205     /* no command may have a negative argument. */
206     USAGE();
207   else if (arg == 0)
208     {
209       /* SELECT must have a non-zero argument. */
210       if (cmd == &XA_SELECT)
211         USAGE();
212     }
213   else /* arg > 0 */
214     {
215       /* no command other than SELECT and DEMO may have a non-zero argument. */
216       if (cmd != &XA_DEMO && cmd != &XA_SELECT)
217         USAGE();
218     }
219
220
221
222   /* For backward compatibility: -demo with no arguments used to send a
223      "DEMO 0" ClientMessage to the xscreensaver process, which brought up
224      the built-in demo mode dialog.  Now that the demo mode dialog is no
225      longer built in, we bring it up by just running the "xscreensaver-demo"
226      program.
227
228      Note that "-DEMO <n>" still sends a ClientMessage.
229    */
230   if (cmd == &XA_PREFS ||
231       (cmd == &XA_DEMO && arg == 0))
232     {
233       char buf [512];
234       char *new_argv[] = { "xscreensaver-demo", 0, 0, 0, 0, 0 };
235       int ac = 1;
236
237       if (dpyname)
238         {
239           new_argv[ac++] = "-display";
240           new_argv[ac++] = dpyname;
241         }
242
243       if (cmd == &XA_PREFS)
244         new_argv[ac++] = "-prefs";
245
246       fflush(stdout);
247       fflush(stderr);
248       execvp (new_argv[0], new_argv);   /* shouldn't return */
249
250       sprintf (buf, "%s: could not exec %s", progname, new_argv[0]);
251       perror(buf);
252       fflush(stdout);
253       fflush(stderr);
254       exit (-1);
255     }
256
257
258
259   if (!dpyname) dpyname = (char *) getenv ("DISPLAY");
260
261   if (!dpyname)
262     {
263       dpyname = ":0.0";
264       fprintf (stderr,
265                "%s: warning: $DISPLAY is not set: defaulting to \"%s\".\n",
266                progname, dpyname);
267     }
268
269   dpy = XOpenDisplay (dpyname);
270   if (!dpy)
271     {
272       fprintf (stderr, "%s: can't open display %s\n", progname,
273                (dpyname ? dpyname : "(null)"));
274       exit (1);
275     }
276
277   XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
278   XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
279   XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
280   XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
281   XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
282   XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
283   XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
284   XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False);
285   XA_RESTART = XInternAtom (dpy, "RESTART", False);
286   XA_CYCLE = XInternAtom (dpy, "CYCLE", False);
287   XA_NEXT = XInternAtom (dpy, "NEXT", False);
288   XA_PREV = XInternAtom (dpy, "PREV", False);
289   XA_SELECT = XInternAtom (dpy, "SELECT", False);
290   XA_EXIT = XInternAtom (dpy, "EXIT", False);
291   XA_DEMO = XInternAtom (dpy, "DEMO", False);
292   XA_PREFS = XInternAtom (dpy, "PREFS", False);
293   XA_LOCK = XInternAtom (dpy, "LOCK", False);
294   XA_BLANK = XInternAtom (dpy, "BLANK", False);
295   XA_THROTTLE = XInternAtom (dpy, "THROTTLE", False);
296   XA_UNTHROTTLE = XInternAtom (dpy, "UNTHROTTLE", False);
297
298   XSync (dpy, 0);
299
300   if (cmd == (Atom *) &watch)
301     {
302       i = watch (dpy);
303       exit (i);
304     }
305
306   if (*cmd == XA_ACTIVATE || *cmd == XA_LOCK ||
307       *cmd == XA_NEXT || *cmd == XA_PREV || *cmd == XA_SELECT)
308     /* People never guess that KeyRelease deactivates the screen saver too,
309        so if we're issuing an activation command, wait a second. */
310     sleep (1);
311
312   i = xscreensaver_command (dpy, *cmd, arg, True, NULL);
313   if (i < 0) exit (i);
314   else exit (0);
315 }
316
317
318 static int
319 watch (Display *dpy)
320 {
321   char *v = 0;
322   Window window = RootWindow (dpy, 0);
323   XWindowAttributes xgwa;
324   XEvent event;
325   CARD32 *last = 0;
326
327   if (v) free (v);
328   XGetWindowAttributes (dpy, window, &xgwa);
329   XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
330
331   while (1)
332     {
333       XNextEvent (dpy, &event);
334       if (event.xany.type == PropertyNotify &&
335           event.xproperty.state == PropertyNewValue &&
336           event.xproperty.atom == XA_SCREENSAVER_STATUS)
337         {
338           Atom type;
339           int format;
340           unsigned long nitems, bytesafter;
341           CARD32 *data = 0;
342
343           if (XGetWindowProperty (dpy,
344                                   RootWindow (dpy, 0),  /* always screen #0 */
345                                   XA_SCREENSAVER_STATUS,
346                                   0, 999, False, XA_INTEGER,
347                                   &type, &format, &nitems, &bytesafter,
348                                   (unsigned char **) &data)
349               == Success
350               && type
351               && data)
352             {
353               time_t tt;
354               char *s;
355               Bool changed = False;
356               Bool running = False;
357
358               if (type != XA_INTEGER || nitems < 3)
359                 {
360                 STATUS_LOSE:
361                   if (last) free (last);
362                   if (data) free (data);
363                   fprintf (stderr, "%s: bad status format on root window.\n",
364                            progname);
365                   return -1;
366                 }
367                   
368               tt = (time_t) data[1];
369               if (tt <= (time_t) 666000000L) /* early 1991 */
370                 goto STATUS_LOSE;
371
372               s = ctime(&tt);
373               if (s[strlen(s)-1] == '\n')
374                 s[strlen(s)-1] = 0;
375
376               if (!last || data[0] != last[0])
377                 {
378                   /* State changed. */
379                   if (data[0] == XA_BLANK)
380                     printf ("BLANK %s\n", s);
381                   else if (data[0] == XA_LOCK)
382                     printf ("LOCK %s\n", s);
383                   else if (data[0] == 0)
384                     printf ("UNBLANK %s\n", s);
385                   else
386                     goto STATUS_LOSE;
387                 }
388
389               if (!last)
390                 changed = True;
391               else
392                 {
393                   int i;
394                   for (i = 2; i < nitems; i++)
395                     {
396                       if (data[i] != last[i])
397                         changed = True;
398                       if (data[i])
399                         running = True;
400                     }
401                 }
402
403               if (running && changed)
404                 {
405                   int i;
406                   fprintf (stdout, "RUN", s);
407                   for (i = 2; i < nitems; i++)
408                     fprintf (stdout, " %d", (int) data[i]);
409                   fprintf (stdout, "\n");
410                 }
411
412               fflush (stdout);
413
414               if (last) free (last);
415               last = data;
416             }
417           else
418             {
419               if (last) free (last);
420               if (data) free (data);
421               fprintf (stderr, "%s: no saver status on root window.\n",
422                        progname);
423               return -1;
424             }
425         }
426     }
427 }