X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fxscreensaver.c;h=92b0f6f4a35a1e08419c6cee7497ce7f96d1ee57;hb=2a991a811de4c7b22f812682b267b616a809fd9a;hp=54aee1dc459486747a4935639decd77523b42728;hpb=0a1527cc01e9894017614b7c36b838b2b6914ba9;p=xscreensaver diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c index 54aee1dc..92b0f6f4 100644 --- a/driver/xscreensaver.c +++ b/driver/xscreensaver.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski +/* xscreensaver, Copyright (c) 1991-1998 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 @@ -9,14 +9,15 @@ * implied warranty. */ -#include "version.h" - /* ======================================================================== * First we wait until the keyboard and mouse become idle for the specified - * amount of time. We do this by periodically checking with the XIdle - * server extension. + * amount of time. We do this in one of three different ways: periodically + * checking with the XIdle server extension; selecting key and mouse events + * on (nearly) all windows; or by waiting for the MIT-SCREEN-SAVER extension + * to send us a "you are idle" event. * - * Then, we map a full screen black window. + * Then, we map a full screen black window (or, in the case of the + * MIT-SCREEN-SAVER extension, use the one it gave us.) * * We place a __SWM_VROOT property on this window, so that newly-started * clients will think that this window is a "virtual root" window. @@ -61,15 +62,15 @@ * and a few other things. The included "xscreensaver_command" program * sends these messsages. * - * If we don't have the XIdle extension, then we do the XAutoLock trick: - * notice every window that gets created, and wait 30 seconds or so until - * its creating process has settled down, and then select KeyPress events on - * those windows which already select for KeyPress events. It's important - * that we not select KeyPress on windows which don't select them, because - * that would interfere with event propagation. This will break if any - * program changes its event mask to contain KeyRelease or PointerMotion - * more than 30 seconds after creating the window, but that's probably - * pretty rare. + * If we don't have the XIdle, MIT-SCREEN-SAVER, or SGI SCREEN_SAVER + * extensions, then we do the XAutoLock trick: notice every window that + * gets created, and wait 30 seconds or so until its creating process has + * settled down, and then select KeyPress events on those windows which + * already select for KeyPress events. It's important that we not select + * KeyPress on windows which don't select them, because that would + * interfere with event propagation. This will break if any program + * changes its event mask to contain KeyRelease or PointerMotion more than + * 30 seconds after creating the window, but that's probably pretty rare. * * The reason that we can't select KeyPresses on windows that don't have * them already is that, when dispatching a KeyPress event, X finds the @@ -90,698 +91,1279 @@ * "client changing event mask" problem that the KeyPress events hack does. * I think polling is more reliable. * - * None of this crap happens if we're using the XIdle extension, so install - * it if the description above sounds just too flaky to live. It is, but - * those are your choices. + * None of this crap happens if we're using one of the extensions, so install + * one of them if the description above sounds just too flaky to live. It + * is, but those are your choices. + * + * A third idle-detection option could be implemented (but is not): when + * running on the console display ($DISPLAY is `localhost`:0) and we're on a + * machine where /dev/tty and /dev/mouse have reasonable last-modification + * times, we could just stat() those. But the incremental benefit of + * implementing this is really small, so forget I said anything. * * Debugging hints: * - Have a second terminal handy. * - Be careful where you set your breakpoints, you don't want this to * stop under the debugger with the keyboard grabbed or the blackout * window exposed. - * - you probably can't set breakpoints in functions that are called on + * - If you run your debugger under XEmacs, try M-ESC (x-grab-keyboard) + * to keep your emacs window alive even when xscreensaver has grabbed. + * - Go read the code related to `debug_p'. + * - You probably can't set breakpoints in functions that are called on * the other side of a call to fork() -- if your clients are dying * with signal 5, Trace/BPT Trap, you're losing in this way. - * - If you aren't using XIdle, don't leave this stopped under the - * debugger for very long, or the X input buffer will get huge because - * of the keypress events it's selecting for. This can make your X - * server wedge with "no more input buffers." + * - If you aren't using a server extension, don't leave this stopped + * under the debugger for very long, or the X input buffer will get + * huge because of the keypress events it's selecting for. This can + * make your X server wedge with "no more input buffers." * - * ======================================================================== - */ + * ======================================================================== */ -#if __STDC__ -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" #endif #include +#include #include #include #include +#include +#include #include - -#ifdef HAVE_XIDLE +#ifdef HAVE_XMU +# ifndef VMS +# include +# else /* !VMS */ +# include +# endif /* !VMS */ +#else /* !HAVE_XMU */ +# include "xmu.h" +#endif /* !HAVE_XMU */ + +#ifdef HAVE_XIDLE_EXTENSION #include -#endif +#endif /* HAVE_XIDLE_EXTENSION */ #include "xscreensaver.h" +#include "version.h" +#include "yarandom.h" +#include "resources.h" +#include "visual.h" -#if defined(SVR4) || defined(SYSV) -# define srandom(i) srand((unsigned int)(i)) -#else -extern void srandom P((int)); /* srand() is in stdlib.h... */ -#endif +saver_info *global_si_kludge = 0; /* I hate C so much... */ -extern char *get_string_resource P((char *, char *)); -extern Bool get_boolean_resource P((char *, char *)); -extern int get_integer_resource P((char *, char *)); -extern unsigned int get_minutes_resource P((char *, char *)); -extern unsigned int get_seconds_resource P((char *, char *)); +char *progname = 0; +char *progclass = 0; +XrmDatabase db = 0; -extern Visual *get_visual_resource P((Display *, char *, char *)); -extern int get_visual_depth P((Display *, Visual *)); -extern void notice_events_timer P((XtPointer closure, void *timer)); -extern void cycle_timer P((void *junk1, XtPointer junk2)); -extern void activate_lock_timer P((void *junk1, XtPointer junk2)); -extern void sleep_until_idle P((Bool until_idle_p)); +static Atom XA_SCREENSAVER_RESPONSE; +static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV; +static Atom XA_EXIT, XA_RESTART, XA_LOCK, XA_SELECT; +Atom XA_DEMO, XA_PREFS; -extern void ensure_no_screensaver_running P((void)); -extern void initialize_screensaver_window P((void)); -extern void disable_builtin_screensaver P((void)); + +static XrmOptionDescRec options [] = { + { "-timeout", ".timeout", XrmoptionSepArg, 0 }, + { "-cycle", ".cycle", XrmoptionSepArg, 0 }, + { "-lock-mode", ".lock", XrmoptionNoArg, "on" }, + { "-no-lock-mode", ".lock", XrmoptionNoArg, "off" }, + { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 }, + { "-lock-vts", ".lockVTs", XrmoptionNoArg, "on" }, + { "-no-lock-vts", ".lockVTs", XrmoptionNoArg, "off" }, + { "-visual", ".visualID", XrmoptionSepArg, 0 }, + { "-install", ".installColormap", XrmoptionNoArg, "on" }, + { "-no-install", ".installColormap", XrmoptionNoArg, "off" }, + { "-verbose", ".verbose", XrmoptionNoArg, "on" }, + { "-silent", ".verbose", XrmoptionNoArg, "off" }, + { "-timestamp", ".timestamp", XrmoptionNoArg, "on" }, + { "-capture-stderr", ".captureStderr", XrmoptionNoArg, "on" }, + { "-no-capture-stderr", ".captureStderr", XrmoptionNoArg, "off" }, + { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" }, + { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" }, + { "-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "on" }, + { "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" }, + { "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" }, + { "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" }, + { "-splash", ".splash", XrmoptionNoArg, "on" }, + { "-no-splash", ".splash", XrmoptionNoArg, "off" }, + { "-nosplash", ".splash", XrmoptionNoArg, "off" }, + { "-idelay", ".initialDelay", XrmoptionSepArg, 0 }, + { "-nice", ".nice", XrmoptionSepArg, 0 }, + + /* Actually these are built in to Xt, but just to be sure... */ + { "-synchronous", ".synchronous", XrmoptionNoArg, "on" }, + { "-xrm", NULL, XrmoptionResArg, NULL } +}; + +static char *defaults[] = { +#include "XScreenSaver_ad.h" + 0 +}; -extern void hack_environment P((void)); -extern void grab_keyboard_and_mouse P((void)); -extern void ungrab_keyboard_and_mouse P((void)); +#ifdef _VROOT_H_ +ERROR! You must not include vroot.h in this file. +#endif + +static void +do_help (saver_info *si) +{ + fflush (stdout); + fflush (stderr); + fprintf (stdout, "\ +xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski \n\ +The standard Xt command-line options are accepted; other options include:\n\ +\n\ + -timeout When the screensaver should activate.\n\ + -cycle How long to let each hack run before switching.\n\ + -lock-mode Require a password before deactivating.\n\ + -lock-timeout Grace period before locking; default 0.\n\ + -visual Which X visual to run on.\n\ + -install Install a private colormap.\n\ + -verbose Be loud.\n\ + -no-splash Don't display a splash-screen at startup.\n\ + -help This message.\n\ +\n\ +See the manual for other options and X resources.\n\ +\n\ +The `xscreensaver' program should be left running in the background.\n\ +Use the `xscreensaver-command' program to manipulate a running xscreensaver.\n\ +\n\ +The `*programs' resource controls which graphics demos will be launched by\n\ +the screensaver. See `man xscreensaver' or the web page for more details.\n\ +\n\ +Just getting started? Try this:\n\ +\n\ + xscreensaver &\n\ + xscreensaver-command -demo\n\ +\n\ +For updates, check http://www.jwz.org/xscreensaver/\n\ +\n", + si->version); + fflush (stdout); + fflush (stderr); + exit (1); +} -extern void save_argv P((int argc, char **argv)); +char * +timestring (void) +{ + time_t now = time ((time_t *) 0); + char *str = (char *) ctime (&now); + char *nl = (char *) strchr (str, '\n'); + if (nl) *nl = 0; /* take off that dang newline */ + return str; +} -char *screensaver_version; -char *progname; -char *progclass; -XrmDatabase db; +static Bool blurb_timestamp_p = False; /* kludge */ -XtAppContext app; +const char * +blurb (void) +{ + if (!blurb_timestamp_p) + return progname; + else + { + static char buf[255]; + char *ct = timestring(); + int n = strlen(progname); + if (n > 100) n = 99; + strncpy(buf, progname, n); + buf[n++] = ':'; + buf[n++] = ' '; + strncpy(buf+n, ct+11, 8); + strcpy(buf+n+9, ": "); + return buf; + } +} -Display *dpy; -Screen *screen; -Visual *visual; -int visual_depth; -Widget toplevel_shell; +int +saver_ehandler (Display *dpy, XErrorEvent *error) +{ + saver_info *si = global_si_kludge; /* I hate C so much... */ + + fprintf (real_stderr, "\n" + "#######################################" + "#######################################\n\n" + "%s: X Error! PLEASE REPORT THIS BUG.\n\n" + "#######################################" + "#######################################\n\n", + blurb()); + if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr)) + { + fprintf (real_stderr, "\n"); + if (si->prefs.xsync_p) + { + saver_exit (si, -1, "because of synchronous X Error"); + } + else + { + fprintf(real_stderr, + "%s: to dump a core file, re-run with `-sync'.\n" + "%s: see http://www.jwz.org/xscreensaver/bugs.html\n" + "\t\tfor bug reporting information.\n\n", + blurb(), blurb()); + saver_exit (si, -1, 0); + } + } + else + fprintf (real_stderr, " (nonfatal.)\n"); + return 0; +} -Time lock_timeout; + +/* The zillions of initializations. + */ -extern Time timeout; -extern Time cycle; -extern Time passwd_timeout; -extern Time pointer_timeout; -extern Time notice_events_timeout; -extern XtIntervalId lock_id, cycle_id; +static void get_screenhacks (saver_info *si); -Bool use_xidle; -Bool verbose_p; -Bool lock_p, locked_p; -extern char **screenhacks; -extern int screenhacks_count; -extern char *shell; -extern int nice_inferior; -extern Window screensaver_window; -extern Cursor cursor; -extern Colormap cmap, cmap2; -extern Bool fade_p, unfade_p; -extern int fade_seconds, fade_ticks; -extern Bool install_cmap_p; -extern Bool locking_disabled_p; -extern Bool demo_mode_p; -extern Bool dbox_up_p; -extern int next_mode_p; -static time_t initial_delay; +/* Set progname, version, etc. This is done very early. + */ +static void +set_version_string (saver_info *si, int *argc, char **argv) +{ + progclass = "XScreenSaver"; -extern Atom XA_VROOT, XA_XSETROOT_ID; -extern Atom XA_SCREENSAVER_VERSION, XA_SCREENSAVER_ID; + /* progname is reset later, after we connect to X. */ + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; -static Atom XA_SCREENSAVER; -static Atom XA_ACTIVATE, XA_DEACTIVATE, XA_CYCLE, XA_NEXT, XA_PREV; -static Atom XA_EXIT, XA_RESTART, XA_DEMO, XA_LOCK; + if (strlen(progname) > 100) /* keep it short. */ + progname[99] = 0; -#ifdef NO_MOTIF /* kludge */ -Bool demo_mode_p = 0; -Bool dbox_up_p = 0; -Time passwd_timeout = 0; -#endif + /* The X resource database blows up if argv[0] has a "." in it. */ + { + char *s = argv[0]; + while ((s = strchr (s, '.'))) + *s = '_'; + } - -#ifdef NO_DEMO_MODE -# define demo_mode() abort() -#else -extern void demo_mode P((void)); -#endif - -static XrmOptionDescRec options [] = { - { "-timeout", ".timeout", XrmoptionSepArg, 0 }, - { "-idelay", ".initialDelay",XrmoptionSepArg, 0 }, - { "-cycle", ".cycle", XrmoptionSepArg, 0 }, - { "-visual", ".visual", XrmoptionSepArg, 0 }, - { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 }, - { "-verbose", ".verbose", XrmoptionNoArg, "on" }, - { "-silent", ".verbose", XrmoptionNoArg, "off" }, - { "-xidle", ".xidle", XrmoptionNoArg, "on" }, - { "-no-xidle", ".xidle", XrmoptionNoArg, "off" }, - { "-lock", ".lock", XrmoptionNoArg, "on" }, - { "-no-lock", ".lock", XrmoptionNoArg, "off" } -}; + si->version = (char *) malloc (5); + memcpy (si->version, screensaver_id + 17, 4); + si->version [4] = 0; +} -static char *defaults[] = { -#include "XScreenSaver.ad.h" - 0 -}; +/* Initializations that potentially take place as a priveleged user: + If the xscreensaver executable is setuid root, then these initializations + are run as root, before discarding privileges. + */ static void -do_help () +privileged_initialization (saver_info *si, int *argc, char **argv) { - printf ("\ -xscreensaver %s, copyright (c) 1991-1993 by Jamie Zawinski .\n\ -The standard Xt command-line options are accepted; other options include:\n\ -\n\ - -timeout when the screensaver should activate\n\ - -cycle how long to let each hack run\n\ - -idelay how long to sleep before startup\n\ - -demo enter interactive demo mode on startup\n\ - -verbose be loud\n\ - -silent don't\n\ - -xidle use the XIdle server extension\n\ - -no-xidle don't\n\ - -lock require a password before deactivating\n\ - -no-lock don't\n\ - -lock-timeout grace period before locking; default 0\n\ - -help this message\n\ -\n\ -Use the `xscreensaver-command' program to control a running screensaver.\n\ -\n\ -The *programs, *colorPrograms, and *monoPrograms resources control which\n\ -graphics demos will be launched by the screensaver. See the man page for\n\ -more details.\n\n", - screensaver_version); - #ifdef NO_LOCKING - printf("Support for locking was not enabled at compile-time.\n"); -#endif -#ifdef NO_DEMO_MODE - printf("Support for demo mode was not enabled at compile-time.\n"); -#endif -#ifndef HAVE_XIDLE - printf("Support for the XIdle extension was not enabled at compile-time.\n"); -#endif + si->locking_disabled_p = True; + si->nolock_reason = "not compiled with locking support"; +#else /* !NO_LOCKING */ + si->locking_disabled_p = False; + if (! lock_init (*argc, argv)) /* before hack_uid() for proper permissions */ + { + si->locking_disabled_p = True; + si->nolock_reason = "error getting password"; + } +#endif /* NO_LOCKING */ - fflush (stdout); - exit (1); +#ifndef NO_SETUID + hack_uid (si); +#endif /* NO_SETUID */ } -static void -get_screenhacks () +/* Open the connection to the X server, and intern our Atoms. + */ +static Widget +connect_to_server (saver_info *si, int *argc, char **argv) { - char *data[3]; - int i, hacks_size = 10; + Widget toplevel_shell; - data[0] = get_string_resource ("programs", "Programs"); - data[1] = ((CellsOfScreen (screen) <= 2) - ? get_string_resource ("monoPrograms", "MonoPrograms") - : get_string_resource ("colorPrograms", "ColorPrograms")); - data[2] = 0; - if (! data[0]) data[0] = data[1], data[1] = 0; + XSetErrorHandler (saver_ehandler); + toplevel_shell = XtAppInitialize (&si->app, progclass, + options, XtNumber (options), + argc, argv, defaults, 0, 0); + + si->dpy = XtDisplay (toplevel_shell); + si->db = XtDatabase (si->dpy); + XtGetApplicationNameAndClass (si->dpy, &progname, &progclass); + + if(strlen(progname) > 100) /* keep it short. */ + progname [99] = 0; + + db = si->db; /* resources.c needs this */ + + XA_VROOT = XInternAtom (si->dpy, "__SWM_VROOT", False); + XA_SCREENSAVER = XInternAtom (si->dpy, "SCREENSAVER", False); + XA_SCREENSAVER_VERSION = XInternAtom (si->dpy, "_SCREENSAVER_VERSION",False); + XA_SCREENSAVER_ID = XInternAtom (si->dpy, "_SCREENSAVER_ID", False); + XA_SCREENSAVER_TIME = XInternAtom (si->dpy, "_SCREENSAVER_TIME", False); + XA_SCREENSAVER_RESPONSE = XInternAtom (si->dpy, "_SCREENSAVER_RESPONSE", + False); + XA_XSETROOT_ID = XInternAtom (si->dpy, "_XSETROOT_ID", False); + XA_ACTIVATE = XInternAtom (si->dpy, "ACTIVATE", False); + XA_DEACTIVATE = XInternAtom (si->dpy, "DEACTIVATE", False); + XA_RESTART = XInternAtom (si->dpy, "RESTART", False); + XA_CYCLE = XInternAtom (si->dpy, "CYCLE", False); + XA_NEXT = XInternAtom (si->dpy, "NEXT", False); + XA_PREV = XInternAtom (si->dpy, "PREV", False); + XA_SELECT = XInternAtom (si->dpy, "SELECT", False); + XA_EXIT = XInternAtom (si->dpy, "EXIT", False); + XA_DEMO = XInternAtom (si->dpy, "DEMO", False); + XA_PREFS = XInternAtom (si->dpy, "PREFS", False); + XA_LOCK = XInternAtom (si->dpy, "LOCK", False); + + return toplevel_shell; +} - screenhacks = (char **) malloc (sizeof (char *) * hacks_size); - screenhacks_count = 0; - for (i = 0; data[i]; i++) +/* Handle the command-line arguments that were not handled for us by Xt. + Issue an error message and exit if there are unknown options. + */ +static void +process_command_line (saver_info *si, int *argc, char **argv) +{ + int i; + for (i = 1; i < *argc; i++) { - int j = 0; - char *d = data [i]; - int size = strlen (d); - while (j < size) + if (!strcmp (argv[i], "-debug")) + /* no resource for this one, out of paranoia. */ + si->prefs.debug_p = True; + + else if (!strcmp (argv[i], "-initial-demo-mode")) + /* This isn't an advertized option; it is used internally to implement + the "Reinitialize" button on the Demo Mode window. */ + si->demo_mode_p = True; + + else if (!strcmp (argv[i], "-h") || + !strcmp (argv[i], "-help") || + !strcmp (argv[i], "--help")) + do_help (si); + + else { - int end, start = j; - if (d[j] == ' ' || d[j] == '\t' || d[j] == '\n' || d[j] == 0) + const char *s = argv[i]; + fprintf (stderr, "%s: unknown option \"%s\". Try \"-help\".\n", + blurb(), s); + + if (s[0] == '-' && s[1] == '-') s++; + if (!strcmp (s, "-activate") || + !strcmp (s, "-deactivate") || + !strcmp (s, "-cycle") || + !strcmp (s, "-next") || + !strcmp (s, "-prev") || + !strcmp (s, "-exit") || + !strcmp (s, "-restart") || + !strcmp (s, "-demo") || + !strcmp (s, "-prefs") || + !strcmp (s, "-preferences") || + !strcmp (s, "-lock") || + !strcmp (s, "-version") || + !strcmp (s, "-time")) { - j++; - continue; + fprintf (stderr, "\n\ + However, %s is an option to the `xscreensaver-command' program.\n\ + The `xscreensaver' program is a daemon that runs in the background.\n\ + You control a running xscreensaver process by sending it messages\n\ + with `xscreensaver-command'. See the man pages for details,\n\ + or check the web page: http://www.jwz.org/xscreensaver/\n\n", + s); + + /* Since version 1.21 renamed the "-lock" option to "-lock-mode", + suggest that explicitly. */ + if (!strcmp (s, "-lock")) + fprintf (stderr, "\ + Or perhaps you meant either the \"-lock-mode\" or the\n\ + \"-lock-timeout \" options to xscreensaver?\n\n"); } - if (hacks_size <= screenhacks_count) - screenhacks = (char **) realloc (screenhacks, - (hacks_size = hacks_size * 2) * - sizeof (char *)); - screenhacks [screenhacks_count++] = d + j; - while (d[j] != 0 && d[j] != '\n') - j++; - end = j; - while (j > start && (d[j-1] == ' ' || d[j-1] == '\t')) - j--; - d[j] = 0; - j = end + 1; + exit (1); } } +} + + +/* Print out the xscreensaver banner to the tty if applicable; + Issue any other warnings that are called for at this point. + */ +static void +print_banner (saver_info *si) +{ + saver_preferences *p = &si->prefs; - /* shrink all whitespace to one space, for the benefit of the "demo" - mode display. We only do this when we can easily tell that the - whitespace is not significant (no shell metachars). + /* This resource gets set some time before the others, so that we know + whether to print the banner (and so that the banner gets printed before + any resource-database-related error messages.) */ - for (i = 0; i < screenhacks_count; i++) + p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean")); + + /* Ditto, for the locking_disabled_p message. */ + p->lock_p = get_boolean_resource ("lock", "Boolean"); + + if (p->verbose_p) + fprintf (stderr, + "%s %s, copyright (c) 1991-1998 by Jamie Zawinski \n" + " pid = %d.\n", + blurb(), si->version, (int) getpid ()); + + if (p->debug_p) + fprintf (stderr, "\n" + "%s: Warning: running in DEBUG MODE. Be afraid.\n" + "\n" + "\tNote that in debug mode, the xscreensaver window will only\n" + "\tcover the left half of the screen. (The idea is that you\n" + "\tcan still see debugging output in a shell, if you position\n" + "\tit on the right side of the screen.)\n" + "\n" + "\tDebug mode is NOT SECURE. Do not run with -debug in\n" + "\tuntrusted environments.\n" + "\n", + blurb()); + + if (p->verbose_p) { - char *s = screenhacks [i]; - char *s2; - int L = strlen (s); - int j, k; - for (j = 0; j < L; j++) + if (!si->uid_message || !*si->uid_message) + describe_uids (si, stderr); + else { - switch (s[j]) - { - case '\'': case '"': case '`': case '\\': - goto DONE; - case '\t': - s[j] = ' '; - case ' ': - k = 0; - for (s2 = s+j+1; *s2 == ' ' || *s2 == '\t'; s2++) - k++; - if (k > 0) - for (s2 = s + j + 1; *s2; s2++) - s2 [0] = s2 [k]; - break; - } + if (si->orig_uid && *si->orig_uid) + fprintf (stderr, "%s: initial effective uid/gid was %s.\n", + blurb(), si->orig_uid); + fprintf (stderr, "%s: %s\n", blurb(), si->uid_message); } - DONE: - ; } - if (screenhacks_count) - { - /* Shrink down the screenhacks array to be only as big as it needs to. - This doesn't really matter at all. */ - screenhacks = (char **) - realloc (screenhacks, ((screenhacks_count + 1) * sizeof(char *))); - screenhacks [screenhacks_count] = 0; - } - else + /* If locking was not able to be initalized for some reason, explain why. + (This has to be done after we've read the lock_p resource.) + */ + if (p->lock_p && si->locking_disabled_p) { - free (screenhacks); - screenhacks = 0; + p->lock_p = False; + fprintf (stderr, "%s: locking is disabled (%s).\n", blurb(), + si->nolock_reason); + if (strstr (si->nolock_reason, "passw")) + fprintf (stderr, "%s: does xscreensaver need to be setuid? " + "consult the manual.\n", blurb()); + else if (strstr (si->nolock_reason, "running as ")) + fprintf (stderr, + "%s: locking only works when xscreensaver is launched\n" + "\t by a normal, non-privileged user (e.g., not \"root\".)\n" + "\t See the manual for details.\n", + blurb()); } } +/* Examine all of the display's screens, and populate the `saver_screen_info' + structures. + */ static void -get_resources () +initialize_per_screen_info (saver_info *si, Widget toplevel_shell) { - visual = get_visual_resource (dpy, "visual", "Visual"); - timeout = 1000 * get_minutes_resource ("timeout", "Time"); - cycle = 1000 * get_minutes_resource ("cycle", "Time"); - lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time"); - nice_inferior = get_integer_resource ("nice", "Nice"); - verbose_p = get_boolean_resource ("verbose", "Boolean"); - lock_p = get_boolean_resource ("lock", "Boolean"); - install_cmap_p = get_boolean_resource ("installColormap", "Boolean"); - fade_p = get_boolean_resource ("fade", "Boolean"); - unfade_p = get_boolean_resource ("unfade", "Boolean"); - fade_seconds = get_seconds_resource ("fadeSeconds", "Time"); - fade_ticks = get_integer_resource ("fadeTicks", "Integer"); - shell = get_string_resource ("bourneShell", "BourneShell"); - initial_delay = get_seconds_resource ("initialDelay", "Time"); - passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time"); - pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time"); - notice_events_timeout = 1000 * get_seconds_resource ("windowCreationTimeout", - "Time"); - if (timeout < 10000) timeout = 10000; - if (cycle < 2000) cycle = 2000; - if (passwd_timeout == 0) passwd_timeout = 30000; - if (pointer_timeout == 0) pointer_timeout = 5000; - if (notice_events_timeout == 0) notice_events_timeout = 10000; - if (fade_seconds == 0 || fade_ticks == 0) fade_p = False; - if (! fade_p) unfade_p = False; + Bool found_any_writable_cells = False; + int i; - visual_depth = get_visual_depth (dpy, visual); + si->nscreens = ScreenCount(si->dpy); + si->screens = (saver_screen_info *) + calloc(sizeof(saver_screen_info), si->nscreens); - if (visual_depth <= 1 || CellsOfScreen (screen) <= 2) - install_cmap_p = False; + si->default_screen = &si->screens[DefaultScreen(si->dpy)]; -#ifdef NO_LOCKING - locking_disabled_p = True; - if (lock_p) + for (i = 0; i < si->nscreens; i++) { - lock_p = False; - fprintf (stderr, "%s: %snot compiled with support for locking.\n", - progname, (verbose_p ? "## " : "")); - } -#else /* ! NO_LOCKING */ - if (lock_p && locking_disabled_p) - { - fprintf (stderr, "%s: %slocking is disabled.\n", progname, - (verbose_p ? "## " : "")); - lock_p = False; - } -#endif /* ! NO_LOCKING */ + saver_screen_info *ssi = &si->screens[i]; + ssi->global = si; + ssi->screen = ScreenOfDisplay (si->dpy, i); - /* don't set use_xidle unless it is explicitly specified */ - if (get_string_resource ("xidle", "Boolean")) - use_xidle = get_boolean_resource ("xidle", "Boolean"); - else -#ifdef HAVE_XIDLE /* pick a default */ - use_xidle = True; -#else - use_xidle = False; -#endif - get_screenhacks (); -} + /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */ + ssi->default_visual = + get_visual_resource (ssi->screen, "visualID", "VisualID", False); -char * -timestring () -{ - long now = time ((time_t *) 0); - char *str = (char *) ctime (&now); - char *nl = (char *) strchr (str, '\n'); - if (nl) *nl = 0; /* take off that dang newline */ - return str; -} + ssi->current_visual = ssi->default_visual; + ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual); -#ifdef NO_SETUID -# define hack_uid() -# define hack_uid_warn() -#else /* !NO_SETUID */ -extern void hack_uid P((void)); -extern void hack_uid_warn P((void)); -#endif /* NO_SETUID */ + if (ssi == si->default_screen) + /* Since this is the default screen, use the one already created. */ + ssi->toplevel_shell = toplevel_shell; + else + /* Otherwise, each screen must have its own unmapped root widget. */ + ssi->toplevel_shell = + XtVaAppCreateShell (progname, progclass, applicationShellWidgetClass, + si->dpy, + XtNscreen, ssi->screen, + XtNvisual, ssi->current_visual, + XtNdepth, visual_depth (ssi->screen, + ssi->current_visual), + 0); + + if (! found_any_writable_cells) + { + /* Check to see whether fading is ever possible -- if any of the + screens on the display has a PseudoColor visual, then fading can + work (on at least some screens.) If no screen has a PseudoColor + visual, then don't bother ever trying to fade, because it will + just cause a delay without causing any visible effect. + */ + if (has_writable_cells (ssi->screen, ssi->current_visual) || + get_visual (ssi->screen, "PseudoColor", True, False) || + get_visual (ssi->screen, "GrayScale", True, False)) + found_any_writable_cells = True; + } + } + si->fading_possible_p = found_any_writable_cells; +} -#ifndef NO_LOCKING -extern Bool unlock_p P((Widget)); -extern Bool lock_init P((void)); -#endif -static void initialize (); -static void main_loop (); -static void initialize (); +/* Populate `saver_preferences' with the contents of the resource database. + Note that this may be called multiple times -- it is re-run each time + the ~/.xscreensaver file is reloaded. + This function can be very noisy, since it issues resource syntax errors + and so on. + */ void -main (argc, argv) - int argc; - char **argv; +get_resources (saver_info *si) { - initialize (argc, argv); - main_loop (); -} + char *s; + saver_preferences *p = &si->prefs; + + if (si->init_file_date == 0) + /* The date will be 0 the first time this is called; and when this is + called subsequent times, the file will have already been reloaded. */ + read_init_file (si); + + p->xsync_p = get_boolean_resource ("synchronous", "Synchronous"); + if (p->xsync_p) + XSynchronize(si->dpy, True); + + p->verbose_p = get_boolean_resource ("verbose", "Boolean"); + p->timestamp_p = get_boolean_resource ("timestamp", "Boolean"); + p->lock_p = get_boolean_resource ("lock", "Boolean"); + p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean"); + p->fade_p = get_boolean_resource ("fade", "Boolean"); + p->unfade_p = get_boolean_resource ("unfade", "Boolean"); + p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time"); + p->fade_ticks = get_integer_resource ("fadeTicks", "Integer"); + p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean"); + p->nice_inferior = get_integer_resource ("nice", "Nice"); + + p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time"); + p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time"); + p->timeout = 1000 * get_minutes_resource ("timeout", "Time"); + p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time"); + p->cycle = 1000 * get_minutes_resource ("cycle", "Time"); + p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time"); + p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time"); + p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout", + "Time"); + p->shell = get_string_resource ("bourneShell", "BourneShell"); + p->help_url = get_string_resource("helpURL", "URL"); + p->load_url_command = get_string_resource("loadURL", "LoadURL"); -static void -initialize_connection (argc, argv) - int argc; - char **argv; -{ - toplevel_shell = XtAppInitialize (&app, progclass, - options, XtNumber (options), - &argc, argv, defaults, 0, 0); + if ((s = get_string_resource ("splash", "Boolean"))) + if (!get_boolean_resource("splash", "Boolean")) + p->splash_duration = 0; + if (s) free (s); - dpy = XtDisplay (toplevel_shell); - screen = XtScreen (toplevel_shell); - db = XtDatabase (dpy); - XtGetApplicationNameAndClass (dpy, &progname, &progclass); + if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p)) + { + fprintf (stderr, + (si->nscreens == 1 + ? "%s: the screen has no PseudoColor or GrayScale visuals.\n" + : "%s: no screens have PseudoColor or GrayScale visuals.\n"), + blurb()); + fprintf (stderr, "%s: ignoring the request for fading/unfading.\n", + blurb()); + } - if (argc == 2 && !strcmp (argv[1], "-help")) - do_help (); - else if (argc > 1) + /* don't set use_xidle_extension unless it is explicitly specified */ + if ((s = get_string_resource ("xidleExtension", "Boolean"))) + p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean"); + else +#ifdef HAVE_XIDLE_EXTENSION /* pick a default */ + p->use_xidle_extension = True; /* if we have it, use it */ +#else /* !HAVE_XIDLE_EXTENSION */ + p->use_xidle_extension = False; +#endif /* !HAVE_XIDLE_EXTENSION */ + if (s) free (s); + + /* don't set use_mit_extension unless it is explicitly specified */ + if ((s = get_string_resource ("mitSaverExtension", "Boolean"))) + p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension", + "Boolean"); + else +#ifdef HAVE_MIT_SAVER_EXTENSION /* pick a default */ + p->use_mit_saver_extension = False; /* Default false, because it sucks */ +#else /* !HAVE_MIT_SAVER_EXTENSION */ + p->use_mit_saver_extension = False; +#endif /* !HAVE_MIT_SAVER_EXTENSION */ + if (s) free (s); + + + /* don't set use_mit_extension unless it is explicitly specified */ + if ((s = get_string_resource ("sgiSaverExtension", "Boolean"))) + p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension", + "Boolean"); + else +#ifdef HAVE_SGI_SAVER_EXTENSION /* pick a default */ + p->use_sgi_saver_extension = True; /* if we have it, use it */ +#else /* !HAVE_SGI_SAVER_EXTENSION */ + p->use_sgi_saver_extension = False; +#endif /* !HAVE_SGI_SAVER_EXTENSION */ + if (s) free (s); + + + /* Throttle the various timeouts to reasonable values. + */ + if (p->passwd_timeout == 0) p->passwd_timeout = 30000; /* 30 secs */ + if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */ + if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ + if (p->pointer_timeout == 0) p->pointer_timeout = 5000; /* 5 secs */ + if (p->notice_events_timeout == 0) + p->notice_events_timeout = 10000; /* 10 secs */ + if (p->fade_seconds == 0 || p->fade_ticks == 0) + p->fade_p = False; + if (! p->fade_p) p->unfade_p = False; + + p->watchdog_timeout = p->cycle; + if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */ + if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */ + + get_screenhacks (si); + + if (p->debug_p) { - fprintf (stderr, "%s: unknown option %s\n", progname, argv [1]); - exit (1); + XSynchronize(si->dpy, True); + p->xsync_p = True; + p->verbose_p = True; + p->timestamp_p = True; + p->initial_delay = 0; } - get_resources (); - hack_uid_warn (); - hack_environment (); - XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False); - XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False); - XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION", False); - XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False); - XA_XSETROOT_ID = XInternAtom (dpy, "_XSETROOT_ID", False); - XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False); - XA_DEACTIVATE = XInternAtom (dpy, "DEACTIVATE", False); - XA_RESTART = XInternAtom (dpy, "RESTART", False); - XA_CYCLE = XInternAtom (dpy, "CYCLE", False); - XA_NEXT = XInternAtom (dpy, "NEXT", False); - XA_PREV = XInternAtom (dpy, "PREV", False); - XA_EXIT = XInternAtom (dpy, "EXIT", False); - XA_DEMO = XInternAtom (dpy, "DEMO", False); - XA_LOCK = XInternAtom (dpy, "LOCK", False); + + blurb_timestamp_p = p->timestamp_p; } -extern void init_sigchld P((void)); +/* If any server extensions have been requested, try and initialize them. + Issue warnings if requests can't be honored. + */ static void -initialize (argc, argv) - int argc; - char **argv; +initialize_server_extensions (saver_info *si) { - Bool initial_demo_mode_p = False; - screensaver_version = (char *) malloc (5); - memcpy (screensaver_version, screensaver_id + 17, 4); - screensaver_version [4] = 0; - progname = argv[0]; /* reset later; this is for the benefit of lock_init() */ - -#ifdef NO_LOCKING - locking_disabled_p = True; -#else - locking_disabled_p = False; - if (! lock_init ()) /* before hack_uid() for proper permissions */ - locking_disabled_p = True; -#endif + saver_preferences *p = &si->prefs; - hack_uid (); - progclass = "XScreenSaver"; + if (p->use_sgi_saver_extension) + { +#ifdef HAVE_SGI_SAVER_EXTENSION + if (! query_sgi_saver_extension (si)) + { + fprintf (stderr, + "%s: display %s does not support the SGI SCREEN_SAVER extension.\n", + blurb(), DisplayString (si->dpy)); + p->use_sgi_saver_extension = False; + } + else if (p->use_mit_saver_extension) + { + fprintf (stderr, + "%s: SGI SCREEN_SAVER extension used instead" + " of MIT-SCREEN-SAVER extension.\n", + blurb()); + p->use_mit_saver_extension = False; + } + else if (p->use_xidle_extension) + { + fprintf (stderr, + "%s: SGI SCREEN_SAVER extension used instead of XIDLE extension.\n", + blurb()); + p->use_xidle_extension = False; + } +#else /* !HAVE_MIT_SAVER_EXTENSION */ + fprintf (stderr, + "%s: not compiled with support for the SGI SCREEN_SAVER" + " extension.\n", + blurb()); + p->use_sgi_saver_extension = False; +#endif /* !HAVE_SGI_SAVER_EXTENSION */ + } - /* remove -demo switch before saving argv */ - { - int i; - for (i = 1; i < argc; i++) - while (!strcmp ("-demo", argv [i])) + if (p->use_mit_saver_extension) + { +#ifdef HAVE_MIT_SAVER_EXTENSION + if (! query_mit_saver_extension (si)) { - int j; - initial_demo_mode_p = True; - for (j = i; j < argc; j++) - argv [j] = argv [j+1]; - argv [j] = 0; - argc--; - if (argc <= i) break; + fprintf (stderr, + "%s: display %s does not support the MIT-SCREEN-SAVER" + " extension.\n", + blurb(), DisplayString (si->dpy)); + p->use_mit_saver_extension = False; } - } - save_argv (argc, argv); - initialize_connection (argc, argv); + else if (p->use_xidle_extension) + { + fprintf (stderr, + "%s: MIT-SCREEN-SAVER extension used instead of XIDLE" + " extension.\n", + blurb()); + p->use_xidle_extension = False; + } +#else /* !HAVE_MIT_SAVER_EXTENSION */ + fprintf (stderr, + "%s: not compiled with support for the MIT-SCREEN-SAVER" + " extension.\n", + blurb()); + p->use_mit_saver_extension = False; +#endif /* !HAVE_MIT_SAVER_EXTENSION */ + } - ensure_no_screensaver_running (); - demo_mode_p = initial_demo_mode_p; - screensaver_window = 0; - cursor = 0; - initialize_screensaver_window (); - srandom ((int) time ((time_t *) 0)); - cycle_id = 0; - lock_id = 0; - locked_p = False; - if (use_xidle) + if (p->use_xidle_extension) { -#ifdef HAVE_XIDLE +#ifdef HAVE_XIDLE_EXTENSION int first_event, first_error; - if (! XidleQueryExtension (dpy, &first_event, &first_error)) + if (! XidleQueryExtension (si->dpy, &first_event, &first_error)) { fprintf (stderr, "%s: display %s does not support the XIdle extension.\n", - progname, DisplayString (dpy)); - use_xidle = 0; + blurb(), DisplayString (si->dpy)); + p->use_xidle_extension = False; } -#else - fprintf (stderr, "%s: not compiled with support for XIdle.\n", - progname); - use_xidle = 0; -#endif +#else /* !HAVE_XIDLE_EXTENSION */ + fprintf (stderr, "%s: not compiled with support for XIdle.\n", blurb()); + p->use_xidle_extension = False; +#endif /* !HAVE_XIDLE_EXTENSION */ } - init_sigchld (); + if (p->verbose_p && p->use_mit_saver_extension) + fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n", + blurb()); + if (p->verbose_p && p->use_sgi_saver_extension) + fprintf (stderr, "%s: using SGI SCREEN_SAVER server extension.\n", + blurb()); + if (p->verbose_p && p->use_xidle_extension) + fprintf (stderr, "%s: using XIdle server extension.\n", + blurb()); +} + - if (verbose_p) - printf ("\ -%s %s, copyright (c) 1991-1993 by Jamie Zawinski .\n\ - pid = %d.\n", progname, screensaver_version, getpid ()); +/* For the case where we aren't using an server extensions, select user events + on all the existing windows, and launch timers to select events on + newly-created windows as well. - disable_builtin_screensaver (); + If a server extension is being used, this does nothing. + */ +static void +select_events (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i; - if (initial_demo_mode_p) - /* If the user wants demo mode, don't wait around before doing it. */ - initial_delay = 0; + if (p->use_xidle_extension || + p->use_mit_saver_extension || + p->use_sgi_saver_extension) + return; -#ifdef HAVE_XIDLE - if (! use_xidle) -#endif + if (p->initial_delay && !si->demo_mode_p) { - if (initial_delay) - { - if (verbose_p) - { - printf ("%s: waiting for %d second%s...", progname, - initial_delay, (initial_delay == 1 ? "" : "s")); - fflush (stdout); - } - sleep (initial_delay); - if (verbose_p) - printf (" done.\n"); - } - if (verbose_p) + if (p->verbose_p) { - printf ("%s: selecting events on extant windows...", progname); + fprintf (stderr, "%s: waiting for %d second%s...", blurb(), + (int) p->initial_delay/1000, + (p->initial_delay == 1000 ? "" : "s")); + fflush (stderr); fflush (stdout); } - notice_events_timer ((XtPointer) - RootWindowOfScreen (XtScreen (toplevel_shell)), - 0); - if (verbose_p) - printf (" done.\n"); + usleep (p->initial_delay); + if (p->verbose_p) + fprintf (stderr, " done.\n"); } + + if (p->verbose_p) + { + fprintf (stderr, "%s: selecting events on extant windows...", blurb()); + fflush (stderr); + fflush (stdout); + } + + /* Select events on the root windows of every screen. This also selects + for window creation events, so that new subwindows will be noticed. + */ + for (i = 0; i < si->nscreens; i++) + start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen)); + + if (p->verbose_p) + fprintf (stderr, " done.\n"); } -extern void suspend_screenhack P((Bool suspend_p)); +/* Loop forever: + + - wait until the user is idle; + - blank the screen; + - wait until the user is active; + - unblank the screen; + - repeat. + */ static void -main_loop () +main_loop (saver_info *si) { + saver_preferences *p = &si->prefs; while (1) { - if (! demo_mode_p) - sleep_until_idle (True); + if (! si->demo_mode_p) + sleep_until_idle (si, True); - if (demo_mode_p) - demo_mode (); + maybe_reload_init_file (si); + +#ifndef NO_DEMO_MODE + if (si->demo_mode_p) + demo_mode (si); else +#endif /* !NO_DEMO_MODE */ { - if (verbose_p) - printf ("%s: user is idle; waking up at %s.\n", progname, - timestring()); - blank_screen (); - spawn_screenhack (True); - if (cycle) - cycle_id = XtAppAddTimeOut (app, cycle, cycle_timer, 0); + if (p->verbose_p) + fprintf (stderr, "%s: user is idle; waking up at %s.\n", blurb(), + timestring()); + maybe_reload_init_file (si); + blank_screen (si); + spawn_screenhack (si, True); + if (p->cycle) + si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer, + (XtPointer) si); #ifndef NO_LOCKING - if (lock_p && lock_timeout == 0) - locked_p = True; - if (lock_p && !locked_p) + if (p->lock_p && + !si->locking_disabled_p && + p->lock_timeout == 0) + si->locked_p = True; + + if (p->lock_p && !si->locked_p) /* locked_p might be true already because of ClientMessage */ - lock_id = XtAppAddTimeOut (app,lock_timeout,activate_lock_timer,0); -#endif + si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout, + activate_lock_timer, + (XtPointer) si); +#endif /* !NO_LOCKING */ PASSWD_INVALID: - sleep_until_idle (False); /* until not idle */ + sleep_until_idle (si, False); /* until not idle */ + maybe_reload_init_file (si); #ifndef NO_LOCKING - if (locked_p) + if (si->locked_p) { Bool val; - if (locking_disabled_p) abort (); - dbox_up_p = True; - ungrab_keyboard_and_mouse (); - suspend_screenhack (True); - XUndefineCursor (dpy, screensaver_window); - if (verbose_p) - printf ("%s: prompting for password.\n", progname); - val = unlock_p (toplevel_shell); - if (verbose_p && val == False) - printf ("%s: password incorrect!\n", progname); - dbox_up_p = False; - XDefineCursor (dpy, screensaver_window, cursor); - suspend_screenhack (False); - grab_keyboard_and_mouse (); + if (si->locking_disabled_p) abort (); + si->dbox_up_p = True; + + { + saver_screen_info *ssi = si->default_screen; + suspend_screenhack (si, True); + XUndefineCursor (si->dpy, ssi->screensaver_window); + if (p->verbose_p) + fprintf (stderr, "%s: prompting for password.\n", blurb()); + val = unlock_p (si); + if (p->verbose_p && val == False) + fprintf (stderr, "%s: password incorrect!\n", blurb()); + si->dbox_up_p = False; + XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor); + suspend_screenhack (si, False); + } + if (! val) goto PASSWD_INVALID; - locked_p = False; + si->locked_p = False; } -#endif - unblank_screen (); - kill_screenhack (); - if (cycle_id) +#endif /* !NO_LOCKING */ + + if (p->verbose_p) + fprintf (stderr, "%s: user is active at %s.\n", + blurb(), timestring ()); + + /* Let's kill it before unblanking, to get it to stop drawing as + soon as possible... */ + kill_screenhack (si); + unblank_screen (si); + si->selection_mode = 0; + + if (si->cycle_id) { - XtRemoveTimeOut (cycle_id); - cycle_id = 0; + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; } + #ifndef NO_LOCKING - if (lock_id) + if (si->lock_id) { - XtRemoveTimeOut (lock_id); - lock_id = 0; + XtRemoveTimeOut (si->lock_id); + si->lock_id = 0; } -#endif - if (verbose_p) - printf ("%s: user is active; going to sleep at %s.\n", progname, - timestring ()); +#endif /* !NO_LOCKING */ + + if (p->verbose_p) + fprintf (stderr, "%s: going to sleep.\n", blurb()); } } } + +int +main (int argc, char **argv) +{ + Widget shell; + saver_info the_si; + saver_info *si = &the_si; + int i; + + memset(si, 0, sizeof(*si)); + global_si_kludge = si; /* I hate C so much... */ + + srandom ((int) time ((time_t *) 0)); + + set_version_string (si, &argc, argv); + save_argv (argc, argv); + privileged_initialization (si, &argc, argv); + hack_environment (si); + + shell = connect_to_server (si, &argc, argv); + process_command_line (si, &argc, argv); + print_banner (si); + initialize_per_screen_info (si, shell); + get_resources (si); + + for (i = 0; i < si->nscreens; i++) + if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen)) + exit (1); + + initialize_server_extensions (si); + initialize_screensaver_window (si); + select_events (si); + init_sigchld (); + disable_builtin_screensaver (si, True); + initialize_stderr (si); + + if (!si->demo_mode_p) + make_splash_dialog (si); + + main_loop (si); /* doesn't return */ + return 0; +} + +/* Parsing the programs resource. + */ + +static char * +reformat_hack (const char *hack) +{ + int i; + const char *in = hack; + int indent = 13; + char *h2 = (char *) malloc(strlen(in) + indent + 2); + char *out = h2; + + while (isspace(*in)) in++; /* skip whitespace */ + while (*in && !isspace(*in) && *in != ':') + *out++ = *in++; /* snarf first token */ + while (isspace(*in)) in++; /* skip whitespace */ + + if (*in == ':') + *out++ = *in++; /* copy colon */ + else + { + in = hack; + out = h2; /* reset to beginning */ + } + + *out = 0; + + while (isspace(*in)) in++; /* skip whitespace */ + for (i = strlen(h2); i < indent; i++) /* indent */ + *out++ = ' '; + + /* copy the rest of the line. */ + while (*in) + { + /* shrink all whitespace to one space, for the benefit of the "demo" + mode display. We only do this when we can easily tell that the + whitespace is not significant (no shell metachars). + */ + switch (*in) + { + case '\'': case '"': case '`': case '\\': + { + /* Metachars are scary. Copy the rest of the line unchanged. */ + while (*in) + *out++ = *in++; + } + break; + case ' ': case '\t': + { + while (*in == ' ' || *in == '\t') + in++; + *out++ = ' '; + } + break; + default: + *out++ = *in++; + break; + } + } + *out = 0; + + /* strip trailing whitespace. */ + out = out-1; + while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n')) + *out-- = 0; + + return h2; +} + + +static void +get_screenhacks (saver_info *si) +{ + saver_preferences *p = &si->prefs; + int i = 0; + int start = 0; + int end = 0; + int size; + char *d; + + d = get_string_resource ("monoPrograms", "MonoPrograms"); + if (d && !*d) { free(d); d = 0; } + if (!d) + d = get_string_resource ("colorPrograms", "ColorPrograms"); + if (d && !*d) { free(d); d = 0; } + + if (d) + { + fprintf (stderr, + "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\ + see the manual for details.\n", blurb()); + free(d); + } + + d = get_string_resource ("programs", "Programs"); + + if (p->screenhacks) + { + for (i = 0; i < p->screenhacks_count; i++) + if (p->screenhacks[i]) + free (p->screenhacks[i]); + free(p->screenhacks); + p->screenhacks = 0; + } + + if (!d || !*d) + { + p->screenhacks_count = 0; + p->screenhacks = 0; + return; + } + + size = strlen (d); + + + /* Count up the number of newlines (which will be equal to or larger than + the number of hacks.) + */ + i = 0; + for (i = 0; d[i]; i++) + if (d[i] == '\n') + i++; + i++; + + p->screenhacks = (char **) calloc (sizeof (char *), i+1); + + /* Iterate over the lines in `d' (the string with newlines) + and make new strings to stuff into the `screenhacks' array. + */ + p->screenhacks_count = 0; + while (start < size) + { + /* skip forward over whitespace. */ + while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n') + start++; + + /* skip forward to newline or end of string. */ + end = start; + while (d[end] != 0 && d[end] != '\n') + end++; + + /* null terminate. */ + d[end] = 0; + + p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start); + if (p->screenhacks_count >= i) + abort(); + + start = end+1; + } + + if (p->screenhacks_count == 0) + { + free (p->screenhacks); + p->screenhacks = 0; + } +} + + + +/* Processing ClientMessage events. + */ + +static void +clientmessage_response (saver_info *si, Window w, Bool error, + const char *stderr_msg, + const char *protocol_msg) +{ + char *proto; + int L; + saver_preferences *p = &si->prefs; + if (error || p->verbose_p) + fprintf (stderr, "%s: %s\n", blurb(), stderr_msg); + + L = strlen(protocol_msg); + proto = (char *) malloc (L + 2); + proto[0] = (error ? '-' : '+'); + strcpy (proto+1, protocol_msg); + L++; + + XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8, + PropModeReplace, proto, L); + XSync (si->dpy, False); + free (proto); +} Bool -handle_clientmessage (event, until_idle_p) - XEvent *event; - Bool until_idle_p; +handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p) { - Atom type; + saver_preferences *p = &si->prefs; + Atom type = 0; + Window window = event->xclient.window; + + /* Preferences might affect our handling of client messages. */ + maybe_reload_init_file (si); + if (event->xclient.message_type != XA_SCREENSAVER) { char *str; - str = XGetAtomName (dpy, event->xclient.message_type); - fprintf (stderr, "%s: %sunrecognised ClientMessage type %s received\n", - progname, (verbose_p ? "## " : ""), - (str ? str : "(null)")); + str = XGetAtomName (si->dpy, event->xclient.message_type); + fprintf (stderr, "%s: unrecognised ClientMessage type %s received\n", + blurb(), (str ? str : "(null)")); if (str) XFree (str); + return False; } if (event->xclient.format != 32) { - fprintf (stderr, "%s: %sClientMessage of format %d received, not 32\n", - progname, (verbose_p ? "## " : ""), event->xclient.format); + fprintf (stderr, "%s: ClientMessage of format %d received, not 32\n", + blurb(), event->xclient.format); + return False; } + type = event->xclient.data.l[0]; if (type == XA_ACTIVATE) { if (until_idle_p) { - if (verbose_p) - printf ("%s: ACTIVATE ClientMessage received.\n", progname); - return True; + clientmessage_response(si, window, False, + "ACTIVATE ClientMessage received.", + "activating."); + si->selection_mode = 0; + if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + { + XForceScreenSaver (si->dpy, ScreenSaverActive); + return False; + } + else + { + return True; + } } - fprintf (stderr, - "%s: %sClientMessage ACTIVATE received while already active.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response(si, window, True, + "ClientMessage ACTIVATE received while already active.", + "already active."); } else if (type == XA_DEACTIVATE) { if (! until_idle_p) { - if (verbose_p) - printf ("%s: DEACTIVATE ClientMessage received.\n", progname); - return True; + clientmessage_response(si, window, False, + "DEACTIVATE ClientMessage received.", + "deactivating."); + if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + { + XForceScreenSaver (si->dpy, ScreenSaverReset); + return False; + } + else + { + return True; + } } - fprintf (stderr, - "%s: %sClientMessage DEACTIVATE received while inactive.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response(si, window, True, + "ClientMessage DEACTIVATE received while inactive.", + "not active."); } else if (type == XA_CYCLE) { if (! until_idle_p) { - if (verbose_p) - printf ("%s: CYCLE ClientMessage received.\n", progname); - if (cycle_id) - XtRemoveTimeOut (cycle_id); - cycle_id = 0; - cycle_timer (0, 0); + clientmessage_response(si, window, False, + "CYCLE ClientMessage received.", + "cycling."); + si->selection_mode = 0; /* 0 means randomize when its time. */ + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); return False; } - fprintf (stderr, - "%s: %sClientMessage CYCLE received while inactive.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response(si, window, True, + "ClientMessage CYCLE received while inactive.", + "not active."); } else if (type == XA_NEXT || type == XA_PREV) { - if (verbose_p) - printf ("%s: %s ClientMessage received.\n", progname, - (type == XA_NEXT ? "NEXT" : "PREV")); - next_mode_p = 1 + (type == XA_PREV); + clientmessage_response(si, window, False, + (type == XA_NEXT + ? "NEXT ClientMessage received." + : "PREV ClientMessage received."), + "cycling."); + si->selection_mode = (type == XA_NEXT ? -1 : -2); if (! until_idle_p) { - if (cycle_id) - XtRemoveTimeOut (cycle_id); - cycle_id = 0; - cycle_timer (0, 0); + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); + } + else + return True; + } + else if (type == XA_SELECT) + { + char buf [255]; + char buf2 [255]; + long which = event->xclient.data.l[1]; + + sprintf (buf, "SELECT %ld ClientMessage received.", which); + sprintf (buf2, "activating (%ld).", which); + clientmessage_response (si, window, False, buf, buf2); + + if (which < 0) which = 0; /* 0 == "random" */ + si->selection_mode = which; + + if (! until_idle_p) + { + if (si->cycle_id) + XtRemoveTimeOut (si->cycle_id); + si->cycle_id = 0; + cycle_timer ((XtPointer) si, 0); } else return True; @@ -789,108 +1371,163 @@ handle_clientmessage (event, until_idle_p) else if (type == XA_EXIT) { /* Ignore EXIT message if the screen is locked. */ - if (until_idle_p || !locked_p) + if (until_idle_p || !si->locked_p) { - if (verbose_p) - printf ("%s: EXIT ClientMessage received.\n", progname); + clientmessage_response (si, window, False, + "EXIT ClientMessage received.", + "exiting."); if (! until_idle_p) { - unblank_screen (); - kill_screenhack (); - XSync (dpy, False); + unblank_screen (si); + kill_screenhack (si); + XSync (si->dpy, False); } - exit (0); + saver_exit (si, 0, 0); } else - fprintf (stderr, "%s: %sEXIT ClientMessage received while locked.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response (si, window, True, + "EXIT ClientMessage received while locked.", + "screen is locked."); } else if (type == XA_RESTART) { /* The RESTART message works whether the screensaver is active or not, unless the screen is locked, in which case it doesn't work. */ - if (until_idle_p || !locked_p) + if (until_idle_p || !si->locked_p) { - if (verbose_p) - printf ("%s: RESTART ClientMessage received.\n", progname); + clientmessage_response (si, window, False, + "RESTART ClientMessage received.", + "restarting."); if (! until_idle_p) { - unblank_screen (); - kill_screenhack (); - XSync (dpy, False); + unblank_screen (si); + kill_screenhack (si); + XSync (si->dpy, False); } - restart_process (); + + /* make sure error message shows up before exit. */ + if (real_stderr && stderr != real_stderr) + dup2 (fileno(real_stderr), fileno(stderr)); + + restart_process (si); + exit (1); /* shouldn't get here; but if restarting didn't work, + make this command be the same as EXIT. */ } else - fprintf(stderr, "%s: %sRESTART ClientMessage received while locked.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response (si, window, True, + "RESTART ClientMessage received while locked.", + "screen is locked."); } else if (type == XA_DEMO) { #ifdef NO_DEMO_MODE - fprintf (stderr, - "%s: %snot compiled with support for DEMO mode\n", - progname, (verbose_p ? "## " : "")); -#else + clientmessage_response (si, window, True, + "not compiled with support for DEMO mode.", + "demo mode not enabled."); +#else /* !NO_DEMO_MODE */ if (until_idle_p) { - if (verbose_p) - printf ("%s: DEMO ClientMessage received.\n", progname); - demo_mode_p = True; + clientmessage_response (si, window, False, + "DEMO ClientMessage received.", + "Demo mode."); + si->demo_mode_p = True; return True; } - fprintf (stderr, - "%s: %sDEMO ClientMessage received while active.\n", - progname, (verbose_p ? "## " : "")); -#endif + clientmessage_response (si, window, True, + "DEMO ClientMessage received while active.", + "already active."); +#endif /* !NO_DEMO_MODE */ + } + else if (type == XA_PREFS) + { +#ifdef NO_DEMO_MODE + clientmessage_response (si, window, True, + "not compiled with support for DEMO mode.", + "preferences mode not enabled."); +#else /* !NO_DEMO_MODE */ + if (until_idle_p) + { + clientmessage_response (si, window, False, + "PREFS ClientMessage received.", + "preferences mode."); + si->demo_mode_p = (Bool) 2; /* kludge, so sue me. */ + return True; + } + clientmessage_response (si, window, True, + "PREFS ClientMessage received while active.", + "already active."); +#endif /* !NO_DEMO_MODE */ } else if (type == XA_LOCK) { #ifdef NO_LOCKING - fprintf (stderr, "%s: %snot compiled with support for LOCK mode\n", - progname, (verbose_p ? "## " : "")); -#else - if (locking_disabled_p) - fprintf (stderr, - "%s: %sLOCK ClientMessage received, but locking is disabled.\n", - progname, (verbose_p ? "## " : "")); - else if (locked_p) - fprintf (stderr, - "%s: %sLOCK ClientMessage received while already locked.\n", - progname, (verbose_p ? "## " : "")); + clientmessage_response (si, window, True, + "not compiled with support for locking.", + "locking not enabled."); +#else /* !NO_LOCKING */ + if (si->locking_disabled_p) + clientmessage_response (si, window, True, + "LOCK ClientMessage received, but locking is disabled.", + "locking not enabled."); + else if (si->locked_p) + clientmessage_response (si, window, True, + "LOCK ClientMessage received while already locked.", + "already locked."); else { - locked_p = True; - if (verbose_p) - printf ("%s: LOCK ClientMessage received;%s locking.\n", - progname, until_idle_p ? " activating and" : ""); - - if (lock_id) /* we're doing it now, so lose the timeout */ + char buf [255]; + char *response = (until_idle_p + ? "activating and locking." + : "locking."); + si->locked_p = True; + si->selection_mode = 0; + sprintf (buf, "LOCK ClientMessage received; %s", response); + clientmessage_response (si, window, False, buf, response); + + if (si->lock_id) /* we're doing it now, so lose the timeout */ { - XtRemoveTimeOut (lock_id); - lock_id = 0; + XtRemoveTimeOut (si->lock_id); + si->lock_id = 0; } if (until_idle_p) - return True; + { + if (p->use_mit_saver_extension || p->use_sgi_saver_extension) + { + XForceScreenSaver (si->dpy, ScreenSaverActive); + return False; + } + else + { + return True; + } + } } -#endif +#endif /* !NO_LOCKING */ } else { + char buf [1024]; char *str; - str = XGetAtomName (dpy, type); + str = (type ? XGetAtomName(si->dpy, type) : 0); + if (str) - fprintf (stderr, - "%s: %sunrecognised screensaver ClientMessage %s received\n", - progname, (verbose_p ? "## " : ""), str); + { + if (strlen (str) > 80) + strcpy (str+70, "..."); + sprintf (buf, "unrecognised screensaver ClientMessage %s received.", + str); + free (str); + } else - fprintf (stderr, - "%s: %sunrecognised screensaver ClientMessage 0x%x received\n", - progname, (verbose_p ? "## " : ""), - event->xclient.data.l[0]); - if (str) XFree (str); + { + sprintf (buf, + "unrecognised screensaver ClientMessage 0x%x received.", + (unsigned int) event->xclient.data.l[0]); + } + + clientmessage_response (si, window, True, buf, buf); } return False; }