update
This commit is contained in:
@@ -1,6 +1,534 @@
|
||||
# LiveWall
|
||||
# LiveWall 1.8.4 — animated GIF, video & YouTube desktop wallpapers for Windows 11
|
||||
|
||||
A complete, self-contained live-wallpaper app written in C# / WPF (.NET 8). It plays videos,
|
||||
animated GIFs, still images, YouTube links and any web page behind your desktop icons —
|
||||
animated GIFs, still images, **YouTube links** and **any web page** *behind your desktop icons* —
|
||||
exactly where a wallpaper belongs — with a comprehensive settings window where every behaviour can
|
||||
be configured.
|
||||
be configured.
|
||||
|
||||
No store, no subscription, no account.
|
||||
|
||||
---
|
||||
|
||||
## 1. What you got
|
||||
|
||||
| Folder | Contents |
|
||||
|---|---|
|
||||
| **`LiveWall-Setup-1.8.4.exe`** | **Installer** — dark, app-branded setup; per-user install, upgrade detection, Start-menu shortcut, uninstaller, .NET runtime check. |
|
||||
| `LiveWall-Setup-1.8.0.exe`, `LiveWall-Setup-1.7.0.exe`, `LiveWall-Setup-1.6.3.exe` | Previous releases, kept so the update flow can be tried out. |
|
||||
| **`installer\`** | The NSIS script + build script that produce the installer. |
|
||||
| **`prebuilt\`** | Ready-to-run build (`LiveWall.exe` + DLLs, ~1.6 MB). Needs the **.NET 8 Desktop Runtime**. |
|
||||
| **`LiveWall\`** | Full source code (C# + XAML). |
|
||||
| **`LiveWall.Sdk\`** | The plugin contract assembly (`LiveWall.Sdk.dll`) — what plugins are written against. |
|
||||
| **`samples\ClockOverlayPlugin\`** | Complete example plugin that uses every extension point. |
|
||||
| **`docs\plugins\`** | Developer guide for plugins (also shipped next to the app, and readable in Markdown). |
|
||||
| **`build.ps1`** | One-command build / run / publish / self-contained packaging. |
|
||||
| **`README.md`** | This document. |
|
||||
|
||||
## 2. Install it
|
||||
|
||||
### The easy way — the installer
|
||||
|
||||
Run **`LiveWall-Setup-1.8.4.exe`**. It is a **per-user** install, so Windows shows no UAC prompt and
|
||||
nothing is written outside your own profile:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Program files | `%LOCALAPPDATA%\Programs\LiveWall` |
|
||||
| Start-menu shortcut | *LiveWall* |
|
||||
| Optional | desktop shortcut, start at sign-in |
|
||||
| Uninstall | Settings ▸ Apps ▸ Installed apps ▸ *LiveWall* (or `Uninstall.exe` in the folder) |
|
||||
| Your settings | `%APPDATA%\LiveWall` — **kept** by installer upgrades and by the uninstaller (it asks first) |
|
||||
|
||||
The window matches the app: same dark palette and accent colour, the app mark, one page for the
|
||||
options and one for the result — there is no white wizard chrome and no stock "Nullsoft" text. Every
|
||||
control is drawn with `nsDialogs` (`installer/LiveWall.nsi`), positioned as a percentage of the window
|
||||
so the layout holds at any DPI, and the stock Next/Back/Cancel row is replaced by flat buttons.
|
||||
|
||||
The installer checks for the **.NET Desktop Runtime 8 (or newer)** and offers the Microsoft download
|
||||
page if it can't find one; it also closes a running LiveWall before replacing files.
|
||||
|
||||
Pictures of the four screens: [setup](docs/img/installer-setup.png),
|
||||
[update](docs/img/installer-update.png), [finished](docs/img/installer-done.png),
|
||||
[uninstall](docs/img/installer-uninstall.png).
|
||||
|
||||
### Updating an existing installation
|
||||
|
||||
The installer records `InstallDir` and `Version` under `HKCU\Software\LiveWall`, and compares the
|
||||
installed version with its own before it touches anything:
|
||||
|
||||
| What it finds | What happens |
|
||||
|---|---|
|
||||
| Nothing installed | Normal fresh install, you pick the folder. |
|
||||
| **Older** version | An **Update** page appears: *"LiveWall 1.6.3 is already installed in … This will update it to 1.8.4."* It installs into the **same folder** (the folder page is skipped), replaces the program files and keeps `%APPDATA%\LiveWall`. A checkbox offers to wipe the settings and start from scratch. |
|
||||
| **Same** version | Asks *"LiveWall 1.7.0 is already installed. Reinstall it?"* before doing anything. |
|
||||
| **Newer** version | Warns that it would be a downgrade and needs an explicit Yes. |
|
||||
|
||||
Version comparison is numeric on `major.minor.patch`, so `1.6.10` sorts above `1.6.9` and `10.0.0`
|
||||
above `9.9.9`. Installs made before this logic existed (no `Version` value) are treated as older and
|
||||
upgraded in place. `/S` silent installs skip the prompts and always update in place.
|
||||
|
||||
### Antivirus and SmartScreen warnings
|
||||
|
||||
The exe is **not code-signed**, so:
|
||||
|
||||
* SmartScreen shows *"Windows protected your PC"* on the first run — **More info ▸ Run anyway**.
|
||||
Signing needs a paid certificate.
|
||||
* On VirusTotal, `LiveWall-Setup-1.8.2.exe` was flagged by **3 of 71 engines** (SecureAge,
|
||||
Trapmine's ML score, VBA32's "TScope.Malware-Cryptor.SB"), and clean on the other 68 — including
|
||||
Microsoft Defender, Kaspersky, BitDefender, ESET, Malwarebytes, Sophos and Symantec. Those three are
|
||||
reputation/heuristic verdicts on an unsigned installer with zero prevalence, not detections of
|
||||
behaviour. Vendor false-positive reports are filed; see **[docs/antivirus.md](docs/antivirus.md)**
|
||||
for the full analysis, the report template and the per-release checklist.
|
||||
|
||||
Verify what you downloaded — the published hash for this build is:
|
||||
|
||||
```
|
||||
d2de043f02179034c011faae92853178c0d72277265ed39227b432cbf96906e4 LiveWall-Setup-1.8.4.exe (724 KB)
|
||||
5c2d046abb9d491913cd4393bfc4664b120a5c4804b83d6895b0413755ad5c87 LiveWall-Setup-1.8.4-zlib.exe (992 KB)
|
||||
3f704c9355ad8348c85151619c784f0f23a6596c5b837a8950e66eb035cfb1a6 LiveWall-1.8.4-portable.zip (875 KB)
|
||||
```
|
||||
|
||||
If your engine still objects, two alternatives ship from the same source and have no NSIS
|
||||
self-extracting stub at all:
|
||||
|
||||
| Download | What it is |
|
||||
|---|---|
|
||||
| `LiveWall-Setup-1.8.4-zlib.exe` | Same installer, non-solid zlib compression (the solid archive is what the heuristic engines score) |
|
||||
| `LiveWall-1.8.4-portable.zip` (`.\build.ps1 -Task portable`) | Plain zip — unpack anywhere, run `LiveWall.exe`. Nothing to unpack, nothing for an engine to score |
|
||||
|
||||
### Rebuilding the installer
|
||||
|
||||
```bash
|
||||
sudo apt-get install nsis # or the Windows NSIS package
|
||||
./installer/build-installer.sh # reads the version from LiveWall.csproj
|
||||
|
||||
# alternate container for antivirus triage (see docs/antivirus.md)
|
||||
COMPRESS=zlib OUT_SUFFIX=-zlib ./installer/build-installer.sh
|
||||
```
|
||||
|
||||
On Windows, sign the result (publisher instead of "Unknown publisher", and what every antivirus
|
||||
vendor asks for when you report a false positive):
|
||||
|
||||
```powershell
|
||||
.\installer\sign.ps1 -Thumbprint <sha1-of-your-code-signing-cert>
|
||||
```
|
||||
|
||||
It packs everything in `prebuilt\` (a full publish, including `runtimes/`), so publish first:
|
||||
|
||||
```powershell
|
||||
.\build.ps1 -Task publish
|
||||
```
|
||||
|
||||
### Option A — use the prebuilt binary without installing (fastest)
|
||||
|
||||
1. Install the **.NET 8 Desktop Runtime (x64)** if you don't have it:
|
||||
<https://dotnet.microsoft.com/download/dotnet/8.0/runtime>
|
||||
*(Windows 11 usually has it already if you use any modern .NET app.)*
|
||||
2. Copy the whole `prebuilt\` folder somewhere permanent, e.g. `C:\Program Files\LiveWall\`.
|
||||
3. Run **`LiveWall.exe`**.
|
||||
4. Pick a video / GIF / image (or paste a YouTube link) → the wallpaper starts immediately.
|
||||
|
||||
### Option B — build from source
|
||||
|
||||
```powershell
|
||||
# from the folder that contains build.ps1
|
||||
.\build.ps1 # framework-dependent build -> .\prebuilt
|
||||
.\build.ps1 -Task run # build and launch
|
||||
```
|
||||
|
||||
Or plainly:
|
||||
|
||||
```powershell
|
||||
dotnet publish LiveWall\LiveWall.csproj -c Release -r win-x64 --self-contained false -o prebuilt
|
||||
```
|
||||
|
||||
Requires the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0).
|
||||
Want a build that runs on a PC *without* .NET installed? `.\build.ps1 -Task selfcontained`
|
||||
(produces a ~180 MB folder — WPF cannot be trimmed).
|
||||
|
||||
### First run
|
||||
|
||||
LiveWall creates `%APPDATA%\LiveWall\` containing:
|
||||
|
||||
* `settings.json` — every setting (human readable)
|
||||
* `logs\livewall-YYYYMMDD.log` — diagnostics
|
||||
* `web\player.html` — the local YouTube player host page
|
||||
* `WebView2\` — Edge WebView2 profile for web wallpapers
|
||||
|
||||
It also adds a tray icon. Closing the settings window keeps the wallpaper running (configurable).
|
||||
|
||||
---
|
||||
|
||||
## 3. Feature tour
|
||||
|
||||
### Wallpaper sources (`Wallpaper` tab)
|
||||
* **Video files** — mp4, m4v, mov, wmv, avi, mkv, webm, mpg, ts (played through WPF/Media Foundation, H.264 MP4 is the most reliable).
|
||||
* **Animated GIF** — frames are composited by hand (per-frame rects, disposal methods 0–3,
|
||||
transparency), so even huge multi-hundred-frame GIFs animate correctly with constant memory use.
|
||||
* **Still images** — png, jpg, bmp, tif, webp.
|
||||
* **YouTube** — paste `watch?v=…`, `youtu.be/…`, `/shorts/…`, `/live/…`, `/embed/…`, `m.youtube.com`, a
|
||||
`youtube-nocookie` link, or just the 11-character video id. `?t=`/`&t=1m30s` start times are detected.
|
||||
* **Web page** — any `https://` URL becomes a live dashboard/wallpaper.
|
||||
* Drag & drop a file anywhere onto the window, browse for it, or pick from the **Recent** list.
|
||||
|
||||
### Playback (`Playback` tab)
|
||||
| Setting | What it does |
|
||||
|---|---|
|
||||
| Fit mode | Fill (cover/crop), Fit (letterbox), Stretch, Center, **Tile** (repeats small GIFs/images) |
|
||||
| Dim overlay | 0–95 % darkening of the wallpaper + custom tint colour |
|
||||
| Mute / volume | Audio is muted by default; unmute and set volume 0–100 |
|
||||
| Speed | 0.25×–4× for video, GIF and YouTube |
|
||||
| Start at | Skip intros (seconds), also applied on every loop |
|
||||
| Loop forever | Or stop at the end / advance the playlist |
|
||||
| Restart on resume | Jump back to the start offset whenever playback resumes |
|
||||
|
||||
### Displays (`Display` tab)
|
||||
* All displays · primary only · one specific display (live device list with resolution).
|
||||
* **Span mode**: one wallpaper stretched across every monitor (surround setups).
|
||||
* **Interaction mode**: click-through (icons stay usable, default) or interactive (wallpaper raised
|
||||
above the icon layer so YouTube controls and web pages are clickable — Windows hides desktop icons
|
||||
while it's on; toggle with `Ctrl+Alt+I`).
|
||||
* **Re-attach to desktop** button for the rare case where the wallpaper lost its host window.
|
||||
|
||||
### Playlist (`Playlist` tab)
|
||||
Multiple wallpapers of any mix of types, sequential or **shuffle**, rotation interval (1–240 min),
|
||||
"advance when the current video/GIF ends", reorder/add/remove/play-now.
|
||||
|
||||
### Automation (`Automation` tab)
|
||||
* Pause when: a **full-screen app** is in the foreground, a **maximized** window is in front,
|
||||
running **on battery**, the **session is locked**, or over **Remote Desktop**.
|
||||
* **Hide the wallpaper completely while paused** so the GPU goes idle.
|
||||
* Configurable poll interval (0.25–5 s).
|
||||
* **Start with Windows** (HKCU Run entry), start minimized, close-to-tray, tray icon, tray notifications.
|
||||
* **Four global hotkeys** (any `Ctrl/Alt/Shift/Win + key` combination):
|
||||
|
||||
| Action | Default |
|
||||
|---|---|
|
||||
| Pause / resume | `Ctrl+Alt+P` |
|
||||
| Next wallpaper | `Ctrl+Alt+N` |
|
||||
| Reload wallpaper | `Ctrl+Alt+R` |
|
||||
| Toggle interactive mode | `Ctrl+Alt+I` |
|
||||
|
||||
A conflict with another app is reported right under the boxes instead of failing silently.
|
||||
|
||||
### YouTube (`YouTube` tab)
|
||||
Preferred quality (auto/4K/1440p/1080p/720p/480p/360p), start offset, loop, show controls,
|
||||
show related videos, privacy-enhanced `youtube-nocookie.com` embeds, and a free-text field for any raw
|
||||
player parameter (`hl=de&cc_load_policy=1`…).
|
||||
|
||||
### Desktop icons on the wallpaper (`Display` tab)
|
||||
|
||||
When the wallpaper has to run as an **overlay** (see above), it sits in front of the shell's icon
|
||||
window, so the real icons are hidden. LiveWall therefore draws your desktop shortcuts itself:
|
||||
|
||||
* items are read from `%USERPROFILE%\Desktop` and `%PUBLIC%\Desktop` (hidden/system entries skipped),
|
||||
sorted folders-first then alphabetically, with the real shell icons (`SHGetFileInfo`);
|
||||
* click selects, **double-click opens** (`ShellExecute`), right-click offers *Open / Show in File
|
||||
Explorer / Refresh / LiveWall settings / Stop drawing icons*;
|
||||
* the folders are watched with a `FileSystemWatcher`, so new/deleted files appear within ~1.5 s;
|
||||
* icon size (24–128 px), labels, "show on all displays" and "answer right-clicks on empty desktop
|
||||
space" are configurable.
|
||||
|
||||
Since **v1.6** the icons live in *their own* per-pixel transparent window, pinned directly above the
|
||||
wallpaper window instead of being drawn inside it. That matters: WebView2 (YouTube / web pages) and
|
||||
hardware video render into a child `HWND` that is always composited **above every WPF element of the
|
||||
same window**, so the icons used to vanish for exactly those sources. Only the icon pixels are
|
||||
opaque, so clicks between the icons fall through to Windows as usual.
|
||||
|
||||
Since **v1.6.3** the icon window is an **owned window** of the wallpaper window (`GWLP_HWNDPARENT`),
|
||||
which makes Windows itself guarantee that it stays above the wallpaper — no timer, no re-raising, no
|
||||
repaint storm. On top of that a watchdog asks Windows every 2 s *which window is really on top of the
|
||||
first icon*: if the desktop shell is sitting above them, it repairs the layer automatically and says
|
||||
so in the log. **Display ▸ Repair desktop icons** runs the same repair by hand and writes a report.
|
||||
|
||||
Since **v1.6.2** the icon window keeps its own size explicitly (window `Left/Top/Width/Height` in
|
||||
DIPs *and* an explicit canvas size), so it can never be laid out into a zero-sized window — that is
|
||||
how the icons could vanish completely. It also checks itself 1.5 s after it appears, repairs the
|
||||
geometry if needed, and writes a breadcrumb to the log:
|
||||
`Icon window: icons hwnd=0x… shown=True windowVisible=True rect=0,0 1920x1080 expected=… layer=1920x1080 tiles=17 (directly above wallpaper)`.
|
||||
That line is the fastest way to tell "no icons drawn" (`tiles=0`) from "icons drawn but invisible"
|
||||
(`windowVisible=False`, or `rect` not matching `expected`).
|
||||
|
||||
Since **v1.6.1** the two layers no longer fight over the z-order: the icon window re-raises itself
|
||||
only when it is really out of place, the wallpaper only re-orders when it is really not directly
|
||||
above the desktop, and a re-order is *never* "just in case". Each `SetWindowPos` on a layered window
|
||||
forces a repaint, so doing it on every 150 ms timer tick made the icons flicker over video.
|
||||
|
||||
Limitations of this mode: the layout is a grid (Explorer's own icon positions can't be read
|
||||
reliably), there is no drag-to-arrange, and (unless you enable the right-click option) the shell's
|
||||
desktop context menu is the one you get between the icons. Turn the checkbox off for a bare
|
||||
wallpaper with no icons at all.
|
||||
|
||||
### YouTube and web pages (`Wallpaper` tab)
|
||||
|
||||
The player page is served over **https** through a WebView2 virtual-host mapping
|
||||
(`https://livewall.local/player.html`, mapped to `%APPDATA%\LiveWall\web`). Up to v1.5 it was loaded
|
||||
as `file://`, which cannot work: a `file://` page sends no `Referer` (YouTube answers **error 153 —
|
||||
"video player configuration error"**) and its opaque origin breaks the IFrame API `postMessage`
|
||||
handshake, so `onReady` never fires and nothing ever plays. Both are fixed by the https origin.
|
||||
|
||||
LiveWall also keeps an eye on itself and writes the result to the log:
|
||||
|
||||
* `WebView2 runtime 1xx…; https://livewall.local/ -> …` on start-up;
|
||||
* `WebView2 navigation ok (http 200)` / `failed: <WebErrorStatus>`;
|
||||
* `Web probe: {"api":1,"state":1,"t":12.3,…}` every few seconds — `state 1` = playing, `-1` = the API
|
||||
never became ready, `fallback 1` = playing through a plain embed without API control;
|
||||
* if autoplay with sound is refused, the page mutes itself and retries, then reports
|
||||
`forced-mute`; if the API cannot start at all it falls back to a plain `embed/` iframe.
|
||||
|
||||
### Advanced (`Advanced` tab)
|
||||
WebView2 DevTools + context menu, extra Chromium switches, verbose logging, automatic re-attach after
|
||||
Explorer restarts or display changes, recent-list length, open log folder / settings file,
|
||||
reset every setting to defaults, live status log.
|
||||
|
||||
### Plugins (`Plugins` tab)
|
||||
|
||||
LiveWall 1.8 adds a plugin system: .NET 8 assemblies you drop into `%APPDATA%\LiveWall\plugins\` can
|
||||
add wallpaper renderers, draw on the desktop, add settings pages, commands, tray entries and global
|
||||
hotkeys, rewrite wallpaper requests and react to every app event.
|
||||
|
||||
**The plugin system is off by default and every plugin needs two explicit consents.** Turning it on
|
||||
shows a warning that unverified plugins may cause harm to your computer; enabling each individual
|
||||
plugin shows its name, author, folder, SHA-256 hash and the full list of permissions it declared, and
|
||||
nothing is loaded until you tick the acknowledgement and press **Enable**. The accepted hash is
|
||||
remembered: if the file changes, the plugin is unverified again.
|
||||
|
||||
| What you see | What it does |
|
||||
|---|---|
|
||||
| **Enable the plugin system** | Master switch (asks first, stores your consent) |
|
||||
| **Also allow plugins that are not verified on this machine** | Off by default; required for plugins you have not approved before |
|
||||
| **Rescan plugins / Open plugin folder / Create plugin template / Developer docs** | Day-to-day plugin management |
|
||||
| **Installed plugins** | One row per plugin: name, author, state, permissions, *Enable*/*Disable*, *Settings* (the plugin's own pages), *Forget trust* |
|
||||
| **Plugin commands** | Every command a plugin contributes, with a *Run* button |
|
||||
| **Plugin log** | Each loaded plugin, its hash, permissions and what it registered |
|
||||
|
||||
Failed plugins never take LiveWall down: every call into a plugin is guarded, and the reason is shown
|
||||
in the row and written to `%APPDATA%\LiveWall\logs`. A working example ships in
|
||||
`samples\ClockOverlayPlugin\` (a clock overlay + settings page + command + filter + hotkey), and the
|
||||
developer guide is in `docs\plugins\` (`index.md` is the entry point).
|
||||
|
||||
### Tray menu
|
||||
Settings · Pause · Interactive mode · Next wallpaper · Reload wallpaper · **Plugins** (submenu, when a
|
||||
plugin contributes commands) · Open log folder · Exit.
|
||||
|
||||
---
|
||||
|
||||
## 4. How it works (and why it behaves like a real wallpaper)
|
||||
|
||||
Windows draws the desktop in two layers: a shell window (`Progman`) that paints the static wallpaper,
|
||||
and a hidden `WorkerW` window that sits **behind** the one hosting `SHELLDLL_DefView` (your icons).
|
||||
Programs can ask the shell to create that window by sending `WM_SPAWN_WORKER (0x052C)` to `Progman`.
|
||||
|
||||
LiveWall:
|
||||
|
||||
1. sends `0x052C` (with the documented `0xD` w/l parameter variants) and enumerates top-level windows
|
||||
to find the `WorkerW` that is *behind* the icon host;
|
||||
2. creates a borderless WPF `HwndSource` window (`WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE`) per selected
|
||||
display, at the monitor's exact pixel rectangle;
|
||||
3. `SetParent`s it into that `WorkerW`, converts it to a genuine `WS_CHILD` window and keeps it at the
|
||||
bottom of the z-order.
|
||||
|
||||
Because it is a normal child window in the shell's own layer it never appears in Alt+Tab, never steals
|
||||
focus, is covered by normal windows, and the icons draw on top of it. A 1-second watchdog notices when
|
||||
the desktop window disappeared (Explorer restart, resolution change, monitor hot-plug) and rebuilds.
|
||||
|
||||
**Layout-aware, self-verifying attach (v1.4).** The desktop layer is *not* arranged the same way on every Windows
|
||||
build (Windows 11 24H2+ differs from Windows 10), so the app never assumes a layout. It builds a list
|
||||
of candidate parents, in an order that reflects how the shell is actually built on your machine
|
||||
(icons inside Progman → the wallpaper goes *inside Progman below the icon view*; classic layout →
|
||||
the WorkerW behind the icon window), reparents into one, and then **proves it is on screen**: it paints two unmistakable colours
|
||||
in nine small patches spread across the wallpaper, captures those exact pixels from the screen and
|
||||
compares them. Only a strategy that really shows up on screen is kept; the rest are tried in turn and
|
||||
the winner is remembered for the session.
|
||||
|
||||
Each layout is additionally tried with **three hosting techniques**, because whether a window is
|
||||
composited at all depends on the Windows build and on DPI awareness of the shell:
|
||||
|
||||
* *parent at creation* — the HWND is born as a child of the shell window
|
||||
(`HwndSourceParameters.ParentWindow`);
|
||||
* *adopt after first render* — the HWND is born as a normal top-level window, renders one frame,
|
||||
and is then converted to a child with `SetParent` (the classic technique);
|
||||
* *overlay* — the window stays a normal top-level window and is pinned exactly one slot above the
|
||||
desktop window (re-pinned 7×/second, and immediately when the z-order changes). This always
|
||||
composites, but it **covers the desktop icons**.
|
||||
|
||||
While the shell-child techniques are attempted, the app briefly switches its thread to the shell's
|
||||
own DPI awareness context — a mismatch there is a documented cause of "parenting succeeds but the
|
||||
child is never drawn". On builds where child windows are never composited the app now lands on the
|
||||
overlay automatically, so a wallpaper is always visible. The Display tab exposes this as
|
||||
**Wallpaper layer: Auto / Inside the desktop / Overlay**.
|
||||
|
||||
The probe decides which combination paints here. During the very first attach you may see a short
|
||||
flash while it tests — the winner is cached, so later rebuilds are instant and invisible. The host window is created **hidden** and only shown after it
|
||||
has been adopted, which is why there is no black flash while attaching any more.
|
||||
|
||||
Rendering backends:
|
||||
|
||||
* **Media Foundation** (`MediaElement`) for video,
|
||||
* a custom **GIF compositor** (`WriteableBitmap` + `DispatcherTimer`),
|
||||
* **WebView2** for YouTube/web pages, driven through the official YouTube IFrame API with
|
||||
`window.chrome.webview.postMessage` reporting back (e.g. "video ended" → next playlist item).
|
||||
|
||||
Application manifest: `asInvoker` (never needs admin), **Per-Monitor V2 DPI aware**.
|
||||
|
||||
---
|
||||
|
||||
## 5. Troubleshooting
|
||||
|
||||
| Symptom | Fix |
|
||||
|---|---|
|
||||
| **The desktop flashes black for a moment and then nothing happens** | Fixed in v1.1/v1.2: the window is created hidden and only shown after it has been adopted and *proven visible*, and the attach no longer assumes one fixed desktop layout. Make sure you replaced the whole `prebuilt\` folder (About tab shows the version). |
|
||||
| Diagnostics said `SetParent … failed` for every strategy | Fixed in v1.2. The parent check ran while the window was still `WS_POPUP`, where `GetParent()` reports the *owner* instead of the new parent — every successful attach looked like a failure. The check now runs after the window becomes a child (and also cross-checks `GetAncestor`). |
|
||||
| Nothing appears on the desktop | **Advanced ▸ Desktop diagnostics → “Test visibility”**. It prints which shell windows were found, which strategy is active, whether the pixels were confirmed on screen — and it automatically re-detects and re-attaches if the test fails. Also close other desktop-shell modifiers (StartAllBack, ExplorerPatcher, Rainmeter skins that grab WorkerW, Wallpaper Engine) and retry. |
|
||||
| Diagnostics shows every attempt as `NOT visible` with `our window on top at N points` | Send that line: it separates "the window is covered / mis-layered" (N = 0) from "the window is on top but WPF is not painting" (N > 0). `captureBroken=True` means the screen grab is unusable (HDR) and hit-testing alone decided. |
|
||||
| Wallpaper works but desktop icons are hidden | Fixed in v1.5/v1.6 by the **Desktop icons on the wallpaper** layer (Display tab). If you prefer no icons at all, untick it. |
|
||||
| Desktop icons do not appear at all | v1.6.3. Press **Display ▸ Repair desktop icons**, then **Advanced ▸ Diagnostics ▸ Refresh ▸ Copy** and send the `desktop icons:` lines — they state `tiles=N` (0 = nothing was read from your desktop folders), `windowVisible=`, `rect=` vs `expected=`, and whether the window is `directly above wallpaper`. The log also records `Icon watchdog:` lines when the shell covers the icons. |
|
||||
| Older note: | v1.6.2. First check Display ▸ *Draw desktop icons on top of the wallpaper* is still ticked (the icon menu can switch it off), and that you are not in Interactive mode (the icons step aside so the mouse can reach the web page). Then look for the `Icon window: …` line in the log: `tiles=0` means nothing was read from your desktop folders, `windowVisible=False` or a `rect=` that differs from `expected=` means the window itself is wrong. |
|
||||
| Desktop icons flicker or flash | Fixed in v1.6.1 (the wallpaper and the icon layer kept re-raising each other every timer tick; every re-order repaints a layered window). If it still happens, check the log for repeated `Display configuration changed – rebuilding` lines - that would mean something keeps changing the display signature, and v1.6.1 throttles it and says so. |
|
||||
| Icons missing only with video / YouTube / web wallpapers | Fixed in v1.6: the icons moved into their own transparent window, because WebView2 and video paint over anything WPF draws in the wallpaper window. |
|
||||
| YouTube never plays, any video, black screen | Fixed in v1.6 (the player page was loaded from `file://`, which YouTube rejects with error 153). If it still happens, open Advanced ▸ Desktop diagnostics and send the `web probe:` line - it says whether the API became ready and which player state it reached. |
|
||||
| A message says “something went wrong” | That is LiveWall's error surface. Since v1.5 it shows the exception type and message, and the full stack goes to `%APPDATA%\LiveWall\logs`. Paste that line and the problem can be pinned down. |
|
||||
| Wallpaper disappeared after an Explorer crash | It re-attaches automatically within a second or two; otherwise press **Re-attach** or restart LiveWall. |
|
||||
| Video is black / won't play | MP4/H.264 is safest. MKV/AV1/VP9/HEVC usually fail in Media Foundation — re-encode with HandBrake. |
|
||||
| YouTube stays black | Needs internet + WebView2 runtime (ships with Win 11). Some videos forbid embedding — the app tells you the exact error code. |
|
||||
| Can't click YouTube controls | Switch to **Interactive mode** (Display tab or `Ctrl+Alt+I`). Desktop icons are hidden while it's on. |
|
||||
| High CPU/GPU | Enable "pause when a full-screen app is in the foreground", keep "hide while paused", prefer 1080p sources, keep the app muted. |
|
||||
| Wallpaper pauses itself immediately | The status line under the title names the reason (full-screen app, battery, locked session, RDP). v1.1 no longer pauses on battery or hides while paused by default — if you upgraded from 1.0, reset those two checkboxes in the Automation tab. |
|
||||
| Wallpaper is blurry on one of two monitors | Windows blends DPI-scaling when monitors have different scale factors; setting both monitors to the same scale gives pixel-perfect output. |
|
||||
| Hotkey doesn't work | The box turns red with the reason (invalid or already taken). Pick another combination. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Project layout
|
||||
|
||||
```
|
||||
LiveWall/
|
||||
├─ LiveWall.csproj net8.0-windows, UseWPF + UseWindowsForms, WebView2 package
|
||||
├─ app.manifest asInvoker, PerMonitorV2 DPI, long paths
|
||||
├─ App.xaml(.cs) startup, single instance, tray, hotkeys, save debounce, exit
|
||||
├─ MainWindow.xaml(.cs) the whole settings UI (8 tabs) + all handlers
|
||||
├─ Themes/Dark.xaml dark theme: buttons, sliders, tabs, combos, checkboxes, cards
|
||||
├─ Models/AppSettings.cs settings model (INotifyPropertyChanged tree + change bubbling)
|
||||
├─ Services/
|
||||
│ ├─ WallpaperEngine.cs host windows, renderers, pause/playlist state machines, watchdog
|
||||
│ ├─ SystemState.cs full-screen/maximized detection, battery + session-lock tracking
|
||||
│ ├─ SettingsStore.cs JSON persistence (with corrupt-file backup)
|
||||
│ ├─ StartupManager.cs HKCU Run autostart
|
||||
│ ├─ HotkeyManager.cs global hotkeys on a message-only window
|
||||
│ └─ TrayService.cs NotifyIcon + dark context menu
|
||||
├─ Wallpaper/
|
||||
│ ├─ WallpaperWindow.cs host window + self-verifying attach (colour-patch probe)
|
||||
│ ├─ WallpaperRenderer.cs renderer contract + factory
|
||||
│ ├─ VideoRenderer.cs MediaElement playback
|
||||
│ ├─ ImageRenderer.cs images
|
||||
│ ├─ ImageView.cs fit/tile drawing for images & GIFs
|
||||
│ ├─ GifAnimation.cs hand-written GIF frame compositor
|
||||
│ ├─ WebRenderer.cs WebView2 (YouTube + web pages)
|
||||
│ └─ PlayerPage.cs embedded YouTube player host page
|
||||
├─ Interop/
|
||||
│ ├─ Native.cs user32 P/Invokes
|
||||
│ └─ DesktopAttacher.cs WorkerW discovery, parenting, interactive mode
|
||||
├─ Plugins/
|
||||
│ ├─ PluginManifest.cs plugin.json parsing + validation
|
||||
│ ├─ PluginLoader.cs collectible AssemblyLoadContext, SHA-256, state machine
|
||||
│ ├─ PluginHost.cs the host half of IPluginContext: registrar, actions, events, hotkeys
|
||||
│ ├─ PluginSecurity.cs trust store (%APPDATA%\LiveWall\plugin-trust.json)
|
||||
│ ├─ PluginStorage.cs per-plugin JSON key/value storage
|
||||
│ ├─ PluginEventBus.cs type-based publish/subscribe
|
||||
│ ├─ PluginRendererAdapter.cs bridges plugin renderers into the wallpaper window
|
||||
│ └─ PluginWarningWindow the mandatory "unverified plugins may harm your computer" dialog
|
||||
└─ Util/ logging, paths, display enumeration, YouTube URL parser, converter
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Known limitations
|
||||
|
||||
* Video decoding is whatever Media Foundation supports on your PC (no bundled codecs). For exotic
|
||||
formats, re-encode to H.264 MP4.
|
||||
* Interactive mode hides the desktop icons while active (a Windows layering constraint, same as every
|
||||
live-wallpaper app that offers clickable content).
|
||||
* YouTube playback is subject to YouTube's embedding rules and terms of service. Use it the way you'd
|
||||
use the website (this app is not downloading or redistributing anything).
|
||||
* Signing: the binary is unsigned, so SmartScreen may warn on first launch ("More info → Run anyway")
|
||||
and three VirusTotal engines flag the installer. Both go away with a code-signing certificate —
|
||||
see [docs/antivirus.md](docs/antivirus.md).
|
||||
|
||||
---
|
||||
|
||||
## 8. Release notes
|
||||
|
||||
### 1.8.4 — a side menu that stays put
|
||||
|
||||
* **Fixed:** the settings window's navigation strip jittered. The tab headers lived in a WPF
|
||||
`TabPanel`, which is built for a *horizontal* tab strip: it packs the headers into rows and
|
||||
re-measures the whole set whenever one of them changes size. Because the selected header is drawn
|
||||
semi-bold, picking an item re-flowed the list and the other entries jumped around.
|
||||
* The strip now hosts its tabs in a plain vertical `StackPanel` inside a scroll viewer: one item per
|
||||
row, every row the full width of the 196 px column, and a fixed width that does not depend on the
|
||||
selection. The accent bar, hover state and semi-bold label are unchanged.
|
||||
* The column also scrolls now, so the list still fits if a future release adds more sections (or on
|
||||
a very short window).
|
||||
* The app binaries are otherwise unchanged from 1.8.3.
|
||||
|
||||
### 1.8.3 — a setup that looks like the app
|
||||
|
||||
* **New installer UI.** The setup window is now built to match LiveWall's own dark theme instead of
|
||||
the stock Modern-UI wizard: the app's `#0E1116` background, the `#6C8CFF` accent, the icon's
|
||||
indigo-to-violet mark with its green dot, one page that does the choosing and one that confirms.
|
||||
No white header band, no "Nullsoft Install Script" footer, no native button row.
|
||||
* **Flat controls.** The three options (desktop shortcut, start with Windows, start after setup) are
|
||||
square tick boxes, the folder link and the Install/Update/Remove/Finish buttons are flat blocks, all
|
||||
clickable. Enter and Esc still work because the hidden native buttons remain the dialog's defaults.
|
||||
* **Layout scales with DPI.** The page measures the window at run time and places every control as a
|
||||
percentage of it, so 125%/150%/200% displays keep their proportions instead of clumping in a corner.
|
||||
* **No InstFiles page at all.** The files are written while the first page closes, so the window never
|
||||
flashes a second, differently themed screen; the second page simply follows.
|
||||
* **Uninstaller follows the same look**, with the "also delete my settings" choice now on the confirm
|
||||
page instead of a pop-up question.
|
||||
* Everything else is unchanged: per-user install with no UAC, in-place upgrades that keep
|
||||
`%APPDATA%\LiveWall`, same-version and downgrade prompts, `/S` silent installs, and version
|
||||
comparison on `major.minor.patch`.
|
||||
* The app binaries are unchanged apart from the version stamp.
|
||||
|
||||
### 1.8.2 — the cursor comes back
|
||||
|
||||
* **Fixed:** 1.8.1 could make the mouse pointer vanish over the desktop. Two causes, both removed:
|
||||
* the page-level input guard asked for `cursor: none`, which tells Chromium to install a *null*
|
||||
cursor; when the pointer left the page nothing replaced it, and the desktop kept drawing nothing.
|
||||
The guard now asks for `cursor: default` — it still refuses hover feedback, but it never hides
|
||||
the pointer;
|
||||
* the native shield answered `WM_SETCURSOR` with "handled" without setting a cursor itself, so
|
||||
whatever cursor the page left behind stayed in place. It now puts the standard arrow back every
|
||||
time (`LiveWall/Interop/Native.cs ▸ RestoreArrowCursor`), including on mouse-leave and whenever
|
||||
the shield is switched off.
|
||||
* If the pointer ever does go missing on some configuration, `Ctrl+Alt+I` twice (interactive on/off)
|
||||
forces the shield through a full restore.
|
||||
|
||||
### 1.8.1 — the wallpaper stops listening to the mouse
|
||||
|
||||
* **Fixed:** with a YouTube (or any web page) wallpaper, moving the cursor across the desktop made the
|
||||
player react — controls appearing on hover, links highlighting, the progress bar scrubbing, the
|
||||
cursor turning interactive. WebView2 hosts Chromium in real child windows inside the wallpaper
|
||||
window, and nothing that makes a *WPF* window click-through reaches them: the desktop icon layer is
|
||||
a transparent layered window, so the pointer fell straight through it onto the Chromium surface.
|
||||
Now, unless you turn on **Interactive mode**:
|
||||
* the wallpaper window answers `WM_NCHITTEST` with `HTTRANSPARENT` (this is the part that
|
||||
`WS_EX_TRANSPARENT` cannot do for a plain child window, and the part that previously let clicks
|
||||
stop at the wallpaper instead of reaching the desktop);
|
||||
* every Chromium child window is subclassed: `HTTRANSPARENT`, no mouse/pointer/wheel/touch
|
||||
messages, no cursor changes, no activation — plus `WS_EX_TRANSPARENT` so the hit test skips them
|
||||
in the first place (see `LiveWall/Wallpaper/WebInputShield.cs`);
|
||||
* as a second layer, the page itself gets `pointer-events: none` and a capture-phase blocker for
|
||||
wheel/drag/selection, so nothing can show a hover state even if a message slips through.
|
||||
Interactive mode removes all three layers again, and the diagnostics report now says which state
|
||||
the web surface is in (`web input: blocked (mouse pass-through, N window(s) shielded)`).
|
||||
* The desktop icons keep their normal behaviour: clicks over the wallpaper still land on the desktop,
|
||||
never on the page.
|
||||
|
||||
### 1.8.0 — plugins
|
||||
|
||||
* Plugin system with overlay, renderer, filter, settings-page, command, tray and hotkey extension
|
||||
points, a hash-pinned trust store, and a mandatory warning before anything runs.
|
||||
* `LiveWall.Sdk`, a complete example plugin (`samples/ClockOverlayPlugin`) and a developer guide
|
||||
(`docs/plugins/`), shipped inside the installer.
|
||||
|
||||
Reference in New Issue
Block a user