From 53dd0ceecaefbfcf76bcaf210afa3f4234a52229 Mon Sep 17 00:00:00 2001 From: theta Date: Sat, 29 Aug 2026 19:16:31 +0200 Subject: [PATCH] =?UTF-8?q?Add=20theta/wl-svg-bg=20=E2=80=94=20live=20CSS-?= =?UTF-8?q?animated=20SVG=20wallpaper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A [[service]] plugin that yields Noctalia's Background surface on the outputs it owns and drives wl-svg-bg, an external WebKitGTK layer-shell renderer that plays an SVG's CSS animation live instead of rasterizing one frozen frame. Falls back to a transient systemd unit where no wl-svg-bg.service is declared, so it works without the renderer's home-manager module. Writes one file, ~/.local/state/wl-svg-bg/env; makes no network calls. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TnBu6LjZ6EUJ5dgdL6XBUD --- wl-svg-bg/README.md | 84 +++++++++++++ wl-svg-bg/examples/aurora.svg | 57 +++++++++ wl-svg-bg/plugin.toml | 28 +++++ wl-svg-bg/service.luau | 210 +++++++++++++++++++++++++++++++++ wl-svg-bg/thumbnail.webp | Bin 0 -> 9802 bytes wl-svg-bg/translations/en.json | 8 ++ 6 files changed, 387 insertions(+) create mode 100644 wl-svg-bg/README.md create mode 100644 wl-svg-bg/examples/aurora.svg create mode 100644 wl-svg-bg/plugin.toml create mode 100644 wl-svg-bg/service.luau create mode 100644 wl-svg-bg/thumbnail.webp create mode 100644 wl-svg-bg/translations/en.json diff --git a/wl-svg-bg/README.md b/wl-svg-bg/README.md new file mode 100644 index 00000000..d8c2c250 --- /dev/null +++ b/wl-svg-bg/README.md @@ -0,0 +1,84 @@ +# Animated SVG Wallpaper + +Renders a CSS-animated SVG as your wallpaper, **with the animation actually +running**. Ordinary wallpaper tools rasterize an SVG to a single frozen frame; +this one hands the file to a WebKit view on a `wlr-layer-shell` background +surface, so `@keyframes` play live behind your desktop. + +While it is active the plugin asks noctalia to release its own Background +surface on the affected outputs, so the two never draw over each other. + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `theta/wl-svg-bg` | +| Entries | Service: `takeover` | + +## Requirements + +- **`wl-svg-bg`** on `PATH` — the renderer itself, a separate program that this + plugin only drives. It is a single Python script from + [M4jor-Tom/wl-svg-bg.py](https://github.com/M4jor-Tom/wl-svg-bg.py) and needs + PyGObject with the GTK 4, WebKitGTK 6.0 and gtk4-layer-shell typelibs. On + Arch that is `python-gobject gtk4 webkitgtk-6.0 gtk4-layer-shell`; a Nix flake + and a home-manager module are in the repo. +- **`systemd`** — supplies `systemctl` and `systemd-run`, which start and stop + the renderer's user unit. +- A compositor with **`wlr-layer-shell`**: niri, sway, Hyprland, labwc, and the + rest of the wlroots family. This does not work on GNOME or KDE, which do not + implement that protocol. + +## Usage + +1. Install `wl-svg-bg` and enable this plugin. +2. Open the plugin's settings and set **Animated SVG** to a CSS-animated `.svg`. + One ships with the plugin at `examples/aurora.svg` if you do not have one — + animated SVGs are rare, so start there. +3. The wallpaper starts immediately. noctalia stops drawing its own on every + connected output, and monitors plugged in later are picked up automatically. + +**To turn it off, clear the Animated SVG field.** That is the off-switch: it +stops the renderer and gives the outputs back to noctalia. Disabling the plugin +also stops the renderer, but the outputs stay released until noctalia restarts, +because the host discards side effects issued during teardown — so clearing the +field is the clean path. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `svg` | `file` | *(empty)* | Path to the CSS-animated SVG to render. Empty means the plugin claims nothing and starts nothing, which is also how you switch it off. | + +## Notes + +**Cost.** This runs a WebKit view per monitor, continuously, for as long as the +wallpaper is up. That is more expensive than a static wallpaper and it does not +pause when the screen is locked, occluded, or on battery — the compositor gives +a background surface no visibility signal to react to. Budget for it on a +laptop, and prefer SVGs that animate slowly and over large soft shapes: +`examples/aurora.svg` is built that way on purpose. A file full of fast +per-element transforms will cost far more. + +**If the renderer dies**, its unit is restarted automatically +(`Restart=on-failure`). While it is down the outputs stay released, so the +screen shows the bare compositor background rather than your old wallpaper. If +it cannot start at all, the plugin hands every output back to noctalia and +notifies you, so a broken setup fails to your normal wallpaper instead of to +black. + +**Processes spawned.** `systemctl --user start|restart|stop wl-svg-bg.service`, +and — only when no `wl-svg-bg.service` has been declared, which is every install +that does not use the project's home-manager module — `systemd-run --user` to +create a transient unit of the same name. The plugin never writes a unit file +and never runs `daemon-reload`. + +**Files written.** One: `~/.local/state/wl-svg-bg/env`, holding +`WL_SVG_BG_SVG=`. The renderer reads the SVG path from +there rather than from a command line, so paths containing spaces survive. The +plugin compares this file's contents before writing, and only restarts the +renderer when the path actually changed — which is what keeps a plain +`systemctl --user restart noctalia` from reloading your wallpaper. + +**Network.** None. Nothing is fetched, and the rendered SVG is loaded from a +local `file://` URL. diff --git a/wl-svg-bg/examples/aurora.svg b/wl-svg-bg/examples/aurora.svg new file mode 100644 index 00000000..1a578390 --- /dev/null +++ b/wl-svg-bg/examples/aurora.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wl-svg-bg/plugin.toml b/wl-svg-bg/plugin.toml new file mode 100644 index 00000000..d8c1056e --- /dev/null +++ b/wl-svg-bg/plugin.toml @@ -0,0 +1,28 @@ +id = "theta/wl-svg-bg" +name = "Animated SVG Wallpaper" +version = "0.1.0" +author = "theta" +license = "MIT" +# Catalog descriptions are capped at 120 characters (validate-plugins.py). +description = "Renders a CSS-animated SVG as the wallpaper with its animation running, via WebKit on a layer-shell surface." +# Exactly 17, not a floor picked for breadth: below it `onEnable` and the +# Disable/Uninstall exit reasons never fire (plugin_manager.cpp:593,609), which +# silently breaks the off-switch and the teardown path. +plugin_api = 17 +icon = "image" +tags = ["wallpaper", "animation", "service"] +# Everything this plugin spawns. `wl-svg-bg` is the renderer itself; systemd +# supplies both systemctl and systemd-run, which manage its unit. +dependencies = ["wl-svg-bg", "systemd"] + +[[service]] +id = "takeover" +entry = "service.luau" + +[[setting]] +key = "svg" +type = "file" +label_key = "settings.svg.label" +description_key = "settings.svg.description" +extensions = ["svg"] +default = "" diff --git a/wl-svg-bg/service.luau b/wl-svg-bg/service.luau new file mode 100644 index 00000000..8e11e15a --- /dev/null +++ b/wl-svg-bg/service.luau @@ -0,0 +1,210 @@ +-- Coordinates wl-svg-bg, an external WebKitGTK layer-shell renderer, with +-- noctalia: yields noctalia's Background surface on the outputs the renderer +-- owns, and drives the renderer's systemd user unit. +-- +-- The renderer cannot live in here: it needs WebKit to run the SVG's CSS +-- animation live, which a sandboxed Luau plugin cannot host. + +local BIN = "wl-svg-bg" +local UNIT = BIN .. ".service" +local ENV_DIR = "~/.local/state/wl-svg-bg" +local ENV_FILE = ENV_DIR .. "/env" +local TITLE = "Animated SVG wallpaper" + +-- Connectors we have told noctalia to yield, so onOutputsChanged only claims +-- what is new. noctalia's own managed set is runtime-only and clears on restart, +-- which is why the script body re-claims rather than relying on onEnable. +local owned = {} + +local function claimOutputs() + for _, output in ipairs(noctalia.outputs()) do + if not owned[output.name] then + noctalia.setWallpaperEnabled(output.name, false) + owned[output.name] = true + end + end +end + +local function releaseOutputs() + for name in pairs(owned) do + noctalia.setWallpaperEnabled(name, true) + end + owned = {} +end + +-- Start the renderer without a declared unit, which is every install that is +-- not home-manager's: nothing else on Arch or Fedora writes wl-svg-bg.service, +-- and without this branch the plugin can only ever fail there. +-- +-- A transient unit rather than a written one: `systemd-run` spawns and forgets, +-- where generating ~/.config/systemd/user/ + daemon-reload would have the plugin +-- installing units behind the user's back. --unit takes UNIT itself, so the +-- transient unit and the declarative one cannot drift apart and `stop` reaches +-- either; --collect frees the name again once the renderer exits, so the next +-- start can reclaim it. +-- +-- The SVG path travels in EnvironmentFile, never on the command line, so a +-- wallpaper under "~/Pictures/My Wallpapers/" survives whatever splitting the +-- host applies to the command string. wallpaper.py reads WL_SVG_BG_SVG when +-- --svg is absent, which is what makes that possible. +local function runTransient(cause) + if not noctalia.commandExists(BIN) then + noctalia.notifyError(TITLE, BIN .. " is not installed, or not on PATH. " + .. "Install it, then pick the SVG again.") + releaseOutputs() + return + end + -- Stop first: a transient unit cannot be restarted in place, and the name + -- has to be free before systemd-run can claim it. Its result is deliberately + -- ignored — "not running" is the expected case here, not an error. + noctalia.runAsync("systemctl --user stop " .. UNIT, function() + -- Restart=on-failure mirrors the declarative unit. Without it a renderer + -- crash is terminal, and since the outputs are already yielded that + -- means a black screen until the user touches the settings again. + noctalia.runAsync("systemd-run --user --collect --unit=" .. UNIT + .. " -p Restart=on-failure" + .. " -p EnvironmentFile=" .. noctalia.expandPath(ENV_FILE) + .. " " .. BIN, function(result) + if result.exitCode ~= 0 then + -- Report the declarative unit's failure too. This path is also + -- taken when a unit exists but is genuinely broken, and naming + -- only the fallback's error would bury the real cause. + noctalia.notifyError(TITLE, "Could not start the renderer (" + .. tostring(result.exitCode) .. "): " .. tostring(result.stderr) + .. " — the declared unit first failed with: " .. cause) + releaseOutputs() + end + end) + end) +end + +-- `detached` runs without a result callback, for use during teardown: onExit +-- side effects are discarded, but runAsync spawns immediately. +-- +-- The outputs are claimed before the unit is asked to run, so a start that +-- fails outright — a deleted SVG, or no renderer installed at all — would +-- otherwise leave noctalia's Background surface torn down with nothing drawing +-- in its place: bare compositor, forever. Failing to a static wallpaper beats +-- failing to black, so every path that cannot start hands the outputs back. +-- A failed "stop" is not a reason to re-claim. + +local function systemctl(verb, detached) + local command = "systemctl --user " .. verb .. " " .. UNIT + if detached then + noctalia.runAsync(command) + return + end + if not noctalia.commandExists("systemctl") then + noctalia.notifyError(TITLE, "systemctl not found; cannot manage " .. UNIT) + if verb ~= "stop" then + releaseOutputs() + end + return + end + noctalia.runAsync(command, function(result) + if result.exitCode == 0 then + return + end + -- A failed start is not yet a failure: on any non-Nix install it just + -- means no unit was ever declared. Try a transient one before handing + -- the outputs back, and stay quiet until that fails too — notifying here + -- would fire on every healthy Arch session. + if verb ~= "stop" then + runTransient(tostring(result.exitCode) .. " " .. tostring(result.stderr)) + return + end + noctalia.notifyError(TITLE, "systemctl " .. verb .. " failed (" + .. tostring(result.exitCode) .. "): " .. tostring(result.stderr)) + end) +end + +-- Publish the chosen SVG where the unit's EnvironmentFile picks it up, and +-- report whether that changed anything. +-- +-- The comparison is what makes a declarative SVG change take effect without a +-- relogin. EnvironmentFile always beats Environment, so once this file exists +-- the unit's declarative `Environment=` is shadowed for good; a rebuild that +-- changes the SVG restarts noctalia and the renderer in unspecified order, and +-- if the renderer goes first it re-reads the *old* file. Only a restart fixes +-- that, and only a restart when the content actually differs keeps a plain +-- noctalia restart from reloading the wallpaper — the property systemd was +-- chosen for over runStream. +-- +-- A missing or unreadable file is not a differing file: on first boot nothing is +-- running yet, and `start` is what is wanted. +local function writeEnv(svg) + local body = "WL_SVG_BG_SVG=" .. svg .. "\n" + local previous = noctalia.readFile(ENV_FILE) + local changed = type(previous) == "string" and previous ~= "" and previous ~= body + + noctalia.mkdirAll(ENV_DIR) + local ok, err = noctalia.writeFile(ENV_FILE, body) + if not ok then + noctalia.notifyError(TITLE, "Cannot write " .. ENV_FILE .. ": " .. tostring(err)) + return false + end + return true, changed +end + +-- Clearing the SVG is the supported off-switch: it releases the outputs and +-- stops the renderer. +local function activate() + local svg = noctalia.getConfig("svg") + if svg == nil or svg == "" then + if next(owned) ~= nil then + releaseOutputs() + systemctl("stop") + else + -- Naming the bundled file matters: almost nobody owns an animated + -- SVG, so "choose one" alone sends a first-time user to an empty + -- file dialog and an uninstall. + noctalia.notifyError(TITLE, "No SVG selected. Choose one in the plugin " + .. "settings — this plugin ships an example, see its README.") + end + return + end + local ok, changed = writeEnv(svg) + if not ok then + return + end + claimOutputs() + systemctl(changed and "restart" or "start") +end + +activate() + +function onEnable() + activate() +end + +function onOutputsChanged() + -- Keyed on the SVG, not on `owned`. An empty `owned` is also what a load with + -- every output asleep looks like — lid shut on an external-only setup, or + -- noctalia restarting mid-reconfiguration — and gating on it there would + -- disarm this hook for the session: the renderer self-heals via its own + -- hotplug resync while noctalia never yields, which is the two-surface + -- overlap this plugin exists to remove. + local svg = noctalia.getConfig("svg") + if svg ~= nil and svg ~= "" then + claimOutputs() + end +end + +function onConfigChanged() + activate() +end + +function onExit(_, reason) + -- "reload" is a script edit and "shutdown" is the session ending; the + -- renderer should survive both. + -- + -- Deliberately no releaseOutputs() here. setWallpaperEnabled queues a side + -- effect, and the host's Stop path discards onExit's side effects entirely + -- (script_runtime.cpp:527-537), so it would be dead code. Disabling the + -- plugin therefore leaves the outputs yielded until noctalia restarts, which + -- clears the runtime-only managed set. Clearing the SVG setting is the clean + -- off-switch. + if reason == "disable" or reason == "uninstall" then + systemctl("stop", true) + end +end diff --git a/wl-svg-bg/thumbnail.webp b/wl-svg-bg/thumbnail.webp new file mode 100644 index 0000000000000000000000000000000000000000..169e5de44e4dd36cee3f4a9654fd6d0415f7d8c5 GIT binary patch literal 9802 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 literal 0 HcmV?d00001 diff --git a/wl-svg-bg/translations/en.json b/wl-svg-bg/translations/en.json new file mode 100644 index 00000000..27a50b76 --- /dev/null +++ b/wl-svg-bg/translations/en.json @@ -0,0 +1,8 @@ +{ + "settings": { + "svg": { + "label": "Animated SVG", + "description": "The CSS-animated SVG to render as the wallpaper. An example ships with this plugin at examples/aurora.svg. Clearing this field is the off-switch: it stops the renderer and hands the wallpaper back to noctalia." + } + } +}