175 lines
5.8 KiB
Markdown
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.
|