initial commit
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace LiveWall.Sdk;
|
||||
|
||||
/// <summary>Version of the plugin API. Bumped when contracts change in an incompatible way.</summary>
|
||||
public static class PluginApi
|
||||
{
|
||||
/// <summary>Current contract version. Plugins must declare a compatible version in plugin.json.</summary>
|
||||
public const string Version = "1.0";
|
||||
|
||||
/// <summary>Host application version this SDK shipped with (informational).</summary>
|
||||
public const string HostVersion = "1.8.4";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What a plugin is allowed to do, declared in its <c>plugin.json</c> and shown to the user before
|
||||
/// the plugin is enabled. These are declarations, not a sandbox: a plugin runs inside LiveWall's
|
||||
/// process with full .NET rights. They exist so the user knows what they are agreeing to.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PluginPermission
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>Read the current wallpaper, playback state and a snapshot of the settings.</summary>
|
||||
ReadWallpaper = 1 << 0,
|
||||
|
||||
/// <summary>Play / pause / next / set the wallpaper.</summary>
|
||||
ControlPlayback = 1 << 1,
|
||||
|
||||
/// <summary>Draw a layer on top of the wallpaper (clocks, visualisers, badges…).</summary>
|
||||
DesktopOverlay = 1 << 2,
|
||||
|
||||
/// <summary>Contribute pages to the LiveWall settings window.</summary>
|
||||
SettingsUi = 1 << 3,
|
||||
|
||||
/// <summary>Contribute commands the user can run from the app.</summary>
|
||||
Commands = 1 << 4,
|
||||
|
||||
/// <summary>Contribute entries to the tray menu.</summary>
|
||||
TrayMenu = 1 << 5,
|
||||
|
||||
/// <summary>Register global hotkeys.</summary>
|
||||
Hotkeys = 1 << 6,
|
||||
|
||||
/// <summary>Read and write files (the plugin's data folder, and anything the user points it at).</summary>
|
||||
FileSystem = 1 << 7,
|
||||
|
||||
/// <summary>Make outbound network requests.</summary>
|
||||
Network = 1 << 8,
|
||||
|
||||
/// <summary>Supply a wallpaper renderer for sources it claims (overrides the built-in players).</summary>
|
||||
Renderer = 1 << 9,
|
||||
|
||||
/// <summary>Rewrite or veto wallpaper requests before the engine applies them.</summary>
|
||||
Filters = 1 << 10,
|
||||
|
||||
All = ReadWallpaper | ControlPlayback | DesktopOverlay | SettingsUi | Commands | TrayMenu |
|
||||
Hotkeys | FileSystem | Network | Renderer | Filters
|
||||
}
|
||||
|
||||
/// <summary>Human readable description of what every permission means (used by the warning dialog).</summary>
|
||||
public static class PluginPermissionInfo
|
||||
{
|
||||
public static string Describe(PluginPermission permission) => permission switch
|
||||
{
|
||||
PluginPermission.ReadWallpaper => "Read your current wallpaper, playback state and settings",
|
||||
PluginPermission.ControlPlayback => "Start, stop, skip and replace your wallpaper",
|
||||
PluginPermission.DesktopOverlay => "Draw content on top of your desktop wallpaper",
|
||||
PluginPermission.SettingsUi => "Add pages to the LiveWall settings window",
|
||||
PluginPermission.Commands => "Add commands to LiveWall",
|
||||
PluginPermission.TrayMenu => "Add entries to the LiveWall tray menu",
|
||||
PluginPermission.Hotkeys => "Register system-wide keyboard shortcuts",
|
||||
PluginPermission.FileSystem => "Read and write files on your computer",
|
||||
PluginPermission.Network => "Connect to the internet",
|
||||
PluginPermission.Renderer => "Take over wallpaper playback with its own renderer",
|
||||
PluginPermission.Filters => "Rewrite or block wallpaper changes",
|
||||
_ => permission.ToString()
|
||||
};
|
||||
|
||||
/// <summary>Expands a flag set into individual descriptions.</summary>
|
||||
public static IReadOnlyList<(PluginPermission Permission, string Description)> Expand(PluginPermission permissions)
|
||||
{
|
||||
var list = new List<(PluginPermission, string)>();
|
||||
foreach (PluginPermission p in Enum.GetValues<PluginPermission>())
|
||||
{
|
||||
if (p is PluginPermission.None or PluginPermission.All) continue;
|
||||
if (permissions.HasFlag(p)) list.Add((p, Describe(p)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Everything the host knows about a plugin. Filled in by LiveWall before Initialize.</summary>
|
||||
public sealed class PluginInfo
|
||||
{
|
||||
public string Id { get; init; } = "";
|
||||
public string Name { get; init; } = "";
|
||||
public string Version { get; init; } = "";
|
||||
public string Author { get; init; } = "";
|
||||
public string Description { get; init; } = "";
|
||||
public string Homepage { get; init; } = "";
|
||||
public string ApiVersion { get; init; } = "";
|
||||
public PluginPermission Permissions { get; init; }
|
||||
|
||||
/// <summary>Absolute path of the plugin folder.</summary>
|
||||
public string Folder { get; init; } = "";
|
||||
|
||||
/// <summary>Absolute path of the plugin assembly.</summary>
|
||||
public string AssemblyPath { get; init; } = "";
|
||||
|
||||
/// <summary>SHA-256 of the plugin assembly (lower case hex).</summary>
|
||||
public string Sha256 { get; init; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// True when the loaded assembly hash matches the hash the user trusted on this machine, or the
|
||||
/// publisher ships it in LiveWall's built-in trusted list. Unverified plugins run with a warning.
|
||||
/// </summary>
|
||||
public bool Verified { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>The plugin entry point. One public class implementing this per assembly.</summary>
|
||||
public interface ILiveWallPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Called once, on the UI thread, after the user enabled the plugin. Register your extensions
|
||||
/// and subscribe to events here. Exceptions are caught, logged and disable the plugin.
|
||||
/// </summary>
|
||||
void Initialize(IPluginContext context);
|
||||
|
||||
/// <summary>Called when the plugin is disabled, reloaded or the app is closing.</summary>
|
||||
void Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>Everything a plugin can reach. One instance per plugin.</summary>
|
||||
public interface IPluginContext
|
||||
{
|
||||
PluginInfo Info { get; }
|
||||
|
||||
/// <summary>Writes to the LiveWall log next to the host's own messages.</summary>
|
||||
IPluginLog Log { get; }
|
||||
|
||||
/// <summary>Persistent key/value storage owned by the plugin (JSON file in its data folder).</summary>
|
||||
IPluginStorage Storage { get; }
|
||||
|
||||
/// <summary>Read-only snapshot of the user's configuration.</summary>
|
||||
ISettingsSnapshot Settings { get; }
|
||||
|
||||
/// <summary>Actions a plugin may trigger in the host.</summary>
|
||||
IHostActions Host { get; }
|
||||
|
||||
/// <summary>Type based publish/subscribe bus.</summary>
|
||||
IPluginEvents Events { get; }
|
||||
|
||||
/// <summary>Registers extension points (overlays, pages, commands, renderers, filters, hotkeys).</summary>
|
||||
IPluginRegistrar Extensions { get; }
|
||||
|
||||
/// <summary>Cancelled when the plugin is being shut down. Pass it to your async work.</summary>
|
||||
CancellationToken ShutdownToken { get; }
|
||||
}
|
||||
|
||||
/// <summary>Logging façade. Everything lands in %APPDATA%\LiveWall\logs.</summary>
|
||||
public interface IPluginLog
|
||||
{
|
||||
void Debug(string message);
|
||||
void Info(string message);
|
||||
void Warn(string message);
|
||||
void Error(string message, Exception? exception = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per plugin JSON storage. Values are serialised with System.Text.Json, so any POCO works.
|
||||
/// </summary>
|
||||
public interface IPluginStorage
|
||||
{
|
||||
/// <summary>The plugin's private data folder (created on demand).</summary>
|
||||
string DataFolder { get; }
|
||||
|
||||
T? Get<T>(string key, T? fallback = default);
|
||||
void Set<T>(string key, T value);
|
||||
bool Remove(string key);
|
||||
IReadOnlyCollection<string> Keys { get; }
|
||||
|
||||
/// <summary>Absolute path inside the data folder; the directory is created for you.</summary>
|
||||
string PathFor(string fileName);
|
||||
|
||||
/// <summary>Writes pending changes immediately (normally done automatically).</summary>
|
||||
void Flush();
|
||||
}
|
||||
|
||||
/// <summary>Read-only view of the live settings.</summary>
|
||||
public interface ISettingsSnapshot
|
||||
{
|
||||
Guid SnapshotId { get; }
|
||||
string SourceKind { get; } // "Video", "AnimatedGif", "Image", "YouTube", "WebPage"
|
||||
string SourceLocation { get; } // file path or URL
|
||||
bool Muted { get; }
|
||||
double Volume { get; } // 0–100
|
||||
double Speed { get; } // 0.25–4
|
||||
bool Loop { get; }
|
||||
bool InteractiveMode { get; }
|
||||
bool Paused { get; }
|
||||
int DimPercent { get; }
|
||||
string DimColor { get; }
|
||||
IReadOnlyList<string> DisplayDevices { get; }
|
||||
}
|
||||
|
||||
/// <summary>The current wallpaper, as seen by plugins.</summary>
|
||||
public sealed class WallpaperState
|
||||
{
|
||||
public string Kind { get; init; } = "";
|
||||
public string Source { get; init; } = "";
|
||||
public string Title { get; init; } = "";
|
||||
public bool IsPlaying { get; init; }
|
||||
public bool IsPaused { get; init; }
|
||||
public bool IsInteractive { get; init; }
|
||||
public bool SuppliedByPlugin { get; init; }
|
||||
public string RendererName { get; init; } = "";
|
||||
}
|
||||
|
||||
/// <summary>Host actions. Each one is a no-op when the plugin lacks the permission for it.</summary>
|
||||
public interface IHostActions
|
||||
{
|
||||
WallpaperState Current { get; }
|
||||
|
||||
/// <summary>Replaces the wallpaper (needs <see cref="PluginPermission.ControlPlayback"/>).</summary>
|
||||
void SetWallpaper(WallpaperRequest request);
|
||||
|
||||
void Play();
|
||||
void Pause();
|
||||
void TogglePause();
|
||||
void NextWallpaper();
|
||||
void ReloadWallpaper();
|
||||
void Reattach();
|
||||
|
||||
/// <summary>Opens the LiveWall settings window.</summary>
|
||||
void OpenSettings();
|
||||
|
||||
/// <summary>Status line in the settings window.</summary>
|
||||
void SetStatus(string text);
|
||||
|
||||
/// <summary>Balloon notification (respects the user's notification setting).</summary>
|
||||
void ShowNotification(string title, string message);
|
||||
|
||||
/// <summary>Shows or hides the desktop overlay layer for this plugin.</summary>
|
||||
void SetOverlayVisible(bool visible);
|
||||
}
|
||||
|
||||
/// <summary>A wallpaper a plugin wants to show.</summary>
|
||||
public sealed class WallpaperRequest
|
||||
{
|
||||
/// <summary>"Video", "AnimatedGif", "Image", "YouTube", "WebPage" or "Custom".</summary>
|
||||
public string Kind { get; set; } = "Custom";
|
||||
|
||||
/// <summary>File path, URL or any string your renderer interprets.</summary>
|
||||
public string Source { get; set; } = "";
|
||||
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
/// <summary>Free-form data for your own renderer (survives filters, not persisted).</summary>
|
||||
public Dictionary<string, string> Metadata { get; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public WallpaperRequest Clone()
|
||||
{
|
||||
var clone = new WallpaperRequest { Kind = Kind, Source = Source, Title = Title };
|
||||
foreach (var pair in Metadata) clone.Metadata[pair.Key] = pair.Value;
|
||||
return clone;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Kind}:{Source}";
|
||||
}
|
||||
|
||||
/// <summary>Registers extension points. All calls are thread safe and may happen in Initialize.</summary>
|
||||
public interface IPluginRegistrar
|
||||
{
|
||||
/// <summary>Draws above the wallpaper (needs <see cref="PluginPermission.DesktopOverlay"/>).</summary>
|
||||
void AddOverlay(IWallpaperOverlay overlay);
|
||||
|
||||
void RemoveOverlay(IWallpaperOverlay overlay);
|
||||
|
||||
/// <summary>Adds a page to the Plugins tab (needs <see cref="PluginPermission.SettingsUi"/>).</summary>
|
||||
void AddSettingsPage(IPluginSettingsPage page);
|
||||
|
||||
/// <summary>Adds a command to the app and the tray menu.</summary>
|
||||
void AddCommand(PluginCommand command);
|
||||
|
||||
/// <summary>Adds a wallpaper filter (needs <see cref="PluginPermission.Filters"/>).</summary>
|
||||
void AddFilter(IWallpaperFilter filter);
|
||||
|
||||
/// <summary>Takes over rendering of sources it claims (needs <see cref="PluginPermission.Renderer"/>).</summary>
|
||||
void AddRenderer(IWallpaperRendererPlugin renderer);
|
||||
|
||||
/// <summary>Registers a global hotkey such as "Ctrl+Alt+K" (needs <see cref="PluginPermission.Hotkeys"/>).</summary>
|
||||
void AddHotkey(PluginHotkey hotkey);
|
||||
}
|
||||
|
||||
/// <summary>An entry in the app's plugin command list and the tray menu.</summary>
|
||||
public sealed class PluginCommand
|
||||
{
|
||||
public PluginCommand(string id, string title, Action<PluginCommandContext> execute)
|
||||
{
|
||||
Id = id;
|
||||
Title = title;
|
||||
Execute = execute;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>Optional grouping label, e.g. "Wallpaper".</summary>
|
||||
public string Category { get; set; } = "";
|
||||
|
||||
/// <summary>Return false to disable the entry (evaluated when the menu opens).</summary>
|
||||
public Func<bool>? CanExecute { get; set; }
|
||||
|
||||
internal Action<PluginCommandContext> Execute { get; }
|
||||
|
||||
public void Run(PluginCommandContext context) => Execute(context);
|
||||
}
|
||||
|
||||
/// <summary>Passed to <see cref="PluginCommand"/>.</summary>
|
||||
public sealed class PluginCommandContext
|
||||
{
|
||||
public PluginCommandContext(IPluginContext plugin, IHostActions host)
|
||||
{
|
||||
Plugin = plugin;
|
||||
Host = host;
|
||||
}
|
||||
|
||||
public IPluginContext Plugin { get; }
|
||||
public IHostActions Host { get; }
|
||||
}
|
||||
|
||||
/// <summary>A global keyboard shortcut contributed by a plugin.</summary>
|
||||
public sealed class PluginHotkey
|
||||
{
|
||||
public PluginHotkey(string id, string gesture, Action action)
|
||||
{
|
||||
Id = id;
|
||||
Gesture = gesture;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>Combination such as "Ctrl+Alt+K", parsed like LiveWall's own hotkeys.</summary>
|
||||
public string Gesture { get; }
|
||||
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
public Action Action { get; }
|
||||
}
|
||||
|
||||
/// <summary>Raised by plugins to inform others about something (or to listen).</summary>
|
||||
public interface IPluginEvents
|
||||
{
|
||||
IDisposable Subscribe<TEvent>(Action<TEvent> handler) where TEvent : class;
|
||||
void Publish<TEvent>(TEvent payload) where TEvent : class;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
namespace LiveWall.Sdk;
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Events published on IPluginEvents. Subscribe from ILiveWallPlugin.Initialize:
|
||||
//
|
||||
// context.Events.Subscribe<WallpaperChangedEvent>(e => context.Log.Info($"now: {e.Source}"));
|
||||
//
|
||||
// Handlers always run on the WPF UI thread.
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>The host finished starting: settings are loaded and the wallpaper is attached.</summary>
|
||||
public sealed class AppStartedEvent
|
||||
{
|
||||
public string HostVersion { get; init; } = "";
|
||||
public string PluginApiVersion { get; init; } = "";
|
||||
}
|
||||
|
||||
/// <summary>LiveWall is shutting down. Unsubscribe and release resources now.</summary>
|
||||
public sealed class AppStoppingEvent
|
||||
{
|
||||
public string Reason { get; init; } = "exit";
|
||||
}
|
||||
|
||||
/// <summary>A new wallpaper was applied (kind/source/renderer as they are now).</summary>
|
||||
public sealed class WallpaperChangedEvent
|
||||
{
|
||||
public string Kind { get; init; } = "";
|
||||
public string Source { get; init; } = "";
|
||||
public string RendererName { get; init; } = "";
|
||||
public bool FromPlugin { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Playback started, stopped or was suspended (lock screen, battery, fullscreen…).</summary>
|
||||
public sealed class PlaybackChangedEvent
|
||||
{
|
||||
public bool IsPlaying { get; init; }
|
||||
public bool IsPaused { get; init; }
|
||||
public string Reason { get; init; } = "";
|
||||
}
|
||||
|
||||
/// <summary>Interactive mode was toggled.</summary>
|
||||
public sealed class InteractionChangedEvent
|
||||
{
|
||||
public bool Interactive { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Any settings change. <see cref="Property"/> is the property name that changed.</summary>
|
||||
public sealed class SettingsChangedEvent
|
||||
{
|
||||
public string Property { get; init; } = "";
|
||||
public ISettingsSnapshot Snapshot { get; init; } = null!;
|
||||
}
|
||||
|
||||
/// <summary>Display topology changed or the wallpaper was re-attached.</summary>
|
||||
public sealed class DisplayChangedEvent
|
||||
{
|
||||
public IReadOnlyList<string> Devices { get; init; } = Array.Empty<string>();
|
||||
public bool Reattached { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Session state (lock screen, battery, remote desktop) changed.</summary>
|
||||
public sealed class SessionChangedEvent
|
||||
{
|
||||
public bool Locked { get; init; }
|
||||
public bool OnBattery { get; init; }
|
||||
public bool RemoteSession { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Something failed inside the host; useful for plugins that surface errors.</summary>
|
||||
public sealed class HostErrorEvent
|
||||
{
|
||||
public string Message { get; init; } = "";
|
||||
public string Source { get; init; } = "";
|
||||
}
|
||||
|
||||
/// <summary>Plugin-to-plugin messaging: publish any class you like.</summary>
|
||||
public sealed class PluginMessageEvent
|
||||
{
|
||||
public string FromPluginId { get; init; } = "";
|
||||
public string Topic { get; init; } = "";
|
||||
public string? Payload { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace LiveWall.Sdk;
|
||||
|
||||
/// <summary>
|
||||
/// A visual layer drawn on top of the wallpaper, inside LiveWall's desktop overlay window (the same
|
||||
/// window that carries the desktop icons). Because that window is per-pixel transparent, only the
|
||||
/// pixels you paint block the mouse - everything else falls through to the desktop.
|
||||
/// </summary>
|
||||
public interface IWallpaperOverlay
|
||||
{
|
||||
/// <summary>Shown in the plugin's UI and the diagnostics report.</summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the visual. Called once per display, on the UI thread. Keep it cheap and transparent.
|
||||
/// The returned element is stretched to the display and is not hit-testable by default.
|
||||
/// </summary>
|
||||
FrameworkElement CreateView(OverlayContext context);
|
||||
|
||||
/// <summary>Called whenever the wallpaper, playback or interaction state changes.</summary>
|
||||
void Update(OverlayState state);
|
||||
|
||||
/// <summary>Called when the user hides the overlay or the plugin is disabled.</summary>
|
||||
void OnVisibilityChanged(bool visible) { }
|
||||
}
|
||||
|
||||
/// <summary>Display specific context handed to <see cref="IWallpaperOverlay.CreateView"/>.</summary>
|
||||
public sealed class OverlayContext
|
||||
{
|
||||
public int DisplayIndex { get; init; }
|
||||
public string DeviceName { get; init; } = "";
|
||||
public double Width { get; init; }
|
||||
public double Height { get; init; }
|
||||
public double DpiScale { get; init; } = 1.0;
|
||||
public bool Primary { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>State handed to <see cref="IWallpaperOverlay.Update"/>.</summary>
|
||||
public sealed class OverlayState
|
||||
{
|
||||
public string Kind { get; init; } = "";
|
||||
public string Source { get; init; } = "";
|
||||
public string Title { get; init; } = "";
|
||||
public bool IsPlaying { get; init; }
|
||||
public bool IsPaused { get; init; }
|
||||
public bool IsInteractive { get; init; }
|
||||
public DateTime TimestampUtc { get; init; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>A page the plugin adds to the LiveWall settings window (Plugins tab).</summary>
|
||||
public interface IPluginSettingsPage
|
||||
{
|
||||
string Title { get; }
|
||||
|
||||
/// <summary>One or two sentences for the page header.</summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>Builds the page content. Called once, on the UI thread.</summary>
|
||||
FrameworkElement CreateView();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrites wallpaper requests before the engine applies them: resolve a URL, swap a file, pin a
|
||||
/// playlist entry, or veto a change by returning the previous request.
|
||||
/// </summary>
|
||||
public interface IWallpaperFilter
|
||||
{
|
||||
/// <summary>Higher runs first. 0 is a good default.</summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>Return the request to apply (return the argument unchanged to leave it alone).</summary>
|
||||
WallpaperRequest Apply(WallpaperRequest request, IHostActions host);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders wallpaper sources the built-in players cannot handle. The first renderer whose
|
||||
/// <see cref="CanRender"/> returns true wins (highest <see cref="Priority"/> first), so a plugin can
|
||||
/// also take over a source LiveWall would otherwise play itself.
|
||||
/// </summary>
|
||||
public interface IWallpaperRendererPlugin
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>Higher wins over other plugins (built-in renderers are not in this race).</summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>Decides whether this renderer handles the source string.</summary>
|
||||
bool CanRender(WallpaperRequest request);
|
||||
|
||||
IWallpaperRenderer Create();
|
||||
}
|
||||
|
||||
/// <summary>The renderer instance. LiveWall owns its lifetime.</summary>
|
||||
public interface IWallpaperRenderer : IDisposable
|
||||
{
|
||||
/// <summary>The visual shown as the wallpaper. Keep the background opaque.</summary>
|
||||
FrameworkElement View { get; }
|
||||
|
||||
/// <summary>Loads the source. Throw to report a problem (it is shown to the user).</summary>
|
||||
void Load(WallpaperRequest request);
|
||||
|
||||
void Play();
|
||||
void Pause();
|
||||
void Unload();
|
||||
|
||||
/// <summary>Volume is 0–100, speed 0.25–4.</summary>
|
||||
void ApplyPlayback(bool muted, double volume, double speed);
|
||||
|
||||
/// <summary>Raised when playback fails (message is shown in the status bar and the log).</summary>
|
||||
event EventHandler<string>? Failed;
|
||||
|
||||
/// <summary>Raised when a non-looping source finished.</summary>
|
||||
event EventHandler? Ended;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AssemblyName>LiveWall.Sdk</AssemblyName>
|
||||
<RootNamespace>LiveWall.Sdk</RootNamespace>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);CA1416;CS1591</NoWarn>
|
||||
<Description>Contract assembly for LiveWall plugins.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user