http://www.jwz.org/xscreensaver/xscreensaver-5.10.tar.gz
[xscreensaver] / hacks / xscreensaver-sgigl.c
1 /* xscreensaver, Copyright (c) 1997, 2005 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/Xutil.h>
68 #include <X11/Xmu/Error.h>
69
70 #include "vroot.h"
71 #undef RootWindowOfScreen
72 #undef RootWindow
73 #undef DefaultRootWindow
74
75
76 static int
77 BadWindow_ehandler (Display *dpy, XErrorEvent *error)
78 {
79   if (error->error_code == BadWindow ||
80       error->error_code == BadMatch ||
81       error->error_code == BadDrawable)
82     return 0;
83   else
84     {
85       fprintf (stderr, "\nX error in %s:\n", progname);
86       if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
87         exit(1);
88       else
89         fprintf (stderr, " (nonfatal.)\n");
90     }
91   return 0;
92 }
93
94
95 void
96 main(int ac, char **av)
97 {
98   char buf [512];
99   pid_t parent, forked;
100   Display *dpy;
101   Screen *screen;
102   char *s;
103   char *n1 = 0;
104   char *n2 = 0;
105   Bool verbose = False;
106   Window root, vroot;
107   XSizeHints h;
108   long ls;
109
110   progname = av[0];
111
112   s = strrchr(progname, '/');
113   if (s) progname = s+1;
114
115   if (ac < 1)
116     {
117       fprintf(stderr,
118               "usage: %s [ -v ] [ -n window-name ] program arguments...\n",
119               progname);
120       exit(1);
121     }
122
123   if (ac > 2 && !strcmp(av[1], "-v"))
124     {
125       verbose = True;
126       av++;
127       ac--;
128     }
129
130   if (ac > 2 && !strcmp(av[1], "-n"))
131     {
132       n2 = av[2];
133       av += 2;
134       ac -= 2;
135     }
136
137   n1 = strrchr(av[1], '/');
138   if (n1) n1++;
139   else n1 = av[1];
140
141
142   dpy = XOpenDisplay(0);
143   if (!dpy)
144     {
145       fprintf(stderr, "%s: couldn't open display\n", progname);
146       exit(1);
147     }
148
149   screen = DefaultScreenOfDisplay(dpy);
150   root   = XRootWindowOfScreen (screen);
151   vroot  = VirtualRootWindowOfScreen (screen);
152
153   XSelectInput (dpy, root, SubstructureNotifyMask);
154   if (root != vroot)
155     XSelectInput (dpy, vroot, SubstructureNotifyMask);
156
157   XSetErrorHandler (BadWindow_ehandler);
158
159   if (verbose)
160     fprintf(stderr, "%s: selected SubstructureNotifyMask on 0x%x / 0x%x\n",
161             progname, root, vroot);
162
163   parent = getpid();
164
165   if (verbose)
166     fprintf(stderr, "%s: pid is %d\n", progname, parent);
167
168   switch ((int) (forked = fork ()))
169     {
170     case -1:
171       {
172         sprintf (buf, "%s: couldn't fork", progname);
173         perror (buf);
174         exit (1);
175         break;
176       }
177     case 0:     /* forked */
178       {
179         time_t start = time((time_t) 0);
180         XEvent event;
181
182         if (verbose)
183           fprintf(stderr, "%s: forked pid is %d\n", progname, getpid());
184
185         while (1)
186           {
187             XNextEvent(dpy, &event);
188
189             if (event.xany.type == CreateNotify)
190               {
191                 char *name = 0;
192                 Window w = event.xcreatewindow.window;
193                 XSync(dpy, False);
194
195                 XFetchName(dpy, w, &name);
196                 if (!name)
197                   {
198                     /* Try again to see if the name has been set later... */
199                     XSync(dpy, False);
200                     sleep(1);
201                     XFetchName(dpy, w, &name);
202                   }
203
204                 if (name &&
205                     ((n1 && !strcmp(name, n1)) ||
206                      (n2 && !strcmp(name, n2))))
207                   {
208                     if (verbose)
209                       fprintf(stderr, "%s: resizing 0x%x\n", progname, w);
210
211                     /* Make sure the window allows itself to be resized. */
212                     XGetWMNormalHints (dpy, w, &h, &ls);
213                     h.flags |= PMaxSize;
214                     h.max_width = WidthOfScreen(screen)+128;
215                     h.max_height = HeightOfScreen(screen)+128;
216                     XSetWMNormalHints (dpy, w, &h);
217
218                     XMoveResizeWindow(dpy, w, 0, 0,
219                                       WidthOfScreen(screen),
220                                       HeightOfScreen(screen));
221
222 #if 0
223                     if (vroot && vroot != root &&
224                         event.xcreatewindow.parent == root)
225                       {
226                         if (verbose)
227                           fprintf(stderr,
228                                   "%s: reparenting 0x%x from 0x%x to 0x%x\n",
229                                   progname, w, root, vroot);
230                         XReparentWindow(dpy, w, vroot, 0, 0);
231                       }
232 #endif
233
234                     XSync(dpy, False);
235                     fflush(stdout);
236                     fflush(stderr);
237                     exit(0);    /* Note that this only exits a child fork.  */
238                   }
239               }
240
241             if (start + 5 < time((time_t) 0))
242               {
243                 fprintf(stderr,
244                     "%s: timed out: no window named \"%s\" has been created\n",
245                         progname, (n2 ? n2 : n1));
246                 fflush(stdout);
247                 fflush(stderr);
248                 kill(parent, SIGTERM);
249                 exit(1);
250               }
251           }
252         break;
253       }
254     default:    /* foreground */
255       {
256         close (ConnectionNumber (dpy));         /* close display fd */
257         execvp (av[1], av+1);                   /* shouldn't return. */
258         sprintf (buf, "%s: execvp(\"%s\") failed", progname, av[1]);
259         perror (buf);
260         fflush(stderr);
261         fflush(stdout);
262         exit (1);
263         break;
264       }
265     }
266 }