-/* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
- * Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1992-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
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xutil.h>
+#include <X11/keysym.h>
#ifdef __sgi
# include <X11/SGIScheme.h> /* for SgiUseSchemes() */
#include "version.h"
#include "vroot.h"
+#ifndef _XSCREENSAVER_VROOT_H_
+# error Error! You have an old version of vroot.h! Check -I args.
+#endif /* _XSCREENSAVER_VROOT_H_ */
+
+#ifndef isupper
+# define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c) ((c) - 'A' + 'a')
+#endif
+
+
char *progname;
XrmDatabase db;
+XtAppContext app;
Bool mono_p;
static XrmOptionDescRec default_options [] = {
"*installColormap: false",
"*visualID: default",
"*windowID: ",
+ "*desktopGrabber: xscreensaver-getimage %s",
0
};
}
-#ifdef USE_GL
-extern Visual *get_gl_visual (Screen *, const char *, const char *);
-#endif
-
#ifdef XLOCKMORE
extern void pre_merge_options (void);
#endif
void
screenhack_handle_event (Display *dpy, XEvent *event)
{
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
switch (event->xany.type)
{
case KeyPress:
c == 3 || /* ^C */
c == 27) /* ESC */
exit (0);
+ else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
+ XBell (dpy, 0); /* beep for non-chord keys */
}
+ break;
case ButtonPress:
XBell (dpy, 0);
break;
}
+static Visual *
+pick_visual (Screen *screen)
+{
+#ifdef USE_GL
+ /* If we're linking against GL (that is, this is the version of screenhack.o
+ that the GL hacks will use, which is different from the one that the
+ non-GL hacks will use) then try to pick the "best" visual by interrogating
+ the GL library instead of by asking Xlib. GL knows better.
+ */
+ Visual *v = 0;
+ char *string = get_string_resource ("visualID", "VisualID");
+ char *s;
+
+ if (string)
+ for (s = string; *s; s++)
+ if (isupper (*s)) *s = _tolower (*s);
+
+ if (!string || !*string ||
+ !strcmp (string, "gl") ||
+ !strcmp (string, "best") ||
+ !strcmp (string, "color") ||
+ !strcmp (string, "default"))
+ v = get_gl_visual (screen); /* from ../utils/visual-gl.c */
+
+ if (string)
+ free (string);
+ if (v)
+ return v;
+#endif /* USE_GL */
+
+ return get_visual_resource (screen, "visualID", "VisualID", False);
+}
+
+
+/* Notice when the user has requested a different visual or colormap
+ on a pre-existing window (e.g., "-root -visual truecolor" or
+ "-window-id 0x2c00001 -install") and complain, since when drawing
+ on an existing window, we have no choice about these things.
+ */
+static void
+visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
+ Bool window_p)
+{
+ char *visual_string = get_string_resource ("visualID", "VisualID");
+ Visual *desired_visual = pick_visual (screen);
+ char win[100];
+ char why[100];
+
+ if (window == RootWindowOfScreen (screen))
+ strcpy (win, "root window");
+ else
+ sprintf (win, "window 0x%lx", (unsigned long) window);
+
+ if (window_p)
+ sprintf (why, "-window-id 0x%lx", (unsigned long) window);
+ else
+ strcpy (why, "-root");
+
+ if (visual_string && *visual_string)
+ {
+ char *s;
+ for (s = visual_string; *s; s++)
+ if (isupper (*s)) *s = _tolower (*s);
+
+ if (!strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "best"))
+ /* don't warn about these, just silently DWIM. */
+ ;
+ else if (visual != desired_visual)
+ {
+ fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
+ progname, visual_string, why);
+ fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
+ progname, win, XVisualIDFromVisual (visual));
+ }
+ free (visual_string);
+ }
+
+ if (visual == DefaultVisualOfScreen (screen) &&
+ has_writable_cells (screen, visual) &&
+ get_boolean_resource ("installColormap", "InstallColormap"))
+ {
+ fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
+ progname, why);
+ fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
+ progname, win, (unsigned long) cmap);
+ }
+
+# ifdef USE_GL
+ if (!validate_gl_visual (stderr, screen, win, visual))
+ exit (1);
+# endif /* USE_GL */
+}
+
+
+static void
+fix_fds (void)
+{
+ /* Bad Things Happen if stdin, stdout, and stderr have been closed
+ (as by the `sh incantation "attraction >&- 2>&-"). When you do
+ that, the X connection gets allocated to one of these fds, and
+ then some random library writes to stderr, and random bits get
+ stuffed down the X pipe, causing "Xlib: sequence lost" errors.
+ So, we cause the first three file descriptors to be open to
+ /dev/null if they aren't open to something else already. This
+ must be done before any other files are opened (or the closing
+ of that other file will again free up one of the "magic" first
+ three FDs.)
+
+ We do this by opening /dev/null three times, and then closing
+ those fds, *unless* any of them got allocated as #0, #1, or #2,
+ in which case we leave them open. Gag.
+
+ Really, this crap is technically required of *every* X program,
+ if you want it to be robust in the face of "2>&-".
+ */
+ int fd0 = open ("/dev/null", O_RDWR);
+ int fd1 = open ("/dev/null", O_RDWR);
+ int fd2 = open ("/dev/null", O_RDWR);
+ if (fd0 > 2) close (fd0);
+ if (fd1 > 2) close (fd1);
+ if (fd2 > 2) close (fd2);
+}
+
int
main (int argc, char **argv)
{
- XtAppContext app;
Widget toplevel;
Display *dpy;
Window window;
+ Screen *screen;
Visual *visual;
Colormap cmap;
Bool root_p;
Boolean dont_clear /*, dont_map */;
char version[255];
+ fix_fds();
+
+ progname = argv[0]; /* reset later */
+
#ifdef XLOCKMORE
pre_merge_options ();
#endif
merged_options_size, &argc, argv,
merged_defaults, 0, 0);
dpy = XtDisplay (toplevel);
+ screen = XtScreen (toplevel);
db = XtDatabase (dpy);
+
XtGetApplicationNameAndClass (dpy, &progname, &progclass);
+
+ /* half-assed way of avoiding buffer-overrun attacks. */
+ if (strlen (progname) >= 100) progname[100] = 0;
+
XSetErrorHandler (screenhack_ehandler);
XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
int i;
int x = 18;
int end = 78;
- Bool help_p = !strcmp(argv[1], "-help");
+ Bool help_p = (!strcmp(argv[1], "-help") ||
+ !strcmp(argv[1], "--help"));
fprintf (stderr, "%s\n", version);
for (s = progclass; *s; s++) fprintf(stderr, " ");
fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
if (argp) fprintf (stderr, " <arg>");
if (i != merged_options_size - 1) fprintf (stderr, ", ");
}
+
fprintf (stderr, ".\n");
+
+#if 0
+ if (help_p)
+ {
+ fprintf (stderr, "\nResources:\n\n");
+ for (i = 0; i < merged_options_size; i++)
+ {
+ const char *opt = merged_options [i].option;
+ const char *res = merged_options [i].specifier + 1;
+ const char *val = merged_options [i].value;
+ char *s = get_string_resource ((char *) res, (char *) res);
+
+ if (s)
+ {
+ int L = strlen(s);
+ while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
+ s[--L] = 0;
+ }
+
+ fprintf (stderr, " %-16s %-18s ", opt, res);
+ if (merged_options [i].argKind == XrmoptionSepArg)
+ {
+ fprintf (stderr, "[%s]", (s ? s : "?"));
+ }
+ else
+ {
+ fprintf (stderr, "%s", (val ? val : "(null)"));
+ if (val && s && !strcasecmp (val, s))
+ fprintf (stderr, " [default]");
+ }
+ fprintf (stderr, "\n");
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
exit (help_p ? 0 : 1);
}
XGetWindowAttributes (dpy, window, &xgwa);
cmap = xgwa.colormap;
visual = xgwa.visual;
+ screen = xgwa.screen;
+ visual_warning (screen, window, visual, cmap, True);
+
+ /* Select KeyPress and resize events on the external window.
+ */
+ xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
+ XSelectInput (dpy, window, xgwa.your_event_mask);
+
+ /* Select ButtonPress and ButtonRelease events on the external window,
+ if no other app has already selected them (only one app can select
+ ButtonPress at a time: BadAccess results.)
+ */
+ if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
+ XSelectInput (dpy, window,
+ (xgwa.your_event_mask |
+ ButtonPressMask | ButtonReleaseMask));
}
else if (root_p)
{
XWindowAttributes xgwa;
- window = RootWindowOfScreen (XtScreen (toplevel));
+ window = VirtualRootWindowOfScreen (XtScreen (toplevel));
XtDestroyWidget (toplevel);
XGetWindowAttributes (dpy, window, &xgwa);
cmap = xgwa.colormap;
visual = xgwa.visual;
+ visual_warning (screen, window, visual, cmap, False);
}
else
{
Boolean def_visual_p;
- Screen *screen = XtScreen (toplevel);
+ visual = pick_visual (screen);
-#ifdef USE_GL
- visual = get_gl_visual (screen, "visualID", "VisualID");
-#else
- visual = get_visual_resource (screen, "visualID", "VisualID", False);
-#endif
+# ifdef USE_GL
+ if (!validate_gl_visual (stderr, screen, "window", visual))
+ exit (1);
+# endif /* USE_GL */
if (toplevel->core.width <= 0)
toplevel->core.width = 600;
unsigned int bg, bd;
Widget new;
- cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
+ cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
visual, AllocNone);
bg = get_pixel_resource ("background", "Background", dpy, cmap);
bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
XtNcolormap, cmap,
XtNbackground, (Pixel) bg,
XtNborderColor, (Pixel) bd,
- 0);
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
XtDestroyWidget (toplevel);
toplevel = new;
XtRealizeWidget (toplevel);
}
else
{
- XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
+ XtVaSetValues (toplevel,
+ XtNmappedWhenManaged, False,
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
XtRealizeWidget (toplevel);
window = XtWindow (toplevel);
/*
if (dont_map)
{
- XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
+ XtVaSetValues (toplevel, XtNmappedWhenManaged, False, NULL);
XtRealizeWidget (toplevel);
}
else
XtPopup (toplevel, XtGrabNone);
}
- XtVaSetValues(toplevel, XtNtitle, version, 0);
+ XtVaSetValues(toplevel, XtNtitle, version, NULL);
/* For screenhack_handle_events(): select KeyPress, and
announce that we accept WM_DELETE_WINDOW. */
XWindowAttributes xgwa;
XGetWindowAttributes (dpy, window, &xgwa);
XSelectInput (dpy, window,
- xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
+ (xgwa.your_event_mask | KeyPressMask |
+ ButtonPressMask | ButtonReleaseMask));
XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
PropModeReplace,
(unsigned char *) &XA_WM_DELETE_WINDOW, 1);
XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
XSync (dpy, False);
- srandom ((int) time ((time_t *) 0));
+
+ /* This is the one and only place that the random-number generator is
+ seeded in any screenhack. You do not need to seed the RNG again,
+ it is done for you before your code is invoked. */
+# undef ya_rand_init
+ ya_rand_init (0);
+
screenhack (dpy, window); /* doesn't return */
return 0;
}