ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.tar.gz
[xscreensaver] / hacks / xscreensaver-sgigl.c
1 /* xscreensaver, Copyright (c) 1997 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* This is a kludge that lets xscreensaver work with SGI demos that expect
13    to be run from `haven'.  It runs the program given on the command line,
14    then waits for an X window to be created whose name is that of the 
15    program.  Then, it resizes that window to fill the screen.  Run it
16    like this:
17
18        xscreensaver-sgigl /usr/demos/bin/ep -S
19        xscreensaver-sgigl -n ant /usr/demos/General_Demos/ant/RUN
20        xscreensaver-sgigl -n atlantis /usr/demos/General_Demos/atlantis/RUN
21        xscreensaver-sgigl -n /usr/demos/General_Demos/powerflip/powerflip \
22           /usr/demos/General_Demos/powerflip/RUN
23
24    Except that doesn't really work.  You have to do this instead:
25
26        xscreensaver-sgigl -n ant ant.sh
27
28    where ant.sh contains
29
30        #!/bin/sh
31        cd /usr/demos/General_Demos/ant
32        exec ./ant -S
33
34    There's no way to make this work with powerflip at all, since it doesn't
35    take a -S option to run in the foreground.
36  */
37
38 /* #### Another way to do this would be:
39    instead of exec'ing the hack, fork it; then wait for that fork to die.
40    If it dies, but the window ID is still valid, then that means the 
41    sub-process has forked itself (as those fuckwits at SGI are wont to do.)
42    In that case, this process should go to sleep, and set up a signal handler
43    that will destroy the X window when it is killed.  That way, the caller
44    is given a foreground pid which, when killed, will cause the hack to die
45    (by a roundabout mechanism.)
46
47    This would all be so much simpler if those assholes would just:
48
49    1: get out of the habit of writing programs that magically background
50       themselves, and
51
52    2: give the fucking programs arguments which control the window size
53       instead of always making 100x100 windows!
54
55    I won't even dream of having a "-root" option that understood virtual-roots;
56    that would just be too outlandish to even dream about.
57  */
58
59 static char *progname;
60
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <signal.h>
65 #include <sys/time.h>
66 #include <X11/Xlib.h>
67 #include <X11/Xmu/Error.h>
68
69 #include "vroot.h"
70 #undef RootWindowOfScreen
71 #undef RootWindow
72 #undef DefaultRootWindow
73
74
75 static int
76 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
77 {
78   if (error->error_code == BadWindow ||
79       error->error_code == BadMatch ||
80       error->error_code == BadDrawable)
81     return 0;
82   else
83     {
84       fprintf (stderr, "\nX error in %s:\n", progname);
85       if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
86         exit(1);
87       else
88         fprintf (stderr, " (nonfatal.)\n");
89     }
90   return 0;
91 }
92
93
94 void
95 main(int ac, char **av)
96 {
97   char buf [512];
98   pid_t parent, forked;
99   Display *dpy;
100   Screen *screen;
101   char *s;
102   char *n1 = 0;
103   char *n2 = 0;
104   Bool verbose = False;
105   Window root, vroot;
106
107   progname = av[0];
108
109   s = strrchr(progname, '/');
110   if (s) progname = s+1;
111
112   if (ac < 1)
113     {
114       fprintf(stderr,
115               "usage: %s [ -v ] [ -n window-name ] program arguments...\n",
116               progname);
117       exit(1);
118     }
119
120   if (ac > 2 && !strcmp(av[1], "-v"))
121     {
122       verbose = True;
123       av++;
124       ac--;
125     }
126
127   if (ac > 2 && !strcmp(av[1], "-n"))
128     {
129       n2 = av[2];
130       av += 2;
131       ac -= 2;
132     }
133
134   n1 = strrchr(av[1], '/');
135   if (n1) n1++;
136   else n1 = av[1];
137
138
139   dpy = XOpenDisplay(0);
140   if (!dpy)
141     {
142       fprintf(stderr, "%s: couldn't open display\n", progname);
143       exit(1);
144     }
145
146   screen = DefaultScreenOfDisplay(dpy);
147   root   = XRootWindowOfScreen (screen);
148   vroot  = VirtualRootWindowOfScreen (screen);
149
150   XSelectInput (dpy, root, SubstructureNotifyMask);
151   if (root != vroot)
152     XSelectInput (dpy, vroot, SubstructureNotifyMask);
153
154   XSetErrorHandler (BadWindow_ehandler);
155
156   if (verbose)
157     fprintf(stderr, "%s: selected SubstructureNotifyMask on 0x%x / 0x%x\n",
158             progname, root, vroot);
159
160   parent = getpid();
161
162   if (verbose)
163     fprintf(stderr, "%s: pid is %d\n", progname, parent);
164
165   switch ((int) (forked = fork ()))
166     {
167     case -1:
168       {
169         sprintf (buf, "%s: couldn't fork", progname);
170         perror (buf);
171         exit (1);
172         break;
173       }
174     case 0:     /* forked */
175       {
176         time_t start = time((time_t) 0);
177         XEvent event;
178
179         if (verbose)
180           fprintf(stderr, "%s: forked pid is %d\n", progname, getpid());
181
182         while (1)
183           {
184             XNextEvent(dpy, &event);
185
186             if (event.xany.type == CreateNotify)
187               {
188                 char *name = 0;
189                 Window w = event.xcreatewindow.window;
190                 XSync(dpy, False);
191
192                 XFetchName(dpy, w, &name);
193                 if (!name)
194                   {
195                     /* Try again to see if the name has been set later... */
196                     XSync(dpy, False);
197                     sleep(1);
198                     XFetchName(dpy, w, &name);
199                   }
200
201                 if (name &&
202                     ((n1 && !strcmp(name, n1)) ||
203                      (n2 && !strcmp(name, n2))))
204                   {
205                     if (verbose)
206                       fprintf(stderr, "%s: resizing 0x%x\n", progname, w);
207
208                     XMoveResizeWindow(dpy, w, 0, 0,
209                                       WidthOfScreen(screen),
210                                       HeightOfScreen(screen));
211
212 #if 0
213                     if (vroot && vroot != root &&
214                         event.xcreatewindow.parent == root)
215                       {
216                         if (verbose)
217                           fprintf(stderr,
218                                   "%s: reparenting 0x%x from 0x%x to 0x%x\n",
219                                   progname, w, root, vroot);
220                         XReparentWindow(dpy, w, vroot, 0, 0);
221                       }
222 #endif
223
224                     XSync(dpy, False);
225                     fflush(stdout);
226                     fflush(stderr);
227                     exit(0);    /* Note that this only exits a child fork.  */
228                   }
229               }
230
231             if (start + 5 < time((time_t) 0))
232               {
233                 fprintf(stderr,
234                     "%s: timed out: no window named \"%s\" has been created\n",
235                         progname, (n2 ? n2 : n1));
236                 fflush(stdout);
237                 fflush(stderr);
238                 kill(parent, SIGTERM);
239                 exit(1);
240               }
241           }
242         break;
243       }
244     default:    /* foreground */
245       {
246         close (ConnectionNumber (dpy));         /* close display fd */
247         execvp (av[1], av+1);                   /* shouldn't return. */
248         sprintf (buf, "%s: execvp(\"%s\") failed", progname, av[1]);
249         perror (buf);
250         fflush(stderr);
251         fflush(stdout);
252         exit (1);
253         break;
254       }
255     }
256 }