2d7f3326064d97916b44782cf7530bed434d2e14
[xscreensaver] / driver / xscreensaver-command.c
1 /* xscreensaver-command, Copyright (c) 1991-1997
2  *  by Jamie Zawinski <jwz@netscape.com>
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 <X11/Xproto.h>         /* for CARD32 */
19 #include <X11/Xlib.h>
20 #include <X11/Xatom.h>
21 #include <X11/Xutil.h>          /* for XGetClassHint() */
22 #include <X11/Xos.h>
23 #include <stdlib.h>
24
25 #include "version.h"
26
27 #ifdef _VROOT_H_
28 ERROR! you must not include vroot.h in this file
29 #endif
30
31 static char *screensaver_version;
32 static char *usage = "usage: %s -<switch>\n\
33 \n\
34   This program provides external control of a running xscreensaver process.\n\
35   Version %s, copyright (c) 1991-1997 Jamie Zawinski <jwz@netscape.com>.\n\
36 \n\
37   -demo         Enter interactive demo mode.\n\
38   -deactivate   Turns off the screensaver if it is on, as user input would.\n\
39   -activate     Turns it on as if the user had been idle for long enough.\n\
40   -cycle        Stops the current graphics hack and runs a new one.\n\
41   -next         Like either -activate or -cycle, depending on which is more\n\
42                 appropriate, except that the screenhack that will be run is\n\
43                 the next one in the list of hacks, instead of a randomly-\n\
44                 chosen one.  This option could be used for looking at a demo\n\
45                 of each of the configured hacks.\n\
46   -prev         Like -next, but goes in the other direction.\n\
47   -exit         Causes the screensaver process to exit.  This is the same as\n\
48                 killing the process with `kill', but it's easier, since you\n\
49                 don't need to first figure out the pid.  (Note that one\n\
50                 must *never* kill xscreensaver with -9!)\n\
51   -restart      Causes the screensaver process to exit and then restart with\n\
52                 the same command line arguments.  Do this after you've\n\
53                 changed the resource database, to cause the screensaver to\n\
54                 notice the changes.\n\
55   -lock         Same as -activate, but with immediate locking.\n\
56   -version      Prints the version of XScreenSaver that is currently running\n\
57                 on the display.\n\
58   -time         Prints the time at which the screensaver last activated or\n\
59                 deactivated (roughly, how long the user has been idle or\n\
60                 non-idle.)\n\
61 \n\
62   See the man page for more details.\n\
63   For updates, check http://people.netscape.com/jwz/xscreensaver/\n\
64 \n";
65
66 static Window
67 find_screensaver_window (Display *dpy, char *progname, char **version)
68 {
69   int i;
70   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
71   Window root2, parent, *kids;
72   unsigned int nkids;
73
74   if (version) *version = 0;
75
76   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
77     abort ();
78   if (root != root2)
79     abort ();
80   if (parent)
81     abort ();
82   if (! (kids && nkids))
83     abort ();
84   for (i = 0; i < nkids; i++)
85     {
86       Atom type;
87       int format;
88       unsigned long nitems, bytesafter;
89       char *v;
90
91       if (XGetWindowProperty (dpy, kids[i],
92                               XInternAtom (dpy, "_SCREENSAVER_VERSION", False),
93                               0, 200, False, XA_STRING,
94                               &type, &format, &nitems, &bytesafter,
95                               (unsigned char **) &v)
96           == Success
97           && type != None)
98         {
99           if (version)
100             *version = v;
101           return kids[i];
102         }
103     }
104   fprintf (stderr, "%s: no screensaver is running on display %s\n", progname,
105            DisplayString (dpy));
106   exit (1);
107 }
108
109
110 #define USAGE() \
111  { fprintf (stderr, usage, argv[0], screensaver_version); exit (1); }
112
113 int
114 main (int argc, char **argv)
115 {
116   Display *dpy;
117   Window window;
118   int i;
119   int query = 0;
120 #define Q_version 1
121 #define Q_time    2
122   char *message = 0, *dpyname = 0;
123   char *v = 0;
124
125   screensaver_version = (char *) malloc (5);
126   memcpy (screensaver_version, screensaver_id + 17, 4);
127   screensaver_version [4] = 0;
128   for (i = 1; i < argc; i++)
129     {
130       char *s = argv [i];
131       int L = strlen (s);
132       if (L < 2) USAGE ();
133       if (!strncmp (s, "-display", L))          dpyname = argv [++i];
134       else if (message) USAGE ()
135       else if (!strncmp (s, "-activate", L))    message = "ACTIVATE";
136       else if (!strncmp (s, "-deactivate", L))  message = "DEACTIVATE";
137       else if (!strncmp (s, "-cycle", L))       message = "CYCLE";
138       else if (!strncmp (s, "-next", L))        message = "NEXT";
139       else if (!strncmp (s, "-prev", L))        message = "PREV";
140       else if (!strncmp (s, "-exit", L))        message = "EXIT";
141       else if (!strncmp (s, "-restart", L))     message = "RESTART";
142       else if (!strncmp (s, "-demo", L))        message = "DEMO";
143       else if (!strncmp (s, "-lock", L))        message = "LOCK";
144       else if (!strncmp (s, "-version", L))     query   = Q_version;
145       else if (!strncmp (s, "-time", L))        query   = Q_time;
146       else USAGE ();
147     }
148   if (!message && !query) USAGE ();
149   if (!dpyname) dpyname = (char *) getenv ("DISPLAY");
150   dpy = XOpenDisplay (dpyname);
151   if (!dpy)
152     {
153       fprintf (stderr, "%s: can't open display %s\n", argv[0],
154                (dpyname ? dpyname : "(null)"));
155       exit (1);
156     }
157   window = find_screensaver_window (dpy, argv[0], &v);
158
159   if (message)
160     {
161       XEvent event;
162       event.xany.type = ClientMessage;
163       event.xclient.display = dpy;
164       event.xclient.window = window;
165       event.xclient.message_type = XInternAtom (dpy, "SCREENSAVER", False);
166       event.xclient.format = 32;
167       event.xclient.data.l[0] = (long) XInternAtom (dpy, message, False);
168       if (! XSendEvent (dpy, window, False, 0L, &event))
169         {
170           fprintf (stderr, "%s: XSendEvent(dpy, 0x%x ...) failed.\n", argv [0],
171                    (unsigned int) window);
172           exit (1);
173         }
174     }
175   else if (query)
176     {
177       XClassHint hint;
178       memset (&hint, 0, sizeof(hint));
179       if (!v || !*v)
180         {
181           fprintf (stderr, "%s: version property not set on window 0x%x?\n",
182                    argv [0], (unsigned int) window);
183           exit (1);
184         }
185
186       XGetClassHint(dpy, window, &hint);
187       if (!hint.res_class)
188         {
189           fprintf (stderr, "%s: class hints not set on window 0x%x?\n",
190                    argv [0], (unsigned int) window);
191           exit (1);
192         }
193
194       fprintf (stdout, "%s %s", hint.res_class, v);
195
196       if (query != Q_time)
197         {
198           fprintf (stdout, "\n");
199         }
200       else
201         {
202           Atom type;
203           int format;
204           unsigned long nitems, bytesafter;
205           unsigned char *data = 0;
206           XWindowAttributes xgwa;
207           Bool active_p = False;
208
209           xgwa.map_state = IsViewable;
210           XGetWindowAttributes (dpy, window, &xgwa);
211
212           if (XGetWindowProperty (dpy, window,
213                                   XInternAtom (dpy, "__SWM_VROOT", False),
214                                   0, 0, False, XA_WINDOW,
215                                   &type, &format, &nitems, &bytesafter,
216                                   &data)
217               == Success
218               && type != None)
219             active_p = True;
220
221           if (data) free (data);
222           data = 0;
223
224           if (XGetWindowProperty (dpy, window,
225                                   XInternAtom (dpy, "_SCREENSAVER_TIME",False),
226                                   0, 1, False, XA_INTEGER,
227                                   &type, &format, &nitems, &bytesafter,
228                                   &data)
229               == Success
230               && type == XA_INTEGER
231               && data)
232             {
233               CARD32 time32 = *((CARD32 *)data);
234               time_t tt = (time_t) time32;
235
236               if (active_p)
237                 fprintf (stdout, ": active since ");
238               else
239                 fprintf (stdout, ": inactive since ");
240               fprintf (stdout, "%s", ctime(&tt));
241               if (data) free (data);
242             }
243           else
244             {
245               if (data) free (data);
246               fprintf (stdout, "\n");
247               fflush (stdout);
248               fprintf (stderr, "%s: no time on window 0x%x (%s %s).\n",
249                        argv[0], (unsigned int) window,
250                        hint.res_class, (v ? v : "???"));
251               exit (1);
252             }
253         }
254     }
255
256   XSync (dpy, 0);
257   fflush (stdout);
258   fflush (stderr);
259   exit (0);
260 }