Skip to content

veilDocsStart

Usage

Starting the daemon, setting wallpapers, the control commands, the systemd user service and scripts.

Copied from README.md in veil’s repositoryView on GitHub (opens in a new tab)

Start the daemon#

bash
veil daemon                                           # every monitor: its wallpaper from the configuration file, else the saved one
veil daemon --input ~/Videos/waves.mp4                # this wallpaper on every monitor
veil daemon --output DP-3 --output HDMI-A-1           # only these monitors
veil daemon --mode fit --bg-color '#202020'           # letterbox on dark grey (quote the #)

Without --input, each monitor shows its wallpaper from the configuration file, else the one it showed before, else a solid --bg-color (black by default) until veil set. Monitor names are the compositor's (veil outputs, hyprctl monitors, swaymsg -t get_outputs). A monitor keeps its wallpaper by name: unplugged and plugged in again, it shows what it showed. A monitor connected for the first time gets the default wallpaper: that of the latest veil set for every monitor, or else --input or the configuration file's [output."*"].

--mode says how a wallpaper is fitted to its monitor; the daemon's --mode and --bg-color are also the defaults of veil set:

ModeResult
fill (default)Covers the whole monitor, keeping the aspect ratio. The overflow is cropped evenly from both sides.
fitThe whole picture is visible, keeping the aspect ratio. Bars of --bg-color fill the rest.
stretchScaled to exactly the monitor's size; the aspect ratio is not kept.
centerNative size (one pixel per device pixel), centred. Cropped if larger than the monitor, surrounded by --bg-color if smaller.
tileNative size, repeated from the top-left corner. Videos fall back to center.

veil daemon --help (or man veil-daemon) lists every option.

Images and videos#

Whether a file is a picture or a video is read from its content, not from its name:

The file isIt is shown as
A picture (PNG, JPEG, WebP, BMP, TIFF and the other formats of the image crate)An image, whatever its extension.
A GIF with one frameAn image.
A GIF with several framesA video (looped). GStreamer needs gst-libav for it.
An animated WebP or PNGAn image: its first frame. For WebP a warning is logged.
A video container (Matroska, WebM, MP4, QuickTime, 3GP, AVI, Ogg, FLV, ASF, MPEG program or transport stream)A video, whatever its extension. Whether it can be played depends on the installed GStreamer plugins.
Something else, with a video extension (mp4, m4v, mov, mkv, webm, avi, ogv, mpg, mpeg, ts, m2ts, mts, wmv, asf, flv, 3gp, vob)Handed to GStreamer, which knows more containers; its answer is the result.
Something else, with an image extension (for example tga, a format without a signature)Handed to the image decoder.
A HEIC/HEIF or JPEG XL pictureRefused: 'photo.heic' is a picture in the HEIF format, which veil cannot decode. (An AVIF picture is refused by the image decoder: The image format Avif is not supported.)
Anything elseRefused: 'notes.txt' is neither an image nor a video that veil recognises, by its content or by its extension.

A path that does not exist, a directory, an empty file and a file that cannot be read are refused with exactly that reason. A video with a single frame is shown as a still picture and not looped.

Change the wallpaper#

bash
veil set --input ~/Videos/waves.mp4                    # every monitor
veil set --input ~/Pictures/photo.jpg --output DP-3    # one monitor; the others keep theirs
veil set --input ~/Pictures/pattern.png --mode tile    # another mode than the daemon's
veil set --input ~/Pictures/preview.png --no-persist   # not saved: a restart shows what was saved before

The daemon loads the file in the background, and veil set returns once the wallpaper is on screen (for a video, its first frame) on every monitor it is for. If the file is missing or cannot be shown, veil set prints why and exits with status 1, and the old wallpaper stays. A relative path is fine: veil set makes it absolute. If several veil set come faster than files load, the latest wins on each monitor; one that is replaced before it was shown says so and exits with status 1. Without --output, the wallpaper also becomes the default for monitors connected later (a video once it has been shown).

Control a running daemon#

bash
veil clear                     # only the background colour (saved like `veil set`)
veil pause                     # pause videos until `veil resume`, whatever is on screen
veil resume --output DP-3
veil toggle                    # prints `paused` or `playing`
veil status                    # what each monitor shows (--json for scripts)
veil outputs                   # the compositor's monitors, and which ones veil draws on
veil quit                      # stop the daemon; returns once it has ended (alias: veil stop)
veil ping                      # prints `pong` if a daemon runs, else exits with status 3

veil status prints a line per monitor:

text
veil 1.0.0-beta.1 (pid 512958), running for 7s; videos pause on: fullscreen
default: ~/Videos/waves.mp4 (fill, #000000)
DP-3: paused (by veil pause) ~/Videos/waves.mp4 (fill, #000000); 1920x1080 at scale 1; video 1280x720, 30 fps, 0 frames shown, 0 dropped
HDMI-A-1: showing ~/Pictures/wallpaper.png (fit, #000000); 2048x1152 at scale 1.25

Every command takes --help, and has a man page (man veil-set, ...). What veil pause holds lasts until veil resume or the end of the daemon; it is not saved.

Automatic pause#

Under Hyprland and sway, veil follows the compositor's IPC and pauses a video nobody can see: it keeps its frame on screen and decodes nothing, and plays on from the same frame when it can be seen again. --pause-on (or pause_on in the configuration file) says when:

--pause-onA video is paused while its monitor shows
neverNothing pauses it.
fullscreen (default)A fullscreen window. A maximised window does not count.
coveredA fullscreen window, or windows that cover the whole monitor (gaps or a bar leave it uncovered). A translucent window counts as covering.

A video is paused once it has been hidden for a second, so switching workspaces back and forth does not stop it every time, and plays again at once. Under other compositors nothing is paused this way.

On any compositor, a video whose frames the compositor has not taken for --release-unseen-after seconds (default 2) is stopped and its pipeline freed: the screen is locked, the monitor is off, or (in sway) a window covers it. It starts again from its beginning when the monitor shows it again. 0 turns this off.

Configuration file#

~/.config/veil/config.toml (or $XDG_CONFIG_HOME/veil/config.toml, or veil daemon --config FILE) holds the daemon's options and the wallpaper of each monitor:

toml
[defaults]
mode = "fill"           # --mode
bg_color = "#000000"    # --bg-color
pause_on = "covered"    # --pause-on

[output."DP-3"]
input = "~/Videos/waves.mp4"
mode = "fit"

[output."*"]            # every monitor without a section of its own
input = "~/Pictures/wallpaper.png"

A flag on the command line wins over the file, and the file over the built-in default. A monitor with a wallpaper in the file shows it at every start; veil set changes it until the daemon stops. The file is read only when the daemon starts. veil config check reads it as the daemon would, checks its wallpapers and prints what the daemon would start with; a wrong key or value is reported with its line and column. Every key is in docs/configuration.md, and contrib/config.example.toml (opens in a new tab) is an annotated file.

Saved wallpapers#

Every veil set and veil clear that is shown is saved, per monitor, in $XDG_STATE_HOME/veil/state.json (~/.local/state/veil/state.json), with its mode and colour. A daemon started without --input shows each monitor's saved wallpaper again, unless the configuration file gives that monitor one; started with --input, it shows that everywhere and saves it. --no-persist shows a wallpaper without saving it. A saved file that has gone missing is logged, and its monitor shows the background colour. The file is shared by all daemons of a user: a daemon you start for a test in a nested compositor saves there too, unless you give it another XDG_STATE_HOME.

Run as a systemd user service#

The unit (contrib/systemd/veil.service (opens in a new tab)) runs veil daemon without options, so it shows the wallpapers of the configuration file and the saved ones. Installed as above (or by the Nix package):

bash
systemctl --user daemon-reload
systemctl --user enable --now veil
  • systemctl --user start veil returns once every monitor shows its wallpaper, and systemctl --user status veil says how start-up went.
  • After a crash (such as the one on monitor changes, known issue 1) it is restarted after 2 seconds, with the wallpapers it had. It is not restarted after a wrong configuration file (fix it, then systemctl --user restart veil), a second daemon, or the end of the session.
  • A watchdog restarts a daemon whose event loop is stuck, and MALLOC_ARENA_MAX=1 keeps memory flat over video switches.
  • The log is in the journal: journalctl --user -u veil.

The unit starts with graphical-session.target and needs WAYLAND_DISPLAY in the user manager's environment (and HYPRLAND_INSTANCE_SIGNATURE or SWAYSOCK for the automatic pause). Hyprland imports these by itself. A session without graphical-session.target starts it from its own configuration instead of enabling it:

bash
# Hyprland (hyprland.conf)
exec-once = systemctl --user start veil
# sway (config)
exec systemctl --user import-environment WAYLAND_DISPLAY SWAYSOCK && systemctl --user start veil

Daemon options go into the configuration file, or into ExecStart with systemctl --user edit veil. Use systemctl --user stop veil rather than veil quit to stop the service.

Shell completions and man pages#

The Nix package installs both. Otherwise the binary prints the completion script of a shell, and writes the man pages:

bash
veil completions bash > ~/.local/share/bash-completion/completions/veil
veil completions zsh > ~/.zfunc/_veil            # with fpath+=(~/.zfunc) before compinit in ~/.zshrc
veil completions fish > ~/.config/fish/completions/veil.fish
veil man-pages ~/.local/share/man/man1           # veil.1, veil-set.1, ...

In nushell: mkdir ($nu.user-autoload-dirs | first); veil completions nushell | save -f ($nu.user-autoload-dirs | first | path join veil.nu). Create the directories first if they do not exist. Monitor names are not completed.

Scripts and status bars#

The veil commands talk to the daemon through a Unix socket, $XDG_RUNTIME_DIR/veil-$WAYLAND_DISPLAY.sock, that other programs can use too: one JSON request and one JSON response per connection, only for processes of your own user. veil status --json and veil outputs --json print the same answers. Fields, commands and error codes are only ever added, never renamed or removed, and the exit statuses of veil are stable (for example 3 when no daemon runs). The reference of the protocol, the JSON fields and the exit statuses is docs/ipc.md; the exit statuses are also in man veil.