X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fscreenhack.c;h=728d18edabbb7b6b4ed25f0899faa8055b2a4a56;hp=12802ffae5828c4f4b1f1a622e99e62ff72efefe;hb=ffd8c0873576a9e3065696a624dce6b766b77062;hpb=0cac953ce8d5139c1a264b417951ee15a3176b51 diff --git a/hacks/screenhack.c b/hacks/screenhack.c index 12802ffa..728d18ed 100644 --- a/hacks/screenhack.c +++ b/hacks/screenhack.c @@ -1,4 +1,5 @@ -/* xscreensaver, Copyright (c) 1992 Jamie Zawinski +/* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 2001, 2002, 2003, 2004 + * Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -21,40 +22,77 @@ - create a variable `char *progclass' which names this program's resource class. - - create a variable `char defaults []' for the default resources. + - create a variable `char defaults []' for the default resources, and + null-terminate it. - - create a variable `XrmOptionDescRec options []' for the command-line, - and `int options_size' which is `XtNumber (options)'. + - create a variable `XrmOptionDescRec options[]' for the command-line, + and null-terminate it. And that's it... */ -#include "version.h" - #include #include #include #include -#include +#include +#include +#include +#include + +#ifdef __sgi +# include /* for SgiUseSchemes() */ +#endif /* __sgi */ + +#ifdef HAVE_XMU +# ifndef VMS +# include +# else /* VMS */ +# include +# endif +#else +# include "xmu.h" +#endif #include "screenhack.h" +#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 [] = { { "-root", ".root", XrmoptionNoArg, "True" }, { "-window", ".root", XrmoptionNoArg, "False" }, { "-mono", ".mono", XrmoptionNoArg, "True" }, - { "-install", ".installColormap", XrmoptionNoArg, "True" } + { "-install", ".installColormap", XrmoptionNoArg, "True" }, + { "-noinstall",".installColormap", XrmoptionNoArg, "False" }, + { "-visual", ".visualID", XrmoptionSepArg, 0 }, + { "-window-id", ".windowID", XrmoptionSepArg, 0 }, + { 0, 0, 0, 0 } }; static char *default_defaults[] = { - "*root: false", - "*geometry: 500x500", /* this should be .geometry, but nooooo... */ + ".root: false", + "*geometry: 600x480", /* this should be .geometry, but nooooo... */ "*mono: false", "*installColormap: false", + "*visualID: default", + "*windowID: ", + "*desktopGrabber: xscreensaver-getimage %s", 0 }; @@ -63,24 +101,53 @@ static int merged_options_size; static char **merged_defaults; static void -merge_options () +merge_options (void) { - int options_sizeof = options_size * sizeof (options[0]); - int defaults_size; - merged_options_size = XtNumber (default_options) + options_size; + int def_opts_size, opts_size; + int def_defaults_size, defaults_size; + + for (def_opts_size = 0; default_options[def_opts_size].option; + def_opts_size++) + ; + for (opts_size = 0; options[opts_size].option; opts_size++) + ; + + merged_options_size = def_opts_size + opts_size; merged_options = (XrmOptionDescRec *) - malloc (sizeof (default_options) + options_sizeof); - memcpy (merged_options, options, options_sizeof); - memcpy (merged_options + options_size, default_options, - sizeof (default_options)); + malloc ((merged_options_size + 1) * sizeof(*default_options)); + memcpy (merged_options, default_options, + (def_opts_size * sizeof(*default_options))); + memcpy (merged_options + def_opts_size, options, + ((opts_size + 1) * sizeof(*default_options))); - for (defaults_size = 0; defaults [defaults_size]; defaults_size++); + for (def_defaults_size = 0; default_defaults[def_defaults_size]; + def_defaults_size++) + ; + for (defaults_size = 0; defaults[defaults_size]; defaults_size++) + ; merged_defaults = (char **) - malloc (sizeof (default_defaults) + (defaults_size * sizeof (char *))); - memcpy (merged_defaults, default_defaults, sizeof (default_defaults)); - memcpy ((merged_defaults - 1 + - (sizeof (default_defaults) / sizeof (default_defaults[0]))), - defaults, ((defaults_size + 1) * sizeof (defaults[0]))); + malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));; + memcpy (merged_defaults, default_defaults, + def_defaults_size * sizeof(*defaults)); + memcpy (merged_defaults + def_defaults_size, defaults, + (defaults_size + 1) * sizeof(*defaults)); + + /* This totally sucks. Xt should behave like this by default. + If the string in `defaults' looks like ".foo", change that + to "Progclass.foo". + */ + { + char **s; + for (s = merged_defaults; *s; s++) + if (**s == '.') + { + const char *oldr = *s; + char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3); + strcpy (newr, progclass); + strcat (newr, oldr); + *s = newr; + } + } } @@ -89,9 +156,7 @@ merge_options () */ static int -screenhack_ehandler (dpy, error) - Display *dpy; - XErrorEvent *error; +screenhack_ehandler (Display *dpy, XErrorEvent *error) { fprintf (stderr, "\nX error in %s:\n", progname); if (XmuPrintDefaultErrorMessage (dpy, error, stderr)) @@ -102,43 +167,293 @@ screenhack_ehandler (dpy, error) } static Bool -MapNotify_event_p (dpy, event, window) - Display *dpy; - XEvent *event; - XPointer window; +MapNotify_event_p (Display *dpy, XEvent *event, XPointer window) { return (event->xany.type == MapNotify && event->xvisibility.window == (Window) window); } +#ifdef XLOCKMORE +extern void pre_merge_options (void); +#endif + + +static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW; + +/* Dead-trivial event handling: exits if "q" or "ESC" are typed. + Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received. + */ +void +screenhack_handle_event (Display *dpy, XEvent *event) +{ + if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) + XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); + + switch (event->xany.type) + { + case KeyPress: + { + KeySym keysym; + char c = 0; + XLookupString (&event->xkey, &c, 1, &keysym, 0); + if (c == 'q' || + c == 'Q' || + 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; + case ClientMessage: + { + if (event->xclient.message_type != XA_WM_PROTOCOLS) + { + char *s = XGetAtomName(dpy, event->xclient.message_type); + if (!s) s = "(null)"; + fprintf (stderr, "%s: unknown ClientMessage %s received!\n", + progname, s); + } + else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW) + { + char *s1 = XGetAtomName(dpy, event->xclient.message_type); + char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]); + if (!s1) s1 = "(null)"; + if (!s2) s2 = "(null)"; + fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n", + progname, s1, s2); + } + else + { + exit (0); + } + } + break; + } +} + + void -main (argc, argv) - int argc; - char **argv; +screenhack_handle_events (Display *dpy) +{ + while (XPending (dpy)) + { + XEvent event; + XNextEvent (dpy, &event); + screenhack_handle_event (dpy, &event); + } +} + + +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; + Window on_window = 0; XEvent event; + Boolean dont_clear /*, dont_map */; + char version[255]; + + fix_fds(); +#ifdef XLOCKMORE + pre_merge_options (); +#endif merge_options (); + +#ifdef __sgi + /* We have to do this on SGI to prevent the background color from being + overridden by the current desktop color scheme (we'd like our backgrounds + to be black, thanks.) This should be the same as setting the + "*useSchemes: none" resource, but it's not -- if that resource is + present in the `default_defaults' above, it doesn't work, though it + does work when passed as an -xrm arg on the command line. So screw it, + turn them off from C instead. + */ + SgiUseSchemes ("none"); +#endif /* __sgi */ + toplevel = XtAppInitialize (&app, progclass, merged_options, 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); + XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False); + + { + char *v = (char *) strdup(strchr(screensaver_id, ' ')); + char *s1, *s2, *s3, *s4; + s1 = (char *) strchr(v, ' '); s1++; + s2 = (char *) strchr(s1, ' '); + s3 = (char *) strchr(v, '('); s3++; + s4 = (char *) strchr(s3, ')'); + *s2 = 0; + *s4 = 0; + sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)", + progclass, s1, s3); + free(v); + } + if (argc > 1) { + const char *s; int i; int x = 18; int end = 78; - fprintf (stderr, "%s: unrecognised option \"%s\"\n", progname, argv[1]); + 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 (!help_p) + fprintf(stderr, "Unrecognised option: %s\n", argv[1]); fprintf (stderr, "Options include: "); for (i = 0; i < merged_options_size; i++) { @@ -155,38 +470,192 @@ main (argc, argv) if (argp) fprintf (stderr, " "); if (i != merged_options_size - 1) fprintf (stderr, ", "); } + fprintf (stderr, ".\n"); - exit (1); + +#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); } + dont_clear = get_boolean_resource ("dontClearRoot", "Boolean"); +/*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */ mono_p = get_boolean_resource ("mono", "Boolean"); if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2) mono_p = True; root_p = get_boolean_resource ("root", "Boolean"); - if (root_p) + + { + char *s = get_string_resource ("windowID", "WindowID"); + if (s && *s) + on_window = get_integer_resource ("windowID", "WindowID"); + if (s) free (s); + } + + if (on_window) { XWindowAttributes xgwa; - window = RootWindowOfScreen (XtScreen (toplevel)); + window = (Window) on_window; XtDestroyWidget (toplevel); 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 = VirtualRootWindowOfScreen (XtScreen (toplevel)); + XtDestroyWidget (toplevel); + XGetWindowAttributes (dpy, window, &xgwa); + cmap = xgwa.colormap; + visual = xgwa.visual; + visual_warning (screen, window, visual, cmap, False); } else { - XtRealizeWidget (toplevel); - window = XtWindow (toplevel); - if (get_boolean_resource ("installColormap", "InstallColormap")) + Boolean def_visual_p; + visual = pick_visual (screen); + +# 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; + if (toplevel->core.height <= 0) + toplevel->core.height = 480; + + def_visual_p = (visual == DefaultVisualOfScreen (screen)); + + if (!def_visual_p) + { + unsigned int bg, bd; + Widget new; + + cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen), + visual, AllocNone); + bg = get_pixel_resource ("background", "Background", dpy, cmap); + bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap); + + new = XtVaAppCreateShell (progname, progclass, + topLevelShellWidgetClass, dpy, + XtNmappedWhenManaged, False, + XtNvisual, visual, + XtNdepth, visual_depth (screen, visual), + XtNwidth, toplevel->core.width, + XtNheight, toplevel->core.height, + XtNcolormap, cmap, + XtNbackground, (Pixel) bg, + XtNborderColor, (Pixel) bd, + XtNinput, True, /* for WM_HINTS */ + NULL); + XtDestroyWidget (toplevel); + toplevel = new; + XtRealizeWidget (toplevel); + window = XtWindow (toplevel); + } + else + { + XtVaSetValues (toplevel, + XtNmappedWhenManaged, False, + XtNinput, True, /* for WM_HINTS */ + NULL); + XtRealizeWidget (toplevel); + window = XtWindow (toplevel); + + if (get_boolean_resource ("installColormap", "InstallColormap")) + { + cmap = XCreateColormap (dpy, window, + DefaultVisualOfScreen (XtScreen (toplevel)), + AllocNone); + XSetWindowColormap (dpy, window, cmap); + } + else + { + cmap = DefaultColormap (dpy, DefaultScreen (dpy)); + } + } + +/* + if (dont_map) { - cmap = XCreateColormap (dpy, window, - DefaultVisualOfScreen (XtScreen (toplevel)), - AllocNone); - XSetWindowColormap (dpy, window, cmap); + XtVaSetValues (toplevel, XtNmappedWhenManaged, False, NULL); + XtRealizeWidget (toplevel); } else - cmap = DefaultColormap (dpy, DefaultScreen (dpy)); +*/ + { + XtPopup (toplevel, XtGrabNone); + } + + 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 | ButtonReleaseMask)); + XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32, + PropModeReplace, + (unsigned char *) &XA_WM_DELETE_WINDOW, 1); + } } - if (!get_boolean_resource ("dontClearWindow", "Boolean")) /* kludge-o-rama */ + + if (!dont_clear) { XSetWindowBackground (dpy, window, get_pixel_resource ("background", "Background", @@ -194,11 +663,18 @@ main (argc, argv) XClearWindow (dpy, window); } - if (!root_p && toplevel->core.mapped_when_managed) + if (!root_p && !on_window) /* wait for it to be mapped */ XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window); XSync (dpy, False); - srandom ((int) time ((time_t *) 0)); - screenhack (dpy, window); + + /* 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; }