initial commit
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user