Files
2026-11-04 14:29:22 -06:00

175 lines
5.8 KiB
Markdown

# Getting started
This walkthrough builds a plugin that draws a clock on the desktop, then adds a settings page and a
tray command. Ten minutes, no LiveWall source needed.
## 1. Create the project
```bash
mkdir MyClock && cd MyClock
dotnet new classlib -n MyClock -f net8.0-windows
```
Edit `MyClock.csproj`:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF> <!-- overlays and pages are WPF elements -->
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- Point this at LiveWall.Sdk.dll or its project. Private=false keeps the contract out of
your output: LiveWall supplies it at run time. -->
<ProjectReference Include="..\LiveWall.Sdk\LiveWall.Sdk.csproj">
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
</ItemGroup>
</Project>
```
If you only have the shipped binaries, reference the DLL instead:
```xml
<Reference Include="LiveWall.Sdk">
<HintPath>C:\path\to\LiveWall\LiveWall.Sdk.dll</HintPath>
<Private>false</Private>
</Reference>
```
> **Never ship `LiveWall.Sdk.dll` in your plugin folder.** LiveWall loads the contract itself; a second
> copy would give you a *different* `ILiveWallPlugin` type and the plugin would be rejected.
## 2. Write the plugin
`ClockPlugin.cs`:
```csharp
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using LiveWall.Sdk;
public sealed class ClockPlugin : ILiveWallPlugin
{
private TextBlock? _clock;
public void Initialize(IPluginContext context)
{
context.Log.Info("Clock plugin starting");
context.Extensions.AddOverlay(new ClockOverlay(this));
context.Extensions.AddCommand(new PluginCommand("clock.say", "Clock: say hello",
cmd => cmd.Host.ShowNotification("Clock", "Hello!"))
{
Category = "Clock"
});
context.Extensions.AddSettingsPage(new ClockSettingsPage(context));
}
public void Shutdown() => _clock = null;
private sealed class ClockOverlay : IWallpaperOverlay
{
private readonly ClockPlugin _plugin;
public ClockOverlay(ClockPlugin plugin) => _plugin = plugin;
public string Name => "Clock";
public FrameworkElement CreateView(OverlayContext context)
{
_plugin._clock = new TextBlock
{
Text = "12:00",
FontFamily = new FontFamily("Segoe UI Light"),
FontSize = 44,
Foreground = Brushes.White,
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 24, 32, 0)
};
return _plugin._clock;
}
public void Update(OverlayState state) { /* wallpaper changed - optional */ }
}
private sealed class ClockSettingsPage : IPluginSettingsPage
{
private readonly IPluginContext _context;
public ClockSettingsPage(IPluginContext context) => _context = context;
public string Title => "Clock";
public string Description => "Colour of the desktop clock.";
public FrameworkElement CreateView()
{
var box = new TextBox { Text = _context.Storage.Get("colour", "#FFFFFF"), Width = 140 };
box.TextChanged += (_, _) => _context.Storage.Set("colour", box.Text);
return box;
}
}
}
```
`plugin.json` (copy it next to the assembly, and add
`<None Include="plugin.json" CopyToOutputDirectory="PreserveNewest" />` to the csproj):
```json
{
"id": "com.example.clock",
"name": "Desktop Clock",
"version": "1.0.0",
"author": "Your name",
"description": "Draws a clock on top of the wallpaper.",
"apiVersion": "1.0",
"entry": "MyClock.dll",
"type": "ClockPlugin",
"minHostVersion": "1.8.0",
"permissions": ["DesktopOverlay", "SettingsUi", "Commands"]
}
```
## 3. Build and install
```bash
dotnet build -c Release
mkdir "%APPDATA%\LiveWall\plugins\com.example.clock"
copy bin\Release\net8.0-windows\MyClock.dll "%APPDATA%\LiveWall\plugins\com.example.clock\"
copy bin\Release\net8.0-windows\plugin.json "%APPDATA%\LiveWall\plugins\com.example.clock\"
```
Then in LiveWall:
1. **Plugins** tab → tick **Enable the plugin system** → read the warning → accept.
2. Press **Rescan plugins**. *Desktop Clock* appears with an **unverified** badge.
3. Press **Enable** → the per-plugin warning appears, listing the hash and the three permissions →
accept. The clock appears on your desktop.
The folder can be opened from the app: **Plugins ▸ Open plugin folder**.
## 4. Debugging
* Everything you log goes to `%APPDATA%\LiveWall\logs\livewall-YYYYMMDD.log`, prefixed with your
plugin id: `[com.example.clock] Clock plugin starting`.
* `Plugins ▸ Plugin log` shows the same summary in the app, and **Advanced ▸ Diagnostics** includes a
`— Plugins —` section with each plugin's state, hash and permissions.
* Attach a debugger with **Debug ▸ Attach to Process ▸ LiveWall.exe**. Your plugin's exceptions are
visible in the Visual Studio exception window; `Initialize` is called on the UI thread.
* Edit → **Rescan** reloads your assembly without restarting LiveWall (the collectible load context
really does unload — see [troubleshooting.md](troubleshooting.md#unloading)).
* After changing code, rebuild, overwrite the DLL, and press **Disable** then **Enable** (or Rescan).
## 5. Next steps
* [extension-points.md](extension-points.md) — every extension point, with the airspace and threading
caveats you will hit as soon as you do something real.
* [api-reference.md](api-reference.md) — the complete surface.
* [examples.md](examples.md) — recipes: custom renderer, source resolver, playlist provider.