141 lines
5.7 KiB
Markdown
141 lines
5.7 KiB
Markdown
# LiveWall plugin SDK
|
|
|
|
LiveWall 1.8.0 and later can be extended by **plugins**: .NET 8 assemblies that LiveWall loads at
|
|
start-up and hands a context object. A plugin can add wallpaper renderers, draw on the desktop, add
|
|
settings pages, add commands and tray entries, register hotkeys, rewrite wallpaper requests, and read
|
|
the same events the app itself uses.
|
|
|
|
> **Plugins are third-party code.** They run inside LiveWall's process with your rights. LiveWall does
|
|
> not review, sign or sandbox them, and unverified plugins may cause harm to your computer. The user
|
|
> is shown a warning listing the plugin's exact file hash and every permission it declared before it is
|
|
> allowed to run — see [security.md](security.md).
|
|
|
|
---
|
|
|
|
## Contents
|
|
|
|
| Document | What it covers |
|
|
|---|---|
|
|
| [getting-started.md](getting-started.md) | From an empty folder to a running plugin in ten minutes |
|
|
| [api-reference.md](api-reference.md) | Every interface, member and enum in `LiveWall.Sdk` |
|
|
| [extension-points.md](extension-points.md) | Overlays, renderers, filters, pages, commands, hotkeys — with the caveats |
|
|
| [events.md](events.md) | The event catalogue, threading rules and lifetime |
|
|
| [security.md](security.md) | Permissions, trust, hashes, the warning dialog, threat model |
|
|
| [packaging.md](packaging.md) | Folder layout, versioning, checksums, distribution, updates |
|
|
| [troubleshooting.md](troubleshooting.md) | "My plugin does not load", reading the log, unload rules |
|
|
| [examples.md](examples.md) | Copy-paste recipes for the common shapes |
|
|
|
|
The SDK itself is `LiveWall.Sdk.dll` — a small contract assembly. Plugins reference it and nothing
|
|
else; `LiveWall.exe` is deliberately *not* a dependency.
|
|
|
|
---
|
|
|
|
## What a plugin can change
|
|
|
|
| Extension point | Interface | Typical use |
|
|
|---|---|---|
|
|
| Desktop overlay | `IWallpaperOverlay` | Clock, now-playing badge, visualiser, watermark, fps counter |
|
|
| Wallpaper renderer | `IWallpaperRendererPlugin` + `IWallpaperRenderer` | Shaders, emulators, animated 3D scenes, a custom video pipeline |
|
|
| Wallpaper filter | `IWallpaperFilter` | Resolve short links, pick a random source, block changes, add metadata |
|
|
| Settings page | `IPluginSettingsPage` | Your own configuration UI inside LiveWall |
|
|
| Command | `PluginCommand` | "Next quote", "Toggle rain", anything the user can trigger |
|
|
| Tray entry | `PluginCommand` + `TrayMenu` permission | Same commands, reachable with two clicks |
|
|
| Hotkey | `PluginHotkey` | Global shortcut for a plugin action |
|
|
| Events | `IPluginEvents` | React to wallpaper/playback/session changes |
|
|
| Host actions | `IHostActions` | Set the wallpaper, play/pause, notify, open settings |
|
|
| Storage | `IPluginStorage` | Persist your own JSON state, own folders |
|
|
|
|
Everything is registered through `IPluginContext.Extensions` inside `Initialize`. There is no
|
|
"unregister everything" step: LiveWall drops all of a plugin's registrations when it unloads it.
|
|
|
|
---
|
|
|
|
## How loading works (short version)
|
|
|
|
```
|
|
%APPDATA%\LiveWall\plugins\
|
|
└── com.example.clock\ ← one folder per plugin, any name
|
|
├── plugin.json ← identity + permissions (required)
|
|
├── ClockOverlayPlugin.dll ← entry assembly
|
|
└── (any other files you need)
|
|
```
|
|
|
|
1. LiveWall scans the plugin folder at start-up (and whenever the user presses **Rescan**).
|
|
2. `plugin.json` is validated: API version, host version, optional checksum.
|
|
3. Plugins the user has **not** enabled stay on disk, unloaded, listed in **Plugins ▸ Installed plugins**.
|
|
4. Enabling runs the warning dialog. Only after the user accepts is the assembly loaded — into a
|
|
collectible `AssemblyLoadContext` — and `ILiveWallPlugin.Initialize(context)` called.
|
|
5. Any exception in `Initialize` marks the plugin **Failed** and shows the message. The app keeps
|
|
running; a plugin can never take LiveWall down.
|
|
|
|
Full details in [packaging.md](packaging.md) and [troubleshooting.md](troubleshooting.md).
|
|
|
|
---
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
dotnet new classlib -n MyPlugin -f net8.0-windows
|
|
```
|
|
|
|
```xml
|
|
<Project Sdk="Microsoft.NET.Sdk">
|
|
<PropertyGroup>
|
|
<TargetFramework>net8.0-windows</TargetFramework>
|
|
<UseWPF>true</UseWPF>
|
|
<Nullable>enable</Nullable>
|
|
<ImplicitUsings>enable</ImplicitUsings>
|
|
</PropertyGroup>
|
|
<ItemGroup>
|
|
<ProjectReference Include="path\to\LiveWall.Sdk\LiveWall.Sdk.csproj">
|
|
<Private>false</Private>
|
|
</ProjectReference>
|
|
</ItemGroup>
|
|
</Project>
|
|
```
|
|
|
|
```csharp
|
|
using LiveWall.Sdk;
|
|
|
|
public sealed class MyPlugin : ILiveWallPlugin
|
|
{
|
|
public void Initialize(IPluginContext context)
|
|
=> context.Host.ShowNotification("MyPlugin", "Hello from the wallpaper!");
|
|
|
|
public void Shutdown() { }
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"id": "com.example.myplugin",
|
|
"name": "My plugin",
|
|
"version": "1.0.0",
|
|
"author": "you",
|
|
"description": "Does one small thing.",
|
|
"apiVersion": "1.0",
|
|
"entry": "MyPlugin.dll",
|
|
"type": "MyPlugin",
|
|
"minHostVersion": "1.8.0",
|
|
"permissions": ["Commands"]
|
|
}
|
|
```
|
|
|
|
Copy the build output next to the manifest, then use **Plugins ▸ Rescan** and **Enable**.
|
|
|
|
The repository ships a complete, buildable example in `samples/ClockOverlayPlugin/` that uses every
|
|
extension point — read it next to [extension-points.md](extension-points.md).
|
|
|
|
---
|
|
|
|
## Stability promise
|
|
|
|
The contracts live in `LiveWall.Sdk` and are versioned separately from LiveWall's own version:
|
|
|
|
* `PluginApi.Version` (currently **1.0**) is what `plugin.json` must declare.
|
|
* Adding members to an existing interface would break compiled plugins, so new capabilities arrive as
|
|
**new optional interfaces**, and the major version only changes if something must be removed.
|
|
* `minHostVersion` in the manifest refuses to run on an older LiveWall instead of failing mysteriously.
|
|
|
|
See [packaging.md](packaging.md#versioning).
|