-/* xscreensaver, Copyright (c) 1991-1993 Jamie Zawinski <jwz@lucid.com>
+/* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@mcom.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
/* ========================================================================
* 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.
* 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 or MIT-SCREENSAVER 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
* "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 implement (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.
* of the keypress events it's selecting for. This can make your X
* server wedge with "no more input buffers."
*
- * ========================================================================
- */
+ * ======================================================================== */
#if __STDC__
#include <stdlib.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
+#include <X11/Xmu/Error.h>
-#ifdef HAVE_XIDLE
+#ifdef HAVE_XIDLE_EXTENSION
#include <X11/extensions/xidle.h>
-#endif
+#endif /* HAVE_XIDLE_EXTENSION */
+
+#ifdef HAVE_SAVER_EXTENSION
+#include <X11/extensions/scrnsaver.h>
+#endif /* HAVE_SAVER_EXTENSION */
#include "xscreensaver.h"
#if defined(SVR4) || defined(SYSV)
# define srandom(i) srand((unsigned int)(i))
#else
+# ifndef __linux
extern void srandom P((int)); /* srand() is in stdlib.h... */
+# endif
#endif
extern char *get_string_resource P((char *, char *));
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 notice_events_timer P((XtPointer closure, XtIntervalId *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));
extern void save_argv P((int argc, char **argv));
+extern void initialize_stderr P((void));
char *screensaver_version;
char *progname;
extern Time timeout;
extern Time cycle;
+#ifndef NO_LOCKING
extern Time passwd_timeout;
+#endif
extern Time pointer_timeout;
extern Time notice_events_timeout;
extern XtIntervalId lock_id, cycle_id;
-Bool use_xidle;
+Bool use_xidle_extension;
+Bool use_saver_extension;
Bool verbose_p;
Bool lock_p, locked_p;
extern int fade_seconds, fade_ticks;
extern Bool install_cmap_p;
extern Bool locking_disabled_p;
+extern char *nolock_reason;
extern Bool demo_mode_p;
extern Bool dbox_up_p;
extern int next_mode_p;
+#ifdef HAVE_SAVER_EXTENSION
+int saver_ext_event_number = 0;
+int saver_ext_error_number = 0;
+#endif /* HAVE_SAVER_EXTENSION */
+
static time_t initial_delay;
extern Atom XA_VROOT, XA_XSETROOT_ID;
#ifdef NO_MOTIF /* kludge */
Bool demo_mode_p = 0;
Bool dbox_up_p = 0;
+#ifndef NO_LOCKING
Time passwd_timeout = 0;
#endif
+#endif
\f
#ifdef NO_DEMO_MODE
#endif
\f
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" }
+ { "-timeout", ".timeout", XrmoptionSepArg, 0 },
+ { "-cycle", ".cycle", XrmoptionSepArg, 0 },
+ { "-idelay", ".initialDelay", XrmoptionSepArg, 0 },
+ { "-visual", ".visualID", XrmoptionSepArg, 0 },
+ { "-lock-timeout", ".lockTimeout", XrmoptionSepArg, 0 },
+ { "-install", ".installColormap", XrmoptionNoArg, "on" },
+ { "-no-install", ".installColormap", XrmoptionNoArg, "off" },
+ { "-verbose", ".verbose", XrmoptionNoArg, "on" },
+ { "-silent", ".verbose", XrmoptionNoArg, "off" },
+ { "-xidle-extension", ".xidleExtension", XrmoptionNoArg, "on" },
+ { "-no-xidle-extension", ".xidleExtension", XrmoptionNoArg, "off" },
+ { "-ss-extension", ".saverExtension", XrmoptionNoArg, "on" },
+ { "-no-ss-extension", ".saverExtension", XrmoptionNoArg, "off" },
+ { "-lock", ".lock", XrmoptionNoArg, "on" },
+ { "-no-lock", ".lock", XrmoptionNoArg, "off" }
};
static char *defaults[] = {
};
static void
-do_help ()
+do_help P((void))
{
printf ("\
-xscreensaver %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
+xscreensaver %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@mcom.com>.\n\
The standard Xt command-line options are accepted; other options include:\n\
\n\
- -timeout <minutes> when the screensaver should activate\n\
- -cycle <minutes> how long to let each hack run\n\
- -idelay <seconds> 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 <minutes> grace period before locking; default 0\n\
- -help this message\n\
+ -timeout <minutes> When the screensaver should activate.\n\
+ -cycle <minutes> How long to let each hack run.\n\
+ -idelay <seconds> How long to sleep before startup.\n\
+ -visual <id-or-class> Which X visual to run on.\n\
+ -demo Enter interactive demo mode on startup.\n\
+ -install Install a private colormap.\n\
+ -no-install Don't.\n\
+ -verbose Be loud.\n\
+ -silent Don't.\n\
+ -xidle-extension Use the R5 XIdle server extension.\n\
+ -no-xidle-extension Don't.\n\
+ -saver-extension Use the R6 MIT-SCREEN-SAVER server extension.\n\
+ -no-saver-extension Don't.\n\
+ -lock Require a password before deactivating.\n\
+ -no-lock Don't.\n\
+ -lock-timeout <minutes> Grace period before locking; default 0.\n\
+ -help This message.\n\
\n\
Use the `xscreensaver-command' program to control a running screensaver.\n\
\n\
screensaver_version);
#ifdef NO_LOCKING
- printf("Support for locking was not enabled at compile-time.\n");
+ 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");
+ printf ("Support for demo mode was not enabled at compile-time.\n");
#endif
+#if !defined(HAVE_XIDLE_EXTENSION) && !defined(HAVE_SAVER_EXTENSION)
+ printf ("Support for the XIDLE and MIT-SCREEN-SAVER server extensions\
+ was not\n\enabled at compile-time.\n");
+#endif /* !HAVE_XIDLE_EXTENSION && !HAVE_SAVER_EXTENSION */
fflush (stdout);
exit (1);
static void
-get_screenhacks ()
+get_screenhacks P((void))
{
char *data[3];
int i, hacks_size = 10;
static void
-get_resources ()
+get_resources P((void))
{
- visual = get_visual_resource (dpy, "visual", "Visual");
+ /* Note: we can't use the resource ".visual" because Xt is SO FUCKED. */
+ visual = get_visual_resource (dpy, "visualID", "VisualID");
timeout = 1000 * get_minutes_resource ("timeout", "Time");
cycle = 1000 * get_minutes_resource ("cycle", "Time");
lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "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;
+#ifndef NO_LOCKING
+ passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
if (passwd_timeout == 0) passwd_timeout = 30000;
+#endif
+ if (timeout < 10000) timeout = 10000;
+ if (cycle != 0 && cycle < 2000) cycle = 2000;
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;
#ifdef NO_LOCKING
locking_disabled_p = True;
+ nolock_reason = "not compiled with locking support";
if (lock_p)
{
lock_p = False;
#else /* ! NO_LOCKING */
if (lock_p && locking_disabled_p)
{
- fprintf (stderr, "%s: %slocking is disabled.\n", progname,
- (verbose_p ? "## " : ""));
+ fprintf (stderr, "%s: %slocking is disabled (%s).\n", progname,
+ (verbose_p ? "## " : ""), nolock_reason);
lock_p = False;
}
#endif /* ! NO_LOCKING */
- /* don't set use_xidle unless it is explicitly specified */
- if (get_string_resource ("xidle", "Boolean"))
- use_xidle = get_boolean_resource ("xidle", "Boolean");
+ /* don't set use_xidle_extension unless it is explicitly specified */
+ if (get_string_resource ("xidleExtension", "Boolean"))
+ use_xidle_extension = get_boolean_resource ("xidleExtension", "Boolean");
else
-#ifdef HAVE_XIDLE /* pick a default */
- use_xidle = True;
-#else
- use_xidle = False;
-#endif
+#ifdef HAVE_XIDLE_EXTENSION /* pick a default */
+ use_xidle_extension = True;
+#else /* !HAVE_XIDLE_EXTENSION */
+ use_xidle_extension = False;
+#endif /* !HAVE_XIDLE_EXTENSION */
+
+ /* don't set use_saver_extension unless it is explicitly specified */
+ if (get_string_resource ("saverExtension", "Boolean"))
+ use_xidle_extension = get_boolean_resource ("saverExtension", "Boolean");
+ else
+#ifdef HAVE_SAVER_EXTENSION /* pick a default */
+ use_saver_extension = True;
+#else /* !HAVE_SAVER_EXTENSION */
+ use_saver_extension = False;
+#endif /* !HAVE_SAVER_EXTENSION */
+
+
get_screenhacks ();
}
char *
-timestring ()
+timestring P((void))
{
long now = time ((time_t *) 0);
char *str = (char *) ctime (&now);
extern Bool lock_init P((void));
#endif
-static void initialize ();
-static void main_loop ();
-static void initialize ();
+static void initialize P((int argc, char **argv));
+static void main_loop P((void));
void
main (argc, argv)
}
+static int
+saver_ehandler (dpy, error)
+ Display *dpy;
+ XErrorEvent *error;
+{
+ fprintf (real_stderr, "\nX error in %s:\n", progname);
+ if (XmuPrintDefaultErrorMessage (dpy, error, real_stderr))
+ exit (-1);
+ else
+ fprintf (real_stderr, " (nonfatal.)\n");
+ return 0;
+}
+
static void
+#if __STDC__
+initialize_connection (int argc, char **argv)
+#else
initialize_connection (argc, argv)
int argc;
char **argv;
+#endif
{
toplevel_shell = XtAppInitialize (&app, progclass,
options, XtNumber (options),
XA_LOCK = XInternAtom (dpy, "LOCK", False);
}
+#ifdef HAVE_SAVER_EXTENSION
+
+static int
+ignore_all_errors_ehandler (dpy, error)
+ Display *dpy;
+ XErrorEvent *error;
+{
+ return 0;
+}
+
+static void
+init_saver_extension ()
+{
+ XID kill_id;
+ Atom kill_type;
+ Window root = RootWindowOfScreen (screen);
+ XScreenSaverInfo *info;
+ Pixmap blank_pix = XCreatePixmap (dpy, root, 1, 1, 1);
+
+ /* Kill off the old MIT-SCREEN-SAVER client if there is one.
+ This tends to generate X errors, though (possibly due to a bug
+ in the server extension itself?) so just ignore errors here. */
+ if (XScreenSaverGetRegistered (dpy, XScreenNumberOfScreen (screen),
+ &kill_id, &kill_type)
+ && kill_id != blank_pix)
+ {
+ int (*old_handler) ();
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+ XKillClient (dpy, kill_id);
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ }
+
+ XScreenSaverSelectInput (dpy, root, ScreenSaverNotifyMask);
+
+ XScreenSaverRegister (dpy, XScreenNumberOfScreen (screen),
+ (XID) blank_pix, XA_PIXMAP);
+ info = XScreenSaverAllocInfo ();
+
+#if 0
+ /* #### I think this is noticing that the saver is on, and replacing it
+ without turning it off first. */
+ saver = info->window;
+ if (info->state == ScreenSaverOn)
+ {
+ if (info->kind != ScreenSaverExternal)
+ {
+ XResetScreenSaver (display);
+ XActivateScreenSaver (display);
+ }
+ StartSaver ();
+ }
+#endif
+}
+#endif /* HAVE_SAVER_EXTENSION */
+
+
extern void init_sigchld P((void));
static void
#ifdef NO_LOCKING
locking_disabled_p = True;
+ nolock_reason = "not compiled with locking support";
#else
locking_disabled_p = False;
if (! lock_init ()) /* before hack_uid() for proper permissions */
- locking_disabled_p = True;
+ {
+ locking_disabled_p = True;
+ nolock_reason = "error getting password";
+ }
#endif
hack_uid ();
}
save_argv (argc, argv);
initialize_connection (argc, argv);
+ ensure_no_screensaver_running ();
+ if (verbose_p)
+ printf ("\
+%s %s, copyright (c) 1991-1995 by Jamie Zawinski <jwz@mcom.com>.\n\
+ pid = %d.\n", progname, screensaver_version, getpid ());
ensure_no_screensaver_running ();
+
demo_mode_p = initial_demo_mode_p;
screensaver_window = 0;
cursor = 0;
cycle_id = 0;
lock_id = 0;
locked_p = False;
- if (use_xidle)
+
+ if (use_saver_extension)
{
-#ifdef HAVE_XIDLE
+#ifdef HAVE_SAVER_EXTENSION
+ if (! XScreenSaverQueryExtension (dpy,
+ &saver_ext_event_number,
+ &saver_ext_error_number))
+ {
+ fprintf (stderr,
+ "%s: %sdisplay %s does not support the MIT-SCREEN-SAVER extension.\n",
+ progname, (verbose_p ? "## " : ""), DisplayString (dpy));
+ use_saver_extension = False;
+ }
+ else if (use_xidle_extension)
+ {
+ fprintf (stderr,
+ "%s: %sMIT-SCREEN-SAVER extension used instead of XIDLE extension.\n",
+ progname, (verbose_p ? "## " : ""));
+ use_xidle_extension = False;
+ }
+#else /* !HAVE_SAVER_EXTENSION */
+ fprintf (stderr,
+ "%s: %snot compiled with support for the MIT-SCREEN-SAVER extension.\n",
+ progname, (verbose_p ? "## " : ""));
+ use_saver_extension = False;
+#endif /* !HAVE_SAVER_EXTENSION */
+ }
+
+ if (use_xidle_extension)
+ {
+#ifdef HAVE_XIDLE_EXTENSION
int first_event, first_error;
if (! XidleQueryExtension (dpy, &first_event, &first_error))
{
fprintf (stderr,
- "%s: display %s does not support the XIdle extension.\n",
- progname, DisplayString (dpy));
- use_xidle = 0;
+ "%s: %sdisplay %s does not support the XIdle extension.\n",
+ progname, (verbose_p ? "## " : ""), DisplayString (dpy));
+ 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: %snot compiled with support for XIdle.\n",
+ progname, (verbose_p ? "## " : ""));
+ use_xidle_extension = False;
+#endif /* !HAVE_XIDLE_EXTENSION */
}
init_sigchld ();
- if (verbose_p)
- printf ("\
-%s %s, copyright (c) 1991-1993 by Jamie Zawinski <jwz@lucid.com>.\n\
- pid = %d.\n", progname, screensaver_version, getpid ());
-
disable_builtin_screensaver ();
+#ifdef HAVE_SAVER_EXTENSION
+ if (use_saver_extension)
+ init_saver_extension ();
+#endif /* HAVE_SAVER_EXTENSION */
+
+ if (verbose_p && use_saver_extension)
+ fprintf (stderr, "%s: using MIT-SCREEN-SAVER server extension.\n",
+ progname);
+ if (verbose_p && use_xidle_extension)
+ fprintf (stderr, "%s: using XIdle server extension.\n",
+ progname);
+
+ initialize_stderr ();
+ XSetErrorHandler (saver_ehandler);
+
if (initial_demo_mode_p)
/* If the user wants demo mode, don't wait around before doing it. */
initial_delay = 0;
-#ifdef HAVE_XIDLE
- if (! use_xidle)
-#endif
+ if (!use_xidle_extension && !use_saver_extension)
{
if (initial_delay)
{
if (verbose_p)
{
printf ("%s: waiting for %d second%s...", progname,
- initial_delay, (initial_delay == 1 ? "" : "s"));
+ (int) initial_delay, (initial_delay == 1 ? "" : "s"));
fflush (stdout);
}
sleep (initial_delay);
blank_screen ();
spawn_screenhack (True);
if (cycle)
- cycle_id = XtAppAddTimeOut (app, cycle, cycle_timer, 0);
+ cycle_id = XtAppAddTimeOut (app, cycle, (XtPointer)cycle_timer, 0);
#ifndef NO_LOCKING
if (lock_p && lock_timeout == 0)
locked_p = True;
if (lock_p && !locked_p)
/* locked_p might be true already because of ClientMessage */
- lock_id = XtAppAddTimeOut (app,lock_timeout,activate_lock_timer,0);
+ lock_id = XtAppAddTimeOut (app,lock_timeout,
+ (XtPointer)activate_lock_timer,0);
#endif
PASSWD_INVALID:
Bool val;
if (locking_disabled_p) abort ();
dbox_up_p = True;
- ungrab_keyboard_and_mouse ();
+
+ /* We used to ungrab the keyboard here, before calling unlock_p()
+ to pop up the dialog box. This left the keyboard ungrabbed
+ for a small window, during an insecure state. Bennett Todd
+ was seeing the bahavior that, when the load was high, he could
+ actually get characters through to a shell under the saver
+ window (he accidentally typed his password there...)
+
+ So the ungrab has been moved down into pop_passwd_dialog()
+ just after the server is grabbed, closing this window
+ entirely.
+ */
+ /* ungrab_keyboard_and_mouse (); */
+
suspend_screenhack (True);
XUndefineCursor (dpy, screensaver_window);
if (verbose_p)
dbox_up_p = False;
XDefineCursor (dpy, screensaver_window, cursor);
suspend_screenhack (False);
+
+ /* I think this grab is now redundant, but it shouldn't hurt. */
grab_keyboard_and_mouse ();
+
if (! val)
goto PASSWD_INVALID;
locked_p = False;
fprintf (stderr,
"%s: %sunrecognised screensaver ClientMessage 0x%x received\n",
progname, (verbose_p ? "## " : ""),
- event->xclient.data.l[0]);
+ (unsigned int) event->xclient.data.l[0]);
if (str) XFree (str);
}
return False;