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