ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[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 <time.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22
23 #ifdef HAVE_SYS_SELECT_H
24 # include <sys/select.h>
25 #endif /* HAVE_SYS_SELECT_H */
26
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30
31 #include <X11/Xproto.h>         /* for CARD32 */
32 #include <X11/Xlib.h>
33 #include <X11/Xatom.h>
34 #include <X11/Xutil.h>          /* for XGetClassHint() */
35 #include <X11/Xos.h>
36
37 #include "remote.h"
38
39 #ifdef _VROOT_H_
40 ERROR! you must not include vroot.h in this file
41 #endif
42
43 extern char *progname;
44 extern Atom XA_SCREENSAVER, XA_SCREENSAVER_VERSION, XA_SCREENSAVER_RESPONSE;
45 extern Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_EXIT;
46 extern Atom XA_VROOT, XA_SELECT, XA_DEMO, XA_BLANK, XA_LOCK;
47
48
49 static XErrorHandler old_handler = 0;
50 static Bool got_badwindow = False;
51 static int
52 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
53 {
54   if (error->error_code == BadWindow)
55     {
56       got_badwindow = True;
57       return 0;
58     }
59   else
60     {
61       fprintf (stderr, "%s: ", progname);
62       if (!old_handler) abort();
63       return (*old_handler) (dpy, error);
64     }
65 }
66
67
68
69 static Window
70 find_screensaver_window (Display *dpy, char **version)
71 {
72   int i;
73   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
74   Window root2, parent, *kids;
75   unsigned int nkids;
76
77   if (version) *version = 0;
78
79   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
80     abort ();
81   if (root != root2)
82     abort ();
83   if (parent)
84     abort ();
85   if (! (kids && nkids))
86     return 0;
87   for (i = 0; i < nkids; i++)
88     {
89       Atom type;
90       int format;
91       unsigned long nitems, bytesafter;
92       unsigned char *v;
93       int status;
94
95       /* We're walking the list of root-level windows and trying to find
96          the one that has a particular property on it.  We need to trap
97          BadWindows errors while doing this, because it's possible that
98          some random window might get deleted in the meantime.  (That
99          window won't have been the one we're looking for.)
100        */
101       XSync (dpy, False);
102       if (old_handler) abort();
103       got_badwindow = False;
104       old_handler = XSetErrorHandler (BadWindow_ehandler);
105       status = XGetWindowProperty (dpy, kids[i],
106                                    XA_SCREENSAVER_VERSION,
107                                    0, 200, False, XA_STRING,
108                                    &type, &format, &nitems, &bytesafter,
109                                    &v);
110       XSync (dpy, False);
111       XSetErrorHandler (old_handler);
112       old_handler = 0;
113
114       if (got_badwindow)
115         {
116           status = BadWindow;
117           got_badwindow = False;
118         }
119
120       if (status == Success && type != None)
121         {
122           Window ret = kids[i];
123           if (version)
124             *version = (char *) v;
125           XFree (kids);
126           return ret;
127         }
128     }
129
130   if (kids) XFree (kids);
131   return 0;
132 }
133
134
135 static int
136 send_xscreensaver_command (Display *dpy, Atom command, long arg,
137                            Window *window_ret, char **error_ret)
138 {
139   int status = -1;
140   char *v = 0;
141   Window window = find_screensaver_window (dpy, &v);
142   XWindowAttributes xgwa;
143   char err[2048];
144
145   if (window_ret)
146     *window_ret = window;
147
148   if (!window)
149     {
150       sprintf (err, "no screensaver is running on display %s",
151                DisplayString (dpy));
152
153       if (error_ret)
154         {
155           *error_ret = strdup (err);
156           status = -1;
157           goto DONE;
158         }
159
160       if (command == XA_EXIT)
161         {
162           /* Don't print an error if xscreensaver is already dead. */
163           status = 1;
164           goto DONE;
165         }
166
167       fprintf (stderr, "%s: %s\n", progname, err);
168       status = -1;
169       goto DONE;
170     }
171
172   /* Select for property change events, so that we can read the response. */
173   XGetWindowAttributes (dpy, window, &xgwa);
174   XSelectInput (dpy, window, xgwa.your_event_mask | PropertyChangeMask);
175
176   if (command == XA_SCREENSAVER_STATUS ||
177       command == XA_SCREENSAVER_VERSION)
178     {
179       XClassHint hint;
180       memset (&hint, 0, sizeof(hint));
181       if (!v || !*v)
182         {
183           sprintf (err, "version property not set on window 0x%x?",
184                    (unsigned int) window);
185           if (error_ret)
186             *error_ret = strdup (err);
187           else
188             fprintf (stderr, "%s: %s\n", progname, err);
189
190           status = -1;
191           goto DONE;
192         }
193
194       XGetClassHint(dpy, window, &hint);
195       if (!hint.res_class)
196         {
197           sprintf (err, "class hints not set on window 0x%x?",
198                    (unsigned int) window);
199           if (error_ret)
200             *error_ret = strdup (err);
201           else
202             fprintf (stderr, "%s: %s\n", progname, err);
203
204           if (v) free (v);
205           status = -1;
206           goto DONE;
207         }
208
209       fprintf (stdout, "%s %s", hint.res_class, v);
210
211       if (command != XA_SCREENSAVER_STATUS)
212         {
213           fprintf (stdout, "\n");
214         }
215       else
216         {
217           Atom type;
218           int format;
219           unsigned long nitems, bytesafter;
220           unsigned char *dataP = 0;
221
222           if (XGetWindowProperty (dpy,
223                                   RootWindow (dpy, 0),
224                                   XA_SCREENSAVER_STATUS,
225                                   0, 999, False, XA_INTEGER,
226                                   &type, &format, &nitems, &bytesafter,
227                                   &dataP)
228               == Success
229               && type
230               && dataP)
231             {
232               Atom blanked;
233               time_t tt;
234               char *s;
235               Atom *data = (Atom *) dataP;
236
237               if (type != XA_INTEGER || nitems < 3)
238                 {
239                 STATUS_LOSE:
240                   if (data) free (data);
241                   fprintf (stdout, "\n");
242                   fflush (stdout);
243                   fprintf (stderr, "bad status format on root window.\n");
244                   status = -1;
245                   goto DONE;
246                 }
247                   
248               blanked = (Atom) data[0];
249               tt = (time_t) data[1];
250
251               if (tt <= (time_t) 666000000L) /* early 1991 */
252                 goto STATUS_LOSE;
253
254               if (blanked == XA_BLANK)
255                 fputs (": screen blanked since ", stdout);
256               else if (blanked == XA_LOCK)
257                 fputs (": screen locked since ", stdout);
258               else if (blanked == 0)
259                 /* suggestions for a better way to phrase this are welcome. */
260                 fputs (": screen non-blanked since ", stdout);
261               else
262                 /* `blanked' has an unknown value - fail. */
263                 goto STATUS_LOSE;
264
265               s = ctime(&tt);
266               if (s[strlen(s)-1] == '\n')
267                 s[strlen(s)-1] = 0;
268               fputs (s, stdout);
269
270               {
271                 int nhacks = nitems - 2;
272                 Bool any = False;
273                 int i;
274                 for (i = 0; i < nhacks; i++)
275                   if (data[i + 2] > 0)
276                     {
277                       any = True;
278                       break;
279                     }
280
281                 if (any && nhacks == 1)
282                   fprintf (stdout, " (hack #%d)\n", (int) data[2]);
283                 else if (any)
284                   {
285                     fprintf (stdout, " (hacks: ");
286                     for (i = 0; i < nhacks; i++)
287                       {
288                         fprintf (stdout, "#%d", (int) data[2 + i]);
289                         if (i != nhacks-1)
290                           fputs (", ", stdout);
291                       }
292                     fputs (")\n", stdout);
293                   }
294                 else
295                   fputs ("\n", stdout);
296               }
297
298               if (data) free (data);
299             }
300           else
301             {
302               if (dataP) XFree (dataP);
303               fprintf (stdout, "\n");
304               fflush (stdout);
305               fprintf (stderr, "no saver status on root window.\n");
306               status = -1;
307               goto DONE;
308             }
309         }
310
311       /* No need to read a response for these commands. */
312       status = 1;
313       goto DONE;
314     }
315   else
316     {
317       XEvent event;
318       long arg1 = arg;
319       long arg2 = 0;
320
321       if (arg < 0)
322         abort();
323       else if (arg == 0 && command == XA_SELECT)
324         abort();
325       else if (arg != 0 && command == XA_DEMO)
326         {
327           arg1 = 300;   /* version number of the XA_DEMO protocol, */
328           arg2 = arg;   /* since it didn't use to take an argument. */
329         }
330
331       event.xany.type = ClientMessage;
332       event.xclient.display = dpy;
333       event.xclient.window = window;
334       event.xclient.message_type = XA_SCREENSAVER;
335       event.xclient.format = 32;
336       memset (&event.xclient.data, 0, sizeof(event.xclient.data));
337       event.xclient.data.l[0] = (long) command;
338       event.xclient.data.l[1] = arg1;
339       event.xclient.data.l[2] = arg2;
340       if (! XSendEvent (dpy, window, False, 0L, &event))
341         {
342           sprintf (err, "XSendEvent(dpy, 0x%x ...) failed.\n",
343                    (unsigned int) window);
344           if (error_ret)
345             *error_ret = strdup (err);
346           else
347             fprintf (stderr, "%s: %s\n", progname, err);
348           status = -1;
349           goto DONE;
350         }
351     }
352
353   status = 0;
354
355  DONE:
356   if (v) free (v);
357   XSync (dpy, 0);
358   return status;
359 }
360
361
362 static int
363 xscreensaver_command_response (Display *dpy, Window window,
364                                Bool verbose_p, Bool exiting_p,
365                                char **error_ret)
366 {
367   int fd = ConnectionNumber (dpy);
368   int timeout = 10;
369   int status;
370   fd_set fds;
371   struct timeval tv;
372   char err[2048];
373
374   while (1)
375     {
376       FD_ZERO(&fds);
377       FD_SET(fd, &fds);
378       memset(&tv, 0, sizeof(tv));
379       tv.tv_sec = timeout;
380       status = select (fd+1, &fds, 0, &fds, &tv);
381
382       if (status < 0)
383         {
384           char buf[1024];
385           if (error_ret)
386             {
387               sprintf (buf, "error waiting for reply");
388               *error_ret = strdup (buf);
389             }
390           else
391             {
392               sprintf (buf, "%s: error waiting for reply", progname);
393               perror (buf);
394             }
395           return status;
396         }
397       else if (status == 0)
398         {
399           sprintf (err, "no response to command.");
400           if (error_ret)
401             *error_ret = strdup (err);
402           else
403             fprintf (stderr, "%s: %s\n", progname, err);
404           return -1;
405         }
406       else
407         {
408           XEvent event;
409           XNextEvent (dpy, &event);
410           if (event.xany.type == PropertyNotify &&
411               event.xproperty.state == PropertyNewValue &&
412               event.xproperty.atom == XA_SCREENSAVER_RESPONSE)
413             {
414               Status st2;
415               Atom type;
416               int format;
417               unsigned long nitems, bytesafter;
418               unsigned char *msg = 0;
419
420               XSync (dpy, False);
421               if (old_handler) abort();
422               old_handler = XSetErrorHandler (BadWindow_ehandler);
423               st2 = XGetWindowProperty (dpy, window,
424                                         XA_SCREENSAVER_RESPONSE,
425                                         0, 1024, True,
426                                         AnyPropertyType,
427                                         &type, &format, &nitems, &bytesafter,
428                                         &msg);
429               XSync (dpy, False);
430               XSetErrorHandler (old_handler);
431               old_handler = 0;
432
433               if (got_badwindow)
434                 {
435                   if (exiting_p)
436                     return 0;
437
438                   sprintf (err, "xscreensaver window unexpectedly deleted.");
439
440                   if (error_ret)
441                     *error_ret = strdup (err);
442                   else
443                     fprintf (stderr, "%s: %s\n", progname, err);
444
445                   return -1;
446                 }
447
448               if (st2 == Success && type != None)
449                 {
450                   if (type != XA_STRING || format != 8)
451                     {
452                       sprintf (err, "unrecognized response property.");
453
454                       if (error_ret)
455                         *error_ret = strdup (err);
456                       else
457                         fprintf (stderr, "%s: %s\n", progname, err);
458
459                       if (msg) XFree (msg);
460                       return -1;
461                     }
462                   else if (!msg || (msg[0] != '+' && msg[0] != '-'))
463                     {
464                       sprintf (err, "unrecognized response message.");
465
466                       if (error_ret)
467                         *error_ret = strdup (err);
468                       else  
469                         fprintf (stderr, "%s: %s\n", progname, err);
470
471                       if (msg) XFree (msg);
472                       return -1;
473                     }
474                   else
475                     {
476                       int ret = (msg[0] == '+' ? 0 : -1);
477                       sprintf (err, "%s: %s\n", progname, (char *) msg+1);
478
479                       if (error_ret)
480                         *error_ret = strdup (err);
481                       else if (verbose_p || ret != 0)
482                         fprintf ((ret < 0 ? stderr : stdout), "%s\n", err);
483
484                       XFree (msg);
485                       return ret;
486                     }
487                 }
488             }
489         }
490     }
491 }
492
493
494 int
495 xscreensaver_command (Display *dpy, Atom command, long arg, Bool verbose_p,
496                       char **error_ret)
497 {
498   Window w = 0;
499   int status = send_xscreensaver_command (dpy, command, arg, &w, error_ret);
500   if (status == 0)
501     status = xscreensaver_command_response (dpy, w, verbose_p,
502                                             (command == XA_EXIT),
503                                             error_ret);
504
505   fflush (stdout);
506   fflush (stderr);
507   return (status < 0 ? status : 0);
508 }
509
510
511 void
512 server_xscreensaver_version (Display *dpy,
513                              char **version_ret,
514                              char **user_ret,
515                              char **host_ret)
516 {
517   Window window = find_screensaver_window (dpy, 0);
518
519   Atom type;
520   int format;
521   unsigned long nitems, bytesafter;
522
523   if (version_ret)
524     *version_ret = 0;
525   if (user_ret)
526     *user_ret = 0;
527   if (host_ret)
528     *host_ret = 0;
529
530   if (!window)
531     return;
532
533   if (version_ret)
534     {
535       unsigned char *v = 0;
536       XGetWindowProperty (dpy, window, XA_SCREENSAVER_VERSION, 0, 1,
537                           False, XA_STRING, &type, &format, &nitems,
538                           &bytesafter, &v);
539       if (v)
540         {
541           *version_ret = strdup ((char *) v);
542           XFree (v);
543         }
544     }
545
546   if (user_ret || host_ret)
547     {
548       unsigned char *id = 0;
549       const char *user = 0;
550       const char *host = 0;
551
552       XGetWindowProperty (dpy, window, XA_SCREENSAVER_ID, 0, 512,
553                           False, XA_STRING, &type, &format, &nitems,
554                           &bytesafter, &id);
555       if (id && *id)
556         {
557           const char *old_tag = " on host ";
558           const char *s = strstr ((char *) id, old_tag);
559           if (s)
560             {
561               /* found ID of the form "1234 on host xyz". */
562               user = 0;
563               host = s + strlen (old_tag);
564             }
565           else
566             {
567               char *o = 0, *p = 0, *c = 0;
568               o = strchr ((char *) id, '(');
569               if (o) p = strchr (o, '@');
570               if (p) c = strchr (p, ')');
571               if (c)
572                 {
573                   /* found ID of the form "1234 (user@host)". */
574                   user = o+1;
575                   host = p+1;
576                   *p = 0;
577                   *c = 0;
578                 }
579             }
580
581         }
582
583       if (user && *user && *user != '?')
584         *user_ret = strdup (user);
585       else
586         *user_ret = 0;
587
588       if (host && *host && *host != '?')
589         *host_ret = strdup (host);
590       else
591         *host_ret = 0;
592
593       if (id)
594         XFree (id);
595     }
596 }