An OLED-friendly Xorg screensaver and PAM screen locker for MidnightBSD, written in Rust.
It is a drop-in replacement for the xscreensaver daemon and the xscreensaver-command
control client.
Status: v0.5, Xorg only. There is no Wayland support.
- Accepts xscreensaver's daemon flags (
-display,-dpy,-verbose,-vvv,-no-splash,-log, ...). - Speaks the xscreensaver control protocol: the real
xscreensaver-commandworks against it, andnightsaver-commandalso works against a real xscreensaver daemon. - Reads the relevant subset of
~/.xscreensaver, plus its own TOML file. - Four built-in software-rendered patterns (no external hack programs).
- Every frame goes through an OLED guard: brightness cap, slow pixel shift, periodic black rest.
- Optional screen lock with PAM password unlock, using the existing
xscreensaverPAM service so no setuid code is needed in nightsaver itself. - DPMS management, multi-monitor coverage, config reload on file change or
SIGHUP.
Not in scope: running xscreensaver hack binaries, the settings GUI, systemd/D-Bus integration.
Requires the system toolchain from mports (/usr/local/bin/cargo, rustc 1.96 or newer; the crate uses
edition 2024) and the X libraries (libxcb, libxkbcommon) under /usr/local.
make # cargo build --release
make test # cargo test
build.rs adds $LOCALBASE/lib (default /usr/local) to the linker search path, so no RUSTFLAGS
are needed. Override with make LOCALBASE=/other/prefix. Plain cargo build --release works too.
make install # binaries, man pages, sample config, desktop files
make install-pam # PAM policy only (install already does this)
make install-compat-links # optional, xscreensaver -> nightsaver symlinks
PREFIX (default /usr/local), DESTDIR, MANDIR and CARGO can be overridden. Both BSD make and GNU
make are supported. make uninstall removes what was installed (compat links only if they are symlinks).
Installed files:
Path (under $PREFIX) |
Contents |
|---|---|
bin/nightsaver, bin/nightsaver-command |
daemon and client |
libexec/nightsaver-auth |
PAM helper run per unlock attempt |
man/man1, man/man5 |
nightsaver(1), nightsaver-command(1), nightsaver.toml(5) |
etc/nightsaver.toml.sample |
annotated sample configuration |
share/applications/nightsaver*.desktop |
"Blank" and "Lock" launchers |
etc/xdg/autostart/nightsaver.desktop |
starts nightsaver --no-splash at login |
etc/pam.d/nightsaver |
PAM policy, never overwritten |
- Stop the running daemon:
xscreensaver-command -exit - Remove it from your session startup (autostart entry,
.xinitrc, desktop settings). - Start nightsaver:
nightsaver --no-splash &(or log out and in to use the autostart entry). - Your
~/.xscreensavertimeouts, lock settings, DPMS settings and mode are picked up automatically (see below for which keys). Hack names inprograms:that are not nightsaver patterns are skipped.
nightsaver will not start while another saver owns the display, and nightsaver-command is a
compatible client, so scripts that call xscreensaver-command -lock can keep working either via the
real client (which talks to nightsaver) or via the compat links:
make install-compat-links
This creates $PREFIX/bin/xscreensaver and $PREFIX/bin/xscreensaver-command as symlinks to the
nightsaver binaries. It refuses to replace a real file, which is what the xscreensaver package installs;
it prints how to proceed (stop the daemon, then remove the package, e.g. mport delete xscreensaver,
and run it again). Removing the package also removes /usr/local/etc/pam.d/xscreensaver, so
nightsaver falls back to its own policy installed by make install. The setuid
unix-selfauth-helper comes from a separate package and stays installed.
Create ~/.config/nightsaver/config.toml (or pass --config file). Every key is optional.
timeout = "0:05:00" # idle time before blanking
lock = true # require a password
lock_timeout = "0:00:30" # lock this long after blanking
mode = "random" # one | random | blank | off
patterns = ["clock", "starfield", "matrix", "midnight", "cat", "juggler"]
[dpms]
enabled = true
quick_off = true # power the monitor off as soon as it blanks
[oled]
max_brightness = 0.35Precedence: command line > TOML file > ~/.xscreensaver > built-in defaults.
See etc/nightsaver.toml.sample for every key and nightsaver.toml(5) for details. The files are
re-read within about five seconds of a change, or immediately on kill -HUP.
~/.xscreensaver keys that are honoured: timeout, cycle, lock, lockTimeout, passwdTimeout,
dpmsEnabled, dpmsStandby, dpmsSuspend, dpmsOff, dpmsQuickOff (and dpmsQuickoffEnabled),
mode, selected, programs (hack names matched against the patterns below), verbose, splash.
fade/unfade are parsed and ignored; everything else is ignored.
nightsaver [-display host:display.screen] [-verbose] [-quiet] [-no-splash] [-log file]
[-debug] [-sync] [--lock | --no-lock] [--config file] [--pattern name]
Also: -dpy, -disp, -v/-vv/-vvv/-vvvv, -nosplash, -splash (ignored), -version,
-help, and --demo-blank (debug: black windows for 3 seconds, then exit). One or two dashes are accepted.
nightsaver-command -activate | -deactivate | -cycle | -next | -prev | -select N
| -lock | -suspend | -exit | -restart | -time | -watch | -version
Same meaning as xscreensaver-command. Exit status 0 on success, 255 if the daemon refused or is not
reachable, 1 for usage errors. -demo is parsed but answered with "not supported".
| Name | Description |
|---|---|
blank |
Pure black. |
clock |
A time display that drifts around the screen. |
starfield |
Stars flying outward (alias stars). |
matrix |
Falling character rain (aliases glmatrix, xmatrix). |
midnight |
The MidnightBSD logo as a drifting outline with twinkling fill, and the boot loader's ASCII brand (aliases midnightbsd, logo). |
cat |
An ASCII-art cat that blinks, swishes its tail, naps with floating z's and watches the odd mouse go by (aliases asciicat, kitty). |
juggler |
An ASCII-art stick figure juggling a three-ball cascade on smooth arcs (alias juggle). |
-select N and -next/-prev index the configured patterns list. The default list is
clock, starfield, matrix, midnight, cat, juggler; blank is available by name or via mode = "blank".
max_brightness(default 0.35) caps luma on every frame of every pattern; blank is true black.- Pixel shift: the whole frame is moved by a slow pseudo-random walk every
shift_interval(default 60 s), at mostshift_max_px(default 16) pixels from the origin. - Rest interlude: every
rest_interval(10 min) the screen goes fully black forrest_duration(20 s). - Lit-pixel budget: patterns are given a target of at most
lit_budget(5 %) lit pixels as a hint. - Nothing static stays on screen: the clock drifts and the unlock dialog hides itself.
- For the strongest protection use
mode = "blank"with[dpms] enabled = true, quick_off = true.
Locking is off by default. Enable it with lock = true, --lock, or at any time with
nightsaver-command -lock (which works even when lock is false).
- Authentication uses OpenPAM. The default PAM service is
xscreensaver; its policy (/usr/local/etc/pam.d/xscreensaver) runs the setuid helperunix-selfauth-helperthroughpam_exec, so nightsaver verifies your own password without any privilege and is not setuid. - PAM runs in a separate short-lived process,
libexec/nightsaver-auth(not setuid either), started by the daemon for each attempt with the password passed over a pipe. A PAM module that crashes or hangs takes only the helper down: the daemon kills it after 25 s and counts anything but a clean exit 0 as a denial, so the screen stays locked. The daemon looks for the helper next to its own binary first (development builds), then in$PREFIX/libexecas compiled in bymake. make install(ormake install-pam) installsetc/pam.d/nightsaver(a copy of the same policy) only if the file does not exist. It is used automatically when thexscreensaverpolicy is missing (for example aftermport delete xscreensaver), or explicitly withpam_service = "nightsaver".- Wrong passwords show an error for 2 s and add a delay of 1, 2, 4, 8, then 10 s per further failure.
Escape or
passwd_timeout(30 s) closes the dialog. - While locked,
-exit,-restart,SIGTERMandSIGINTare refused (logged). The same applies while a lock is pending: if another program holds the keyboard or mouse grab when a lock is due, the screen stays blanked with a visible "NOT LOCKED" notice until the grab succeeds, and-deactivatedoes not cancel it. Any PAM error counts as a failed unlock. The dialog is never shown without a successful keyboard and pointer grab. Password buffers are zeroed and never logged. - If the user name cannot be looked up, the screen still locks and every attempt is denied (fail closed).
- Configuration files (
~/.xscreensaver, the TOML file) are ignored with an error if they are not regular files owned by you or root, or if they are writable by group or others, since they choose the PAM service.pam_servicemay not contain/. A log file is opened without following symlinks and must be owned by you and not writable by others. - Events another client sends with
XSendEventare ignored except for the control protocol, and RANDR monitors that lie inside another or whose total area exceeds twice the root are collapsed, so a hostile client cannot hide the dialog or force huge allocations.
Security caveats shared with xscreensaver: an X11 locker cannot stop the X server being killed
(for example Ctrl-Alt-Backspace where enabled), switching to a text VT with a logged-in session
(Ctrl-Alt-Fn), or other X clients that already hold input grabs. Disable those if they are in your
threat model. See the SECURITY section of nightsaver(1).
cargo test # unit tests: parsers, patterns, OLED guard, protocol, lock logic
cargo clippy --all-targets -- -D warnings
tests/xvfb_smoke.sh # integration test on a private Xvfb (:99), never touches :0
tests/xvfb_smoke.sh needs Xvfb, xrandr, xprop, xdotool and the real xscreensaver-command, and uses the
debug binaries plus the grab-holding helper (cargo build --all-targets first). Do not run the daemon on your live display for testing without
stopping the real saver first.
Verified on a live MidnightBSD 4.2 Xorg session: blanking, wake on activity, and locking with a
real password accepted and a wrong one rejected through the nightsaver PAM policy.
Not yet tested: DPMS on real hardware (neither Xvfb nor the test machine's Xorg has the DPMS
extension), multi-monitor hotplug, and the MIT-SHM path (rendering uses plain PutImage).
BSD 2-Clause. See LICENSE.