summaryrefslogtreecommitdiffstats
path: root/driver/xscreensaver.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/xscreensaver.c')
-rw-r--r--driver/xscreensaver.c214
1 files changed, 187 insertions, 27 deletions
diff --git a/driver/xscreensaver.c b/driver/xscreensaver.c
index 5eb7167..b5e85a6 100644
--- a/driver/xscreensaver.c
+++ b/driver/xscreensaver.c
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright © 1991-2023 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
@@ -8,7 +8,9 @@
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
+ * ==========================================================================
* XScreenSaver Daemon, version 6.
+ * ==========================================================================
*
* Having started its life in 1991, XScreenSaver acquired a lot of optional
* features to allow it to detect user activity, lock the screen and
@@ -101,6 +103,95 @@
* causes the screen to lock, and how video players request that blanking
* be inhibited. This program invokes "xscreensaver-command" as needed
* to pass those requests along to "xscreensaver" via ClientMessages.
+ *
+ *
+ * ==========================================================================
+ * Ancient History
+ * ==========================================================================
+ *
+ * As mentioned above, XScreenSaver version 6 (released in 2021) requires
+ * the XInput 2 server extension for idle detection and password input.
+ * In XScreenSaver 5 and earlier (1991 through 2020), idle-detection was
+ * far more convoluted. Basically, we selected input events on every window,
+ * and noticed when new windows were created so that we could track them too.
+ * Over the decades, there have also been three optional X11 server extensions
+ * that were applicable to screen saving:
+ *
+ * - XIdle
+ *
+ * This extension provided a function to poll the user's idle time.
+ * It was simple and direct and worked great. Therefore, it was
+ * removed from the X11 distribution in 1994, with the release of
+ * X11R6. https://bugs.freedesktop.org/show_bug.cgi?id=1419
+ *
+ * - SGI SCREEN_SAVER
+ *
+ * This extension sent two new events: "user is idle", and "user is no
+ * longer idle". It was simple and direct and worked great. But as
+ * the name implies, it only ever worked on Silicon Graphics machines.
+ * SGI became irrelevant around 1998 and went out of business in 2009.
+ *
+ * - MIT-SCREEN-SAVER
+ *
+ * This extension still exists, but it is useless to us. Since it still
+ * exists, people sometimes ask why XScreenSaver no longer makes use of it.
+ * The MIT-SCREEN-SAVER extension took the following approach:
+ *
+ * - When the user is idle, immediately map full screen black windows
+ * on each screen.
+ *
+ * - Inform the screen saver client that the screen is now black.
+ *
+ * - When user activity occurs, unmap the windows and then inform the
+ * screen saver client.
+ *
+ * The screen saver client was able to specify a few parameters of that
+ * window, like visual and depth, but that was all.
+ *
+ * The extension was designed with the assumption that a screen saver would
+ * render onto the provided window. However:
+ *
+ * - OpenGL programs may require different visuals than 2D X11 programs,
+ * and you can't change the visual of a window after it has been
+ * created.
+ *
+ * - The extension mapped one window per X11 "Screen", which, in this
+ * modern world, tend to span the entire virtual desktop; whereas
+ * XScreenSaver runs savers full screen on each *monitor* instead.
+ * In other words, it was incompatible with Xinerama / RANDR.
+ *
+ * - Since this extension mapped its own full-screen black windows and
+ * informed us of that after the fact, it precluded "fade" and "unfade"
+ * animations from being implemented.
+ *
+ * - Since it only told us when the user was idle or non-idle, it
+ * precluded the "hysteresis" option from being implemented (ignoring
+ * tiny mouse motions).
+ *
+ * In summary, it created its windows too early, removed them too late,
+ * created windows of the wrong quantity and wrong shape, could not create
+ * them with the proper visuals, and delivered too little information about
+ * what caused the user activity.
+ *
+ * Also, the MIT-SCREEN-SAVER extension was flaky, and using it at all led
+ * to frequent server crashes. Worse, those server crashes were highly
+ * dependent on your particular video driver.
+ *
+ * So that's why, even if the server supports the MIT-SCREEN-SAVER
+ * extension, we don't use it.
+ *
+ * Some video players attempt to inhibit blanking during playback by
+ * calling XResetScreenSaver and/or XScreenSaverSuspend, which affect
+ * only the X server's *built-in* screen saver, and the window created
+ * by the MIT-SCREEN-SAVER extension. To rely upon those calls alone
+ * and then call it a day would betray a willful ignorance of why the
+ * MIT-SCREEN-SAVER extension is a useless foundation upon which to
+ * build a screen saver or screen locker.
+ *
+ * Fortunately, every modern video player and web browser also makes
+ * use of systemd / logind / dbus to request blanking inhibition, and
+ * we receive those requests via our systemd integration in
+ * xscreensaver-systemd (which see).
*/
#ifdef HAVE_CONFIG_H
@@ -134,6 +225,10 @@
# include <sys/wait.h> /* for waitpid() and associated macros */
#endif
+#ifndef HAVE_XINPUT
+# error The XInput2 extension is required
+#endif
+
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
@@ -168,6 +263,7 @@ static const char *version_number = 0;
/* Preferences. */
static Bool lock_p = False;
static Bool locking_disabled_p = False;
+static Bool blanking_disabled_p = False;
static unsigned int blank_timeout = 0;
static unsigned int lock_timeout = 0;
static unsigned int pointer_hysteresis = 0;
@@ -456,8 +552,10 @@ handle_sigchld (Display *dpy, Bool blanked_p)
int ac = 0;
av[ac++] = SAVER_GFX_PROGRAM;
av[ac++] = "--emergency";
- if (verbose_p) av[ac++] = "--verbose";
- if (debug_p) av[ac++] = "--debug";
+ if (verbose_p) av[ac++] = "--verbose";
+ if (verbose_p > 1) av[ac++] = "--verbose";
+ if (verbose_p > 2) av[ac++] = "--verbose";
+ if (debug_p) av[ac++] = "--debug";
av[ac] = 0;
fprintf (stderr, "%s: pid %lu: " SAVER_GFX_PROGRAM
" exited unexpectedly %s: re-launching\n",
@@ -582,7 +680,9 @@ print_banner(void)
/* Hey jerks, the only time someone will see this particular
message is if they are running xscreensaver with '-log' in
order to send me a bug report, and they had damned well
- better try the latest release before they do that. */
+ better try the latest release before they do that --
+ even if your perma-out-of-date distro does not make that
+ easily available to them. */
"\t ###################################################\n"
"\t ### ###\n"
"\t ### THIS VERSION IS VERY OLD! PLEASE UPGRADE! ###\n"
@@ -684,20 +784,24 @@ static void init_line_handler (int lineno,
const char *key, const char *val,
void *closure)
{
+ if (*key == '*' || *key == '.') key++; /* Xrm wildcards */
+
if (!strcmp (key, "verbose")) verbose_p = !strcasecmp (val, "true");
else if (!strcmp (key, "splash")) splash_p = !strcasecmp (val, "true");
else if (!strcmp (key, "lock")) lock_p = !strcasecmp (val, "true");
+ else if (!strcmp (key, "mode")) blanking_disabled_p =
+ !strcasecmp (val, "off");
else if (!strcmp (key, "timeout"))
{
int t = parse_time (val);
if (t > 0) blank_timeout = t;
}
- if (!strcmp (key, "lockTimeout"))
+ else if (!strcmp (key, "lockTimeout"))
{
int t = parse_time (val);
if (t >= 0) lock_timeout = t;
}
- if (!strcmp (key, "pointerHysteresis"))
+ else if (!strcmp (key, "pointerHysteresis"))
{
int i = atoi (val);
if (i >= 0)
@@ -882,7 +986,9 @@ ensure_no_screensaver_running (Display *dpy)
&& type != None
&& (!strcmp ((char *) version, "gnome-screensaver") ||
!strcmp ((char *) version, "mate-screensaver") ||
- !strcmp ((char *) version, "cinnamon-screensaver")))
+ !strcmp ((char *) version, "cinnamon-screensaver") ||
+ !strcmp ((char *) version, "xfce4-screensaver") ||
+ !strcmp ((char *) version, "light-locker")))
{
fprintf (stderr,
"%s: \"%s\" is already running on display %s"
@@ -1130,7 +1236,7 @@ ungrab_mouse (Display *dpy)
/* Some remote desktop clients (e.g., "rdesktop") hold the keyboard GRABBED the
whole time they have focus! This is idiotic because the whole point of
grabbing is to get events when you do *not* have focus, so grabbing only
- when* you have focus is redundant. Anyway, that prevents us from getting a
+ *when* you have focus is redundant. Anyway, that prevents us from getting a
keyboard grab. It turns out that for some of these apps, de-focusing them
forces them to release their grab.
@@ -1261,14 +1367,14 @@ grab_keyboard_and_mouse (Screen *screen)
because I'm not completely convinced it is a safe thing to do.
*/
- if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */
+ if (kstatus != GrabSuccess) /* Do not blank without a kbd grab. */
{
/* If we didn't get both grabs, release the one we did get. */
ungrab_keyboard_and_mouse (dpy);
return False;
}
- return True; /* Grab is good, go ahead and blank. */
+ return True; /* Grab is good, go ahead and blank. */
}
@@ -1304,6 +1410,8 @@ static void
maybe_disable_locking (Display *dpy)
{
const char *why = 0;
+ Bool wayland_p = (getenv ("WAYLAND_DISPLAY") ||
+ getenv ("WAYLAND_SOCKET"));
# ifdef NO_LOCKING
why = "locking disabled at compile time";
@@ -1322,7 +1430,7 @@ maybe_disable_locking (Display *dpy)
/* X11 grabs don't work under Wayland's embedded X11 server. The Wayland
window manager lives at a higher level than the X11 emulation layer. */
- if (!why && getenv ("WAYLAND_DISPLAY"))
+ if (!why && wayland_p)
why = "cannot lock securely under Wayland";
if (!why)
@@ -1344,6 +1452,25 @@ maybe_disable_locking (Display *dpy)
fprintf (stderr, "%s: DEBUG MODE: allowing locking anyway!\n",
blurb());
}
+ else if (wayland_p)
+ {
+ const char *s = blurb();
+ locking_disabled_p = True;
+
+ /* Maybe we should just refuse to launch instead? We can operate
+ properly only if the user uses only X11 programs, and doesn't
+ want to lock the screen.
+ */
+ fprintf (stderr, "\n"
+ "%s: WARNING: Wayland is not supported.\n"
+ "\n"
+ "%s: Under Wayland, idle-detection fails when non-X11\n"
+ "%s: programs are selected, meaning the screen may\n"
+ "%s: blank prematurely. Also, locking is impossible.\n"
+ "%s: See the manual for instructions on configuring\n"
+ "%s: your system to use X11 instead of Wayland.\n\n",
+ s, s, s, s, s, s);
+ }
else
{
locking_disabled_p = True;
@@ -1377,6 +1504,14 @@ main_loop (Display *dpy)
if (! init_xinput (dpy, &xi_opcode))
saver_exit (1);
+ /* Disable server built-in screen saver. */
+ XSetScreenSaver (dpy, 0, 0, 0, 0);
+ XForceScreenSaver (dpy, ScreenSaverReset);
+
+ /* It would be nice to sync the server's DPMS settings here to what is
+ specified in the .xscreensaver file, but xscreensaver-gfx handles that,
+ so that won't happen until the first time the screen blanks. */
+
create_daemon_window (dpy);
handle_signals();
@@ -1402,7 +1537,7 @@ main_loop (Display *dpy)
saver_auth_pid = fork_and_exec (dpy, ac, av);
}
-# ifdef HAVE_LIBSYSTEMD
+# if defined(HAVE_LIBSYSTEMD) || defined(HAVE_LIBELOGIND)
/* Launch xscreensaver-systemd at startup. */
{
char *av[10];
@@ -1413,7 +1548,7 @@ main_loop (Display *dpy)
av[ac] = 0;
saver_systemd_pid = fork_and_exec (dpy, ac, av);
}
-# endif /* HAVE_LIBSYSTEMD */
+# endif /* HAVE_LIBSYSTEMD || HAVE_LIBELOGIND */
/* X11 errors during startup initialization were fatal.
@@ -1629,9 +1764,9 @@ main_loop (Display *dpy)
linking with additional libraries, doing additional X
protocol, and also some finicky error handling, since
the DPMS extension is a pain in the ass. So instead,
- I made xscreensaver-command do that instead. This
- somewhat breaks the abstraction of ClientMessage
- handling, but it's more robust. */
+ I made xscreensaver-command:reset_dpms_timer() do that
+ instead. This somewhat breaks the abstraction of
+ ClientMessage handling, but it's more robust. */
}
else if (msg == XA_LOCK)
{
@@ -1800,6 +1935,9 @@ main_loop (Display *dpy)
case XI_RawKeyRelease:
case XI_RawButtonPress:
case XI_RawButtonRelease:
+ case XI_RawTouchBegin:
+ case XI_RawTouchEnd:
+ case XI_RawTouchUpdate:
if (current_state != AUTH && /* logged by xscreensaver-auth */
(verbose_p > 1 ||
(verbose_p && now - active_at > 1)))
@@ -1892,7 +2030,8 @@ main_loop (Display *dpy)
(lock_p &&
now >= active_at + blank_timeout + lock_timeout)))
{
- fprintf (stderr, "%s: locking\n", blurb());
+ if (verbose_p)
+ fprintf (stderr, "%s: locking\n", blurb());
if (grab_keyboard_and_mouse (mouse_screen (dpy)))
{
current_state = LOCKED;
@@ -1909,16 +2048,25 @@ main_loop (Display *dpy)
else if (force_blank_p ||
now >= active_at + blank_timeout)
{
- fprintf (stderr, "%s: blanking\n", blurb());
- if (grab_keyboard_and_mouse (mouse_screen (dpy)))
+ if (blanking_disabled_p && !force_blank_p)
{
- current_state = BLANKED;
- blanked_at = now;
- store_saver_status (dpy, True, False, now);
+ if (verbose_p)
+ fprintf (stderr, "%s: not blanking: disabled\n", blurb());
}
else
- fprintf (stderr, "%s: unable to grab -- blanking aborted!\n",
- blurb());
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: blanking\n", blurb());
+ if (grab_keyboard_and_mouse (mouse_screen (dpy)))
+ {
+ current_state = BLANKED;
+ blanked_at = now;
+ store_saver_status (dpy, True, False, now);
+ }
+ else
+ fprintf (stderr, "%s: unable to grab -- blanking aborted!\n",
+ blurb());
+ }
}
if (current_state == BLANKED || current_state == LOCKED)
@@ -1930,9 +2078,11 @@ main_loop (Display *dpy)
char *av[20];
int ac = 0;
av[ac++] = SAVER_GFX_PROGRAM;
- if (first_time_p) av[ac++] = "--init";
- if (verbose_p) av[ac++] = "--verbose";
- if (debug_p) av[ac++] = "--debug";
+ if (first_time_p) av[ac++] = "--init";
+ if (verbose_p) av[ac++] = "--verbose";
+ if (verbose_p > 1) av[ac++] = "--verbose";
+ if (verbose_p > 2) av[ac++] = "--verbose";
+ if (debug_p) av[ac++] = "--debug";
if (blank_mode == XA_NEXT)
av[ac++] = "--next";
@@ -2218,6 +2368,15 @@ main (int argc, char **argv)
{
logfile = argv[++i];
if (!logfile) goto HELP;
+ if (! verbose_p) /* might already be -vv */
+ verbose_p = cmdline_verbose_p = cmdline_verbose_val = True;
+ }
+ else if (!strcmp (argv[i], "-ver") ||
+ !strcmp (argv[i], "-vers") ||
+ !strcmp (argv[i], "-version"))
+ {
+ fprintf (stderr, "%s\n", screensaver_id+4);
+ exit (1);
}
else if (!strcmp (argv[i], "-d") ||
!strcmp (argv[i], "-dpy") ||
@@ -2244,6 +2403,7 @@ main (int argc, char **argv)
"\t\t--verbose\n"
"\t\t--no-splash\n"
"\t\t--log logfile\n"
+ "\t\t--version\n"
"\n"
"\tRun 'xscreensaver-settings' to configure.\n"
"\n");