http://ftp.x.org/contrib/applications/xscreensaver-3.02.tar.gz
[xscreensaver] / driver / remote.c
1 /* xscreensaver-command, Copyright (c) 1991-1998
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 <sys/time.h>
20 #include <sys/types.h>
21
22 #ifdef HAVE_SYS_SELECT_H
23 # include <sys/select.h>
24 #endif /* HAVE_SYS_SELECT_H */
25
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29
30 #include <X11/Xproto.h>         /* for CARD32 */
31 #include <X11/Xlib.h>
32 #include <X11/Xatom.h>
33 #include <X11/Xutil.h>          /* for XGetClassHint() */
34 #include <X11/Xos.h>
35
36 #include "remote.h"
37
38 #ifdef _VROOT_H_
39 ERROR! you must not include vroot.h in this file
40 #endif
41
42 extern char *progname;
43 extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE;
44 extern Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_TIME;
45 extern Atom XA_VROOT, XA_SELECT, XA_DEMO;
46
47
48 static XErrorHandler old_handler = 0;
49 static Bool got_badwindow = False;
50 static int
51 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
52 {
53   /* When we notice a window being created, we spawn a timer that waits
54      30 seconds or so, and then selects events on that window.  This error
55      handler is used so that we can cope with the fact that the window
56      may have been destroyed <30 seconds after it was created.
57    */
58   if (error->error_code == BadWindow)
59     {
60       got_badwindow = True;
61       return 0;
62     }
63   else
64     {
65       fprintf (stderr, "%s: ", progname);
66       return (*old_handler) (dpy, error);
67     }
68 }
69
70
71
72 static Window
73 find_screensaver_window (Display *dpy, char **version)
74 {
75   int i;
76   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
77   Window root2, parent, *kids;
78   unsigned int nkids;
79
80   if (version) *version = 0;
81
82   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
83     abort ();
84   if (root != root2)
85     abort ();
86   if (parent)
87     abort ();
88   if (! (kids && nkids))
89     abort ();
90   for (i = 0; i < nkids; i++)
91     {
92       Atom type;
93       int format;
94       unsigned long nitems, bytesafter;
95       char *v;
96
97       if (XGetWindowProperty (dpy, kids[i],
98                               XA_SCREENSAVER_VERSION,
99                               0, 200, False, XA_STRING,
100                               &type, &format, &nitems, &bytesafter,
101                               (unsigned char **) &v)
102           == Success
103           && type != None)
104         {
105           if (version)
106             *version = v;
107           return kids[i];
108         }
109     }
110   fprintf (stderr, "%s: no screensaver is running on display %s\n", progname,
111            DisplayString (dpy));
112   return 0;
113 }
114
115
116 static int
117 send_xscreensaver_command (Display *dpy, Atom command, long arg,
118                            Window *window_ret)
119 {
120   char *v = 0;
121   Window window = find_screensaver_window (dpy, &v);
122   XWindowAttributes xgwa;
123
124   if (window_ret)
125     *window_ret = window;
126
127   if (!window)
128     return -1;
129
130   /* Select for property change events, so that we can read the response. */
131   XGetWindowAttributes (dpy, window, &xgwa);
132   XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
133
134   if (command == XA_SCREENSAVER_TIME ||
135       command == XA_SCREENSAVER_VERSION)
136     {
137       XClassHint hint;
138       memset (&hint, 0, sizeof(hint));
139       if (!v || !*v)
140         {
141           fprintf (stderr, "%s: version property not set on window 0x%x?\n",
142                    progname, (unsigned int) window);
143           return -1;
144         }
145
146       XGetClassHint(dpy, window, &hint);
147       if (!hint.res_class)
148         {
149           fprintf (stderr, "%s: class hints not set on window 0x%x?\n",
150                    progname, (unsigned int) window);
151           return -1;
152         }
153
154       fprintf (stdout, "%s %s", hint.res_class, v);
155
156       if (command != XA_SCREENSAVER_TIME)
157         {
158           fprintf (stdout, "\n");
159         }
160       else
161         {
162           Atom type;
163           int format;
164           unsigned long nitems, bytesafter;
165           unsigned char *data = 0;
166           Bool active_p = False;
167
168           if (XGetWindowProperty (dpy, window, XA_VROOT,
169                                   0, 0, False, XA_WINDOW,
170                                   &type, &format, &nitems, &bytesafter,
171                                   &data)
172               == Success
173               && type != None)
174             active_p = True;
175
176           if (data) free (data);
177           data = 0;
178
179           if (XGetWindowProperty (dpy, window,
180                                   XA_SCREENSAVER_TIME,
181                                   0, 1, False, XA_INTEGER,
182                                   &type, &format, &nitems, &bytesafter,
183                                   &data)
184               == Success
185               && type == XA_INTEGER
186               && data)
187             {
188               CARD32 time32 = *((CARD32 *)data);
189               time_t tt = (time_t) time32;
190
191               if (active_p)
192                 fprintf (stdout, ": screen blanked since ");
193               else
194                 /* suggestions for a better way to phrase this are welcome. */
195                 fprintf (stdout, ": screen non-blanked since ");
196               fprintf (stdout, "%s", ctime(&tt));
197               if (data) free (data);
198             }
199           else
200             {
201               if (data) free (data);
202               fprintf (stdout, "\n");
203               fflush (stdout);
204               fprintf (stderr, "%s: no time on window 0x%x (%s %s).\n",
205                        progname, (unsigned int) window,
206                        hint.res_class, (v ? v : "???"));
207               return -1;
208             }
209         }
210
211       /* No need to read a response for these commands. */
212       return 1;
213     }
214   else
215     {
216       XEvent event;
217       long arg1 = arg;
218       long arg2 = 0;
219
220       if (arg < 0)
221         abort();
222       else if (arg == 0 && command == XA_SELECT)
223         abort();
224       else if (arg != 0 && command == XA_DEMO)
225         {
226           arg1 = 300;   /* version number of the XA_DEMO protocol, */
227           arg2 = arg;   /* since it didn't use to take an argument. */
228         }
229
230       event.xany.type = ClientMessage;
231       event.xclient.display = dpy;
232       event.xclient.window = window;
233       event.xclient.message_type = XA_SCREENSAVER;
234       event.xclient.format = 32;
235       memset (&event.xclient.data, 0, sizeof(event.xclient.data));
236       event.xclient.data.l[0] = (long) command;
237       event.xclient.data.l[1] = arg1;
238       event.xclient.data.l[2] = arg2;
239       if (! XSendEvent (dpy, window, False, 0L, &event))
240         {
241           fprintf (stderr, "%s: XSendEvent(dpy, 0x%x ...) failed.\n",
242                    progname, (unsigned int) window);
243           return -1;
244         }
245     }
246   XSync (dpy, 0);
247   return 0;
248 }
249
250
251 static int
252 xscreensaver_command_response (Display *dpy, Window window, Bool verbose_p)
253 {
254   int fd = ConnectionNumber (dpy);
255   int timeout = 10;
256   int status;
257   fd_set fds;
258   struct timeval tv;
259
260   while (1)
261     {
262       FD_ZERO(&fds);
263       FD_SET(fd, &fds);
264       memset(&tv, 0, sizeof(tv));
265       tv.tv_sec = timeout;
266       status = select (fd+1, &fds, 0, &fds, &tv);
267
268       if (status < 0)
269         {
270           char buf[1024];
271           sprintf (buf, "%s: waiting for reply", progname);
272           perror (buf);
273           return status;
274         }
275       else if (status == 0)
276         {
277           fprintf (stderr, "%s: no response to command.\n", progname);
278           return -1;
279         }
280       else
281         {
282           XEvent event;
283           XNextEvent (dpy, &event);
284           if (event.xany.type == PropertyNotify &&
285               event.xproperty.state == PropertyNewValue &&
286               event.xproperty.atom == XA_SCREENSAVER_RESPONSE)
287             {
288               Status st2;
289               Atom type;
290               int format;
291               unsigned long nitems, bytesafter;
292               char *msg = 0;
293
294               old_handler = XSetErrorHandler (BadWindow_ehandler);
295               XSync (dpy, False);
296
297               st2 = XGetWindowProperty (dpy, window,
298                                         XA_SCREENSAVER_RESPONSE,
299                                         0, 1024, True,
300                                         AnyPropertyType,
301                                         &type, &format, &nitems, &bytesafter,
302                                         (unsigned char **) &msg);
303
304               if (got_badwindow)
305                 {
306                   fprintf (stdout,
307                            "%s: xscreensaver window has been deleted.\n",
308                            progname);
309                   return 0;
310                 }
311
312               if (st2 == Success && type != None)
313                 {
314                   if (type != XA_STRING || format != 8)
315                     {
316                       fprintf (stderr,
317                                "%s: unrecognized response property.\n",
318                                progname);
319                       if (msg) XFree (msg);
320                       return -1;
321                     }
322                   else if (!msg || (msg[0] != '+' && msg[0] != '-'))
323                     {
324                       fprintf (stderr,
325                                "%s: unrecognized response message.\n",
326                                progname);
327                       if (msg) XFree (msg);
328                       return -1;
329                     }
330                   else
331                     {
332                       int ret = (msg[0] == '+' ? 0 : -1);
333                       if (verbose_p || ret != 0)
334                         fprintf ((ret < 0 ? stderr : stdout),
335                                  "%s: %s\n",
336                                  progname,
337                                  msg+1);
338                       XFree (msg);
339                       return ret;
340                     }
341                 }
342             }
343         }
344     }
345 }
346
347
348 int
349 xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p)
350 {
351   Window w = 0;
352   int status = send_xscreensaver_command (dpy, command, arg, &w);
353   if (status == 0)
354     status = xscreensaver_command_response (dpy, w, verbose_p);
355   fflush (stdout);
356   fflush (stderr);
357   return status;
358 }
359
360
361 void
362 server_xscreensaver_version (Display *dpy,
363                              char **version_ret,
364                              char **user_ret,
365                              char **host_ret)
366 {
367   Window window = find_screensaver_window (dpy, 0);
368
369   Atom type;
370   int format;
371   unsigned long nitems, bytesafter;
372
373   if (version_ret)
374     *version_ret = 0;
375   if (host_ret)
376     *host_ret = 0;
377
378   if (!window)
379     return;
380
381   if (version_ret)
382     {
383       char *v = 0;
384       XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 1,
385                           False, XA_STRING, &type, &format, &nitems,
386                           &bytesafter, (unsigned char **) &v);
387       if (v)
388         {
389           *version_ret = strdup (v);
390           XFree (v);
391         }
392     }
393
394   if (user_ret || host_ret)
395     {
396       char *id = 0;
397       const char *user = 0;
398       const char *host = 0;
399
400       XGetWindowProperty (dpy, window, XA_SCREENSAVER_ID, 0, 512,
401                           False, XA_STRING, &type, &format, &nitems,
402                           &bytesafter, (unsigned char **) &id);
403       if (id && *id)
404         {
405           const char *old_tag = " on host ";
406           const char *s = strstr (id, old_tag);
407           if (s)
408             {
409               /* found ID of the form "1234 on host xyz". */
410               user = 0;
411               host = s + strlen (old_tag);
412             }
413           else
414             {
415               char *o = 0, *p = 0, *c = 0;
416               o = strchr (id, '(');
417               if (o) p = strchr (o, '@');
418               if (p) c = strchr (p, ')');
419               if (c)
420                 {
421                   /* found ID of the form "1234 (user@host)". */
422                   user = o+1;
423                   host = p+1;
424                   *p = 0;
425                   *c = 0;
426                 }
427             }
428
429         }
430
431       if (user && *user && *user != '?')
432         *user_ret = strdup (user);
433       else
434         *user_ret = 0;
435
436       if (host && *host && *host != '?')
437         *host_ret = strdup (host);
438       else
439         *host_ret = 0;
440
441       if (id)
442         XFree (id);
443     }
444 }