-/* xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xos.h>
+#include <netdb.h> /* for gethostbyname() */
#ifdef HAVE_XMU
# ifndef VMS
# include <X11/Xmu/Error.h>
#endif /* !HAVE_XMU */
#ifdef HAVE_XIDLE_EXTENSION
-#include <X11/extensions/xidle.h>
+# include <X11/extensions/xidle.h>
#endif /* HAVE_XIDLE_EXTENSION */
#include "xscreensaver.h"
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;
+static Atom XA_THROTTLE, XA_UNTHROTTLE;
Atom XA_DEMO, XA_PREFS;
\f
{ "-no-mit-extension", ".mitSaverExtension",XrmoptionNoArg, "off" },
{ "-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "on" },
{ "-no-sgi-extension", ".sgiSaverExtension",XrmoptionNoArg, "off" },
+ { "-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "on" },
+ { "-no-proc-interrupts", ".procInterrupts", XrmoptionNoArg, "off" },
{ "-splash", ".splash", XrmoptionNoArg, "on" },
{ "-no-splash", ".splash", XrmoptionNoArg, "off" },
{ "-nosplash", ".splash", XrmoptionNoArg, "off" },
fflush (stdout);
fflush (stderr);
fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-1998 by Jamie Zawinski <jwz@jwz.org>\n\
+xscreensaver %s, copyright (c) 1991-1999 by Jamie Zawinski <jwz@jwz.org>\n\
The standard Xt command-line options are accepted; other options include:\n\
\n\
-timeout <minutes> When the screensaver should activate.\n\
{
saver_info *si = global_si_kludge; /* I hate C so much... */
+ if (!real_stderr) real_stderr = stderr;
+
fprintf (real_stderr, "\n"
"#######################################"
"#######################################\n\n"
}
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());
+ fprintf (real_stderr,
+ "#######################################"
+ "#######################################\n\n");
+ fprintf (real_stderr,
+ " If at all possible, please re-run xscreensaver with the command line\n"
+ " arguments `-sync -verbose', and reproduce this bug. That will cause\n"
+ " xscreensaver to dump a `core' file to the current directory. Please\n"
+ " include the stack trace from that core file in your bug report.\n"
+ "\n"
+ " http://www.jwz.org/xscreensaver/bugs.html explains how to create the\n"
+ " most useful bug reports, and how to examine core files.\n"
+ "\n"
+ " The more information you can provide, the better. But please report\n"
+ " report this bug, regardless!\n"
+ "\n");
+ fprintf (real_stderr,
+ "#######################################"
+ "#######################################\n\n");
+
saver_exit (si, -1, 0);
}
}
return 0;
}
+
+/* This error handler is used only while the X connection is being set up;
+ after we've got a connection, we don't use this handler again. The only
+ reason for having this is so that we can present a more idiot-proof error
+ message than "cannot open display."
+ */
+static void
+startup_ehandler (String name, String type, String class,
+ String defalt, /* one can't even spel properly
+ in this joke of a language */
+ String *av, Cardinal *ac)
+{
+ char fmt[512];
+ String p[10];
+ saver_info *si = global_si_kludge; /* I hate C so much... */
+ XrmDatabase *db = XtAppGetErrorDatabase(si->app);
+ *fmt = 0;
+ XtAppGetErrorDatabaseText(si->app, name, type, class, defalt,
+ fmt, sizeof(fmt)-1, *db);
+
+ fprintf (stderr, "%s: ", blurb());
+
+ memset (p, 0, sizeof(p));
+ if (*ac > countof (p)) *ac = countof (p);
+ memcpy ((char *) p, (char *) av, (*ac) * sizeof(*av));
+ fprintf (stderr, fmt, /* Did I mention that I hate C? */
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
+ fprintf (stderr, "\n");
+
+ describe_uids (si, stderr);
+ fprintf (stderr, "\n"
+ "%s: Errors at startup are usually authorization problems.\n"
+ " Did you read the manual? Specifically, the parts\n"
+ " that talk about XAUTH, XDM, and root logins?\n"
+ "\n"
+ " http://www.jwz.org/xscreensaver/man.html\n"
+ "\n",
+ blurb());
+
+ fflush (stderr);
+ fflush (stdout);
+ exit (1);
+}
+
\f
/* The zillions of initializations.
*/
static void
privileged_initialization (saver_info *si, int *argc, char **argv)
{
+#ifndef NO_LOCKING
+ /* before hack_uid() for proper permissions */
+ lock_priv_init (*argc, argv, si->prefs.verbose_p);
+#endif /* NO_LOCKING */
+
+ hack_uid (si);
+}
+
+
+/* Figure out what locking mechanisms are supported.
+ */
+static void
+lock_initialization (saver_info *si, int *argc, char **argv)
+{
#ifdef NO_LOCKING
si->locking_disabled_p = True;
si->nolock_reason = "not compiled with locking support";
#else /* !NO_LOCKING */
si->locking_disabled_p = False;
- /* before hack_uid() for proper permissions */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
if (! lock_init (*argc, argv, si->prefs.verbose_p))
{
si->locking_disabled_p = True;
}
#endif /* NO_LOCKING */
-#ifndef NO_SETUID
hack_uid (si);
-#endif /* NO_SETUID */
}
Widget toplevel_shell;
XSetErrorHandler (saver_ehandler);
+
+ XtAppSetErrorMsgHandler (si->app, startup_ehandler);
toplevel_shell = XtAppInitialize (&si->app, progclass,
options, XtNumber (options),
argc, argv, defaults, 0, 0);
+ XtAppSetErrorMsgHandler (si->app, 0);
si->dpy = XtDisplay (toplevel_shell);
si->prefs.db = XtDatabase (si->dpy);
XA_DEMO = XInternAtom (si->dpy, "DEMO", False);
XA_PREFS = XInternAtom (si->dpy, "PREFS", False);
XA_LOCK = XInternAtom (si->dpy, "LOCK", False);
+ XA_THROTTLE = XInternAtom (si->dpy, "THROTTLE", False);
+ XA_UNTHROTTLE = XInternAtom (si->dpy, "UNTHROTTLE", False);
return toplevel_shell;
}
if (p->verbose_p)
fprintf (stderr,
- "%s %s, copyright (c) 1991-1998 "
+ "%s %s, copyright (c) 1991-1999 "
"by Jamie Zawinski <jwz@jwz.org>.\n",
progname, si->version);
}
}
- si->prefs.fading_possible_p = found_any_writable_cells;
+ si->fading_possible_p = found_any_writable_cells;
}
Bool server_has_xidle_extension_p = False;
Bool server_has_sgi_saver_extension_p = False;
Bool server_has_mit_saver_extension_p = False;
+ Bool system_has_proc_interrupts_p = False;
+ const char *piwhy = 0;
si->using_xidle_extension = p->use_xidle_extension;
si->using_sgi_saver_extension = p->use_sgi_saver_extension;
si->using_mit_saver_extension = p->use_mit_saver_extension;
+ si->using_proc_interrupts = p->use_proc_interrupts;
#ifdef HAVE_XIDLE_EXTENSION
server_has_xidle_extension_p = query_xidle_extension (si);
#ifdef HAVE_MIT_SAVER_EXTENSION
server_has_mit_saver_extension_p = query_mit_saver_extension (si);
#endif
+#ifdef HAVE_PROC_INTERRUPTS
+ system_has_proc_interrupts_p = query_proc_interrupts_available (si, &piwhy);
+#endif
if (!server_has_xidle_extension_p)
si->using_xidle_extension = False;
"%s: not using server's lame MIT-SCREEN-SAVER extension.\n",
blurb());
}
+
+ if (!system_has_proc_interrupts_p)
+ {
+ si->using_proc_interrupts = False;
+ if (p->verbose_p && piwhy)
+ fprintf (stderr, "%s: not using /proc/interrupts: %s.\n", blurb(),
+ piwhy);
+ }
+ else if (p->verbose_p)
+ {
+ if (si->using_proc_interrupts)
+ fprintf (stderr,
+ "%s: consulting /proc/interrupts for keyboard activity.\n",
+ blurb());
+ else
+ fprintf (stderr,
+ "%s: not consulting /proc/interrupts for keyboard activity.\n",
+ blurb());
+ }
}
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));
+ start_notice_events_timer (si, RootWindowOfScreen (si->screens[i].screen),
+ False);
if (p->verbose_p)
fprintf (stderr, " done.\n");
while (1)
{
+ Bool was_locked = False;
sleep_until_idle (si, True);
if (p->verbose_p)
maybe_reload_init_file (si);
- blank_screen (si);
+ if (! blank_screen (si))
+ {
+ /* We were unable to grab either the keyboard or mouse.
+ This means we did not (and must not) blank the screen.
+ If we were to blank the screen while some other program
+ is holding both the mouse and keyboard grabbed, then
+ we would never be able to un-blank it! We would never
+ see any events, and the display would be wedged.
+
+ So, just go around the loop again and wait for the
+ next bout of idleness.
+ */
+
+ fprintf (stderr,
+ "%s: unable to grab keyboard or mouse! Blanking aborted.\n",
+ blurb());
+ continue;
+ }
+
kill_screenhack (si);
- spawn_screenhack (si, True);
+
+ if (!si->throttled_p)
+ spawn_screenhack (si, True);
+ else if (p->verbose_p)
+ fprintf (stderr, "%s: not launching hack (throttled.)\n", blurb());
/* Don't start the cycle timer in demo mode. */
if (!si->demoing_p && p->cycle)
- si->cycle_id = XtAppAddTimeOut (si->app, p->cycle, cycle_timer,
+ si->cycle_id = XtAppAddTimeOut (si->app,
+ (si->selection_mode
+ /* see comment in cycle_timer() */
+ ? 1000 * 60 * 60
+ : p->cycle),
+ cycle_timer,
(XtPointer) si);
#ifndef NO_LOCKING
- if (!si->demoing_p && /* if not going into demo mode */
- p->lock_p && /* and locking is enabled */
- !si->locking_disabled_p && /* and locking is possible */
- p->lock_timeout == 0) /* and locking is not timer-deferred */
- si->locked_p = True; /* then lock right now. */
-
- /* locked_p might be true already because of the above, or because of
- the LOCK ClientMessage. But if not, and if we're supposed to lock
- after some time, set up a timer to do so.
- */
- if (p->lock_p &&
- !si->locked_p &&
- p->lock_timeout > 0)
- si->lock_id = XtAppAddTimeOut (si->app, p->lock_timeout,
- activate_lock_timer,
- (XtPointer) si);
+ {
+ Time lock_timeout = p->lock_timeout;
+
+ if (si->emergency_lock_p && p->lock_p && lock_timeout)
+ {
+ int secs = p->lock_timeout / 1000;
+ if (p->verbose_p)
+ fprintf (stderr,
+ "%s: locking now, instead of waiting for %d:%02d:%02d.\n",
+ blurb(),
+ (secs / (60 * 60)), ((secs / 60) % 60), (secs % 60));
+ lock_timeout = 0;
+ }
+
+ si->emergency_lock_p = False;
+
+ if (!si->demoing_p && /* if not going into demo mode */
+ p->lock_p && /* and locking is enabled */
+ !si->locking_disabled_p && /* and locking is possible */
+ lock_timeout == 0) /* and locking is not timer-deferred */
+ si->locked_p = True; /* then lock right now. */
+
+ /* locked_p might be true already because of the above, or because of
+ the LOCK ClientMessage. But if not, and if we're supposed to lock
+ after some time, set up a timer to do so.
+ */
+ if (p->lock_p &&
+ !si->locked_p &&
+ lock_timeout > 0)
+ si->lock_id = XtAppAddTimeOut (si->app, lock_timeout,
+ activate_lock_timer,
+ (XtPointer) si);
+ }
#endif /* !NO_LOCKING */
saver_screen_info *ssi = si->default_screen;
if (si->locking_disabled_p) abort ();
+ was_locked = True;
si->dbox_up_p = True;
suspend_screenhack (si, True);
XUndefineCursor (si->dpy, ssi->screensaver_window);
unblank_screen (si);
si->locked_p = False;
+ si->emergency_lock_p = False;
si->demoing_p = 0;
si->selection_mode = 0;
+ /* If we're throttled, and the user has explicitly unlocked the screen,
+ then unthrottle. If we weren't locked, then don't unthrottle
+ automatically, because someone might have just bumped the desk... */
+ if (was_locked)
+ {
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+ }
+
if (si->cycle_id)
{
XtRemoveTimeOut (si->cycle_id);
process_command_line (si, &argc, argv);
print_banner (si);
- initialize_per_screen_info (si, shell); /* also sets p->fading_possible_p */
+ load_init_file (p); /* must be before initialize_per_screen_info() */
+ initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
+
+ /* We can only issue this warnings now. */
+ if (p->verbose_p && !si->fading_possible_p && (p->fade_p || p->unfade_p))
+ fprintf (stderr,
+ "%s: there are no PseudoColor or GrayScale visuals.\n"
+ "%s: ignoring the request for fading/unfading.\n",
+ blurb(), blurb());
for (i = 0; i < si->nscreens; i++)
if (ensure_no_screensaver_running (si->dpy, si->screens[i].screen))
exit (1);
- load_init_file (p);
+ lock_initialization (si, &argc, argv);
if (p->xsync_p) XSynchronize (si->dpy, True);
blurb_timestamp_p = p->timestamp_p; /* kludge */
L++;
XChangeProperty (si->dpy, w, XA_SCREENSAVER_RESPONSE, XA_STRING, 8,
- PropModeReplace, proto, L);
+ PropModeReplace, (unsigned char *) proto, L);
XSync (si->dpy, False);
free (proto);
}
Bool
handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
{
+ saver_preferences *p = &si->prefs;
Atom type = 0;
Window window = event->xclient.window;
"activating.");
si->selection_mode = 0;
si->demoing_p = False;
+
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
{
XForceScreenSaver (si->dpy, ScreenSaverActive);
{
if (! until_idle_p)
{
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
clientmessage_response(si, window, False,
"DEACTIVATE ClientMessage received.",
"deactivating.");
"cycling.");
si->selection_mode = 0; /* 0 means randomize when its time. */
si->demoing_p = False;
+
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
if (si->cycle_id)
XtRemoveTimeOut (si->cycle_id);
si->cycle_id = 0;
si->selection_mode = (type == XA_NEXT ? -1 : -2);
si->demoing_p = False;
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
if (! until_idle_p)
{
if (si->cycle_id)
si->selection_mode = which;
si->demoing_p = False;
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
if (! until_idle_p)
{
if (si->cycle_id)
XSync (si->dpy, False);
}
+ fflush (stdout);
+ fflush (stderr);
+ if (real_stdout) fflush (real_stdout);
+ if (real_stderr) fflush (real_stderr);
/* make sure error message shows up before exit. */
if (real_stderr && stderr != real_stderr)
dup2 (fileno(real_stderr), fileno(stderr));
si->selection_mode = which;
si->demoing_p = True;
+ if (si->throttled_p && p->verbose_p)
+ fprintf (stderr, "%s: unthrottled.\n", blurb());
+ si->throttled_p = False;
+
return True;
}
}
#endif /* !NO_LOCKING */
}
+ else if (type == XA_THROTTLE)
+ {
+ if (si->throttled_p)
+ clientmessage_response (si, window, True,
+ "THROTTLE ClientMessage received, but "
+ "already throttled.",
+ "already throttled.");
+ else
+ {
+ char buf [255];
+ char *response = "throttled.";
+ si->throttled_p = True;
+ si->selection_mode = 0;
+ si->demoing_p = False;
+ sprintf (buf, "THROTTLE ClientMessage received; %s", response);
+ clientmessage_response (si, window, False, buf, response);
+
+ if (! until_idle_p)
+ {
+ if (si->cycle_id)
+ XtRemoveTimeOut (si->cycle_id);
+ si->cycle_id = 0;
+ cycle_timer ((XtPointer) si, 0);
+ }
+ }
+ }
+ else if (type == XA_UNTHROTTLE)
+ {
+ if (! si->throttled_p)
+ clientmessage_response (si, window, True,
+ "UNTHROTTLE ClientMessage received, but "
+ "not throttled.",
+ "not throttled.");
+ else
+ {
+ char buf [255];
+ char *response = "unthrottled.";
+ si->throttled_p = False;
+ si->selection_mode = 0;
+ si->demoing_p = False;
+ sprintf (buf, "UNTHROTTLE ClientMessage received; %s", response);
+ clientmessage_response (si, window, False, buf, response);
+
+ if (! until_idle_p)
+ {
+ if (si->cycle_id)
+ XtRemoveTimeOut (si->cycle_id);
+ si->cycle_id = 0;
+ cycle_timer ((XtPointer) si, 0);
+ }
+ }
+ }
else
{
char buf [1024];
{
int i, j;
static const char *exts[][2] = {
- { "SCREEN_SAVER", "SGI Screen-Saver" },
- { "SCREEN-SAVER", "SGI Screen-Saver" },
- { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
- { "XIDLE", "XIdle" },
- { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
- { "READDISPLAY", "SGI Read-Display" },
- { "MIT-SHM", "Shared Memory" },
- { "DOUBLE-BUFFER", "Double-Buffering" },
- { "DPMS", "Power Management" },
- { "GLX", "GLX" }
+ { "SCREEN_SAVER", "SGI Screen-Saver" },
+ { "SCREEN-SAVER", "SGI Screen-Saver" },
+ { "MIT-SCREEN-SAVER", "MIT Screen-Saver" },
+ { "XIDLE", "XIdle" },
+ { "SGI-VIDEO-CONTROL", "SGI Video-Control" },
+ { "READDISPLAY", "SGI Read-Display" },
+ { "MIT-SHM", "Shared Memory" },
+ { "DOUBLE-BUFFER", "Double-Buffering" },
+ { "DPMS", "Power Management" },
+ { "GLX", "GLX" },
+ { "XFree86-VidModeExtension", "XF86 Video-Mode" }
};
fprintf (stderr, "%s: running on display \"%s\"\n", blurb(),
}
}
}
+
+Bool
+display_is_on_console_p (saver_info *si)
+{
+ Bool not_on_console = True;
+ char *dpystr = DisplayString (si->dpy);
+ char *tail = (char *) strchr (dpystr, ':');
+ if (! tail || strncmp (tail, ":0", 2))
+ not_on_console = True;
+ else
+ {
+ char dpyname[255], localname[255];
+ strncpy (dpyname, dpystr, tail-dpystr);
+ dpyname [tail-dpystr] = 0;
+ if (!*dpyname ||
+ !strcmp(dpyname, "unix") ||
+ !strcmp(dpyname, "localhost"))
+ not_on_console = False;
+ else if (gethostname (localname, sizeof (localname)))
+ not_on_console = True; /* can't find hostname? */
+ else
+ {
+ /* We have to call gethostbyname() on the result of gethostname()
+ because the two aren't guarenteed to be the same name for the
+ same host: on some losing systems, one is a FQDN and the other
+ is not. Here in the wide wonderful world of Unix it's rocket
+ science to obtain the local hostname in a portable fashion.
+
+ And don't forget, gethostbyname() reuses the structure it
+ returns, so we have to copy the fucker before calling it again.
+ Thank you master, may I have another.
+ */
+ struct hostent *h = gethostbyname (dpyname);
+ if (!h)
+ not_on_console = True;
+ else
+ {
+ char hn [255];
+ struct hostent *l;
+ strcpy (hn, h->h_name);
+ l = gethostbyname (localname);
+ not_on_console = (!l || !!(strcmp (l->h_name, hn)));
+ }
+ }
+ }
+ return !not_on_console;
+}