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