/* xscreensaver, Copyright (c) 1997, 2005 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
/* This is a kludge that lets xscreensaver work with SGI demos that expect
to be run from `haven'. It runs the program given on the command line,
then waits for an X window to be created whose name is that of the
program. Then, it resizes that window to fill the screen. Run it
like this:
xscreensaver-sgigl /usr/demos/bin/ep -S
xscreensaver-sgigl -n ant /usr/demos/General_Demos/ant/RUN
xscreensaver-sgigl -n atlantis /usr/demos/General_Demos/atlantis/RUN
xscreensaver-sgigl -n /usr/demos/General_Demos/powerflip/powerflip \
/usr/demos/General_Demos/powerflip/RUN
Except that doesn't really work. You have to do this instead:
xscreensaver-sgigl -n ant ant.sh
where ant.sh contains
#!/bin/sh
cd /usr/demos/General_Demos/ant
exec ./ant -S
There's no way to make this work with powerflip at all, since it doesn't
take a -S option to run in the foreground.
*/
/* #### Another way to do this would be:
instead of exec'ing the hack, fork it; then wait for that fork to die.
If it dies, but the window ID is still valid, then that means the
sub-process has forked itself (as those fuckwits at SGI are wont to do.)
In that case, this process should go to sleep, and set up a signal handler
that will destroy the X window when it is killed. That way, the caller
is given a foreground pid which, when killed, will cause the hack to die
(by a roundabout mechanism.)
This would all be so much simpler if those assholes would just:
1: get out of the habit of writing programs that magically background
themselves, and
2: give the fucking programs arguments which control the window size
instead of always making 100x100 windows!
I won't even dream of having a "-root" option that understood virtual-roots;
that would just be too outlandish to even dream about.
*/
static char *progname;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xmu/Error.h>
#include "vroot.h"
#undef RootWindowOfScreen
#undef RootWindow
#undef DefaultRootWindow
static int
BadWindow_ehandler (Display *dpy, XErrorEvent *error)
{
if (error->error_code == BadWindow ||
error->error_code == BadMatch ||
error->error_code == BadDrawable)
return 0;
else
{
fprintf (stderr, "\nX error in %s:\n", progname);
if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
exit(1);
else
fprintf (stderr, " (nonfatal.)\n");
}
return 0;
}
void
main(int ac, char **av)
{
char buf [512];
pid_t parent, forked;
Display *dpy;
Screen *screen;
char *s;
char *n1 = 0;
char *n2 = 0;
Bool verbose = False;
Window root, vroot;
XSizeHints h;
long ls;
progname = av[0];
s = strrchr(progname, '/');
if (s) progname = s+1;
if (ac < 1)
{
fprintf(stderr,
"usage: %s [ -v ] [ -n window-name ] program arguments...\n",
progname);
exit(1);
}
if (ac > 2 && !strcmp(av[1], "-v"))
{
verbose = True;
av++;
ac--;
}
if (ac > 2 && !strcmp(av[1], "-n"))
{
n2 = av[2];
av += 2;
ac -= 2;
}
n1 = strrchr(av[1], '/');
if (n1) n1++;
else n1 = av[1];
dpy = XOpenDisplay(0);
if (!dpy)
{
fprintf(stderr, "%s: couldn't open display\n", progname);
exit(1);
}
screen = DefaultScreenOfDisplay(dpy);
root = XRootWindowOfScreen (screen);
vroot = VirtualRootWindowOfScreen (screen);
XSelectInput (dpy, root, SubstructureNotifyMask);
if (root != vroot)
XSelectInput (dpy, vroot, SubstructureNotifyMask);
XSetErrorHandler (BadWindow_ehandler);
if (verbose)
fprintf(stderr, "%s: selected SubstructureNotifyMask on 0x%x / 0x%x\n",
progname, root, vroot);
parent = getpid();
if (verbose)
fprintf(stderr, "%s: pid is %d\n", progname, parent);
switch ((int) (forked = fork ()))
{
case -1:
{
sprintf (buf, "%s: couldn't fork", progname);
perror (buf);
exit (1);
break;
}
case 0: /* forked */
{
time_t start = time((time_t) 0);
XEvent event;
if (verbose)
fprintf(stderr, "%s: forked pid is %d\n", progname, getpid());
while (1)
{
XNextEvent(dpy, &event);
if (event.xany.type == CreateNotify)
{
char *name = 0;
Window w = event.xcreatewindow.window;
XSync(dpy, False);
XFetchName(dpy, w, &name);
if (!name)
{
/* Try again to see if the name has been set later... */
XSync(dpy, False);
sleep(1);
XFetchName(dpy, w, &name);
}
if (name &&
((n1 && !strcmp(name, n1)) ||
(n2 && !strcmp(name, n2))))
{
if (verbose)
fprintf(stderr, "%s: resizing 0x%x\n", progname, w);
/* Make sure the window allows itself to be resized. */
XGetWMNormalHints (dpy, w, &h, &ls);
h.flags |= PMaxSize;
h.max_width = WidthOfScreen(screen)+128;
h.max_height = HeightOfScreen(screen)+128;
XSetWMNormalHints (dpy, w, &h);
XMoveResizeWindow(dpy, w, 0, 0,
WidthOfScreen(screen),
HeightOfScreen(screen));
#if 0
if (vroot && vroot != root &&
event.xcreatewindow.parent == root)
{
if (verbose)
fprintf(stderr,
"%s: reparenting 0x%x from 0x%x to 0x%x\n",
progname, w, root, vroot);
XReparentWindow(dpy, w, vroot, 0, 0);
}
#endif
XSync(dpy, False);
fflush(stdout);
fflush(stderr);
exit(0); /* Note that this only exits a child fork. */
}
}
if (start + 5 < time((time_t) 0))
{
fprintf(stderr,
"%s: timed out: no window named \"%s\" has been created\n",
progname, (n2 ? n2 : n1));
fflush(stdout);
fflush(stderr);
kill(parent, SIGTERM);
exit(1);
}
}
break;
}
default: /* foreground */
{
close (ConnectionNumber (dpy)); /* close display fd */
execvp (av[1], av+1); /* shouldn't return. */
sprintf (buf, "%s: execvp(\"%s\") failed", progname, av[1]);
perror (buf);
fflush(stderr);
fflush(stdout);
exit (1);
break;
}
}
}