-
Notifications
You must be signed in to change notification settings - Fork 1
Changelog
Release notes for LoupixDeck.PluginSdk. The version surfaced at runtime is
SdkInfo.Version; the host enforces a major-version
match against the value declared in plugin.json (sdkVersion).
Animated touch buttons. A command can now drive a touch button from the host's
central animation scheduler at a target frame rate, instead of the fixed polling
interval used by IDisplayImageCommand. The
plugin draws each frame onto the same IRenderCanvas (90×90) and
tells the host whether anything changed, so unchanged ticks cost no device I/O.
New contracts (additive — existing plugins unaffected)
| Type | Purpose |
|---|---|
IAnimatedDisplayCommand |
Optional, independent command interface for a genuinely animated touch button. Exposes TargetFps and RenderAnimatedFrame(ctx, canvas, frame). A command may implement both this and IDisplayImageCommand — the host prefers the animated path. |
AnimationFrameContext |
Per-frame timing snapshot: FrameNumber, Elapsed, Delta, EffectiveFps. Drive the visual from Elapsed so playback stays correct even when the host clamps the rate. |
AnimationFrameInfo |
Return value of RenderAnimatedFrame. Factory helpers Skip() (nothing changed), Frame(n) (drew, continue) and Final(n) (last frame of a one-shot). The frame number is the host's dirty key for the device push. |
This is a purely additive release: the assembly's Major is unchanged and no
existing member changed, so every 1.x plugin keeps loading and rebuilding is
optional — only a command that wants animation needs the new interface. (The
release also fixes an MSB4044 pack error when the core app is published; that is
build-tooling only and has no effect on the plugin contract.)
Canvas-based rendering. Plugin render paths no longer return PNG bytes — the host
hands the plugin an IRenderCanvas it draws onto with the host's
own primitives, so plugin text and symbols match the core font and symbol library.
DrawImage(bytes) remains as the full-custom escape hatch. (Versions 1.9.0 and
1.10.0 were development steps folded into this release.)
New contracts
| Type | Purpose |
|---|---|
IRenderCanvas |
The drawing surface: Clear; rectangles, rounded rectangles, circles, ellipses, arcs, lines; DrawText (centered or TextHAlign/TextVAlign) + MeasureText; DrawSymbol (tint or SymbolStyle); DrawImage (aspect-fit, + opacity/tint overload with same-array decode caching); a PushTransform/PopTransform/Translate/Rotate/Scale stack. |
SymbolStyle |
Outline / drop-shadow / linear-gradient / rotation styling for the DrawSymbol overload. |
TextHAlign / TextVAlign
|
Independent horizontal/vertical text alignment for the box DrawText overload. |
Changed contracts (rebuild required)
| Member | Change |
|---|---|
IDisplayImageCommand |
byte[]? GetImage(ctx) + string? GetText(ctx) replaced by bool RenderImage(CommandContext ctx, IRenderCanvas canvas). The command's output is now a live, host-owned touch-button layer (movable/scalable in the editor, content read-only). |
ISideStripSession.RenderStrip |
byte[]? RenderStrip() → bool RenderStrip(IRenderCanvas canvas). |
ISegmentStripSession.RenderSegment |
byte[]? RenderSegment(int) → bool RenderSegment(int rotaryIndex, IRenderCanvas canvas). |
PluginColor gained Transparent. The assembly's
Major is unchanged, so non-rendering plugins keep loading; only a plugin that
implements one of the three changed render members must rebuild against 1.11.0.
Side strips. A plugin can render and own a Razer Stream Controller side display strip (or a single dial segment of one) while the rest of the device keeps working. See Side Strips. (Version 1.7.0 was skipped.)
New contracts
| Type | Purpose |
|---|---|
ISideStripProvider |
Discoverable, stateless renderer + session factory bound to a strip in plugin-override mode. Id (persisted, orphan-safe) + Title + CreateSession. |
ISideStripSession |
One live per-side attachment: RenderStrip, StripChanged, OnStripTapped, OnStripSwiped; IDisposable. |
ISegmentStripProvider / ISegmentStripSession
|
Render an individual dial's strip segment in the host's segmented mode (RenderSegment); the rest stay host-rendered. |
SideStripContext / SideStripRotary
|
Strip geometry (Side/Width/Height), the adjacent dials' labels + bound commands, and RequestNextPage/RequestPreviousPage callbacks. |
StripSide / StripSwipeDirection
|
Which strip a session drives (Left/Right); vertical swipe direction (Up/Down). |
LoupixPlugin additions
| Member | Purpose |
|---|---|
virtual IEnumerable<ISideStripProvider> GetSideStripProviders() |
Returns the plugin's side-strip providers; collected after Initialize alongside GetCommands. Defaults to none. |
Full-device takeover: a plugin can now seize the entire device for a HUD, overlay, screensaver, or video, and pick how the host pushes frames to it. (Version 1.5.0 was skipped — there is no 1.5.0 release.)
New contracts
| Type | Purpose |
|---|---|
IExclusiveModeProvider |
Plugin-supplied controller for the global exclusive mode. While active, the host suppresses page mappings, freezes folder navigation, and routes every hardware input to the provider. RenderMode and SingleTileSlot are default-implemented, so the contract is forward-compatible. |
ExclusiveRenderMode |
How the host pushes a provider's frames: FullScreen (default — one composited blit + DRAW), Grid, DirtyTiles (only changed tiles), SingleTile. The lever for higher frame rates. |
IPluginHost additions
| Member | Purpose |
|---|---|
bool RequestExclusiveMode(IExclusiveModeProvider provider) |
Hands the active device to provider. Returns false if another provider already owns it (no stealing); on success the host calls provider.OnEnter() before returning true. |
void ReleaseExclusiveMode(IExclusiveModeProvider provider) |
Releases exclusive mode. No-op unless provider is the current owner; on a match the host calls provider.OnExit() and restores the normal page. |
bool IsInExclusiveMode { get; } |
true while any provider currently owns the active device. |
Host surface for browser launches and transient touch feedback, plus two forward-compatible command contracts.
IPluginHost additions
| Member | Purpose |
|---|---|
bool OpenBrowser(string url) |
Opens url in the user's default browser. Host abstracts OS specifics (Windows shell-execute, Linux xdg-open) so OAuth flows don't need per-plugin platform branches. |
void OverlayTouchText(int slot, string text, TimeSpan duration) |
Temporarily paints text on the touch slot at slot and restores the slot's normal content after duration. Fire-and-forget; later calls on the same slot supersede pending restores. |
int GetTouchSlotForRotary(int rotaryIndex) |
Returns the touch slot adjacent to the rotary at rotaryIndex, or -1 when the active device has no such neighbour. Pair with OverlayTouchText for value-flash feedback without hard-coding device geometry. |
CommandContext additions
| Member | Purpose |
|---|---|
int? SourceIndex |
Identifier of the originating control when Target denotes an indexed source (rotary index for RotaryEncoder, touch slot for TouchButton, simple-button index for SimpleButton). null for chained or CLI invocations. |
New command contracts (forward-compatible)
| Type | Purpose |
|---|---|
IAdjustmentCommand |
Rotary-encoder value adjustment — turn invokes ApplyAdjustment, press invokes ApplyReset. Safe to implement now; the host wires it up when available. |
IDisplayImageCommand |
Renders a dynamic PNG on a touch button. Safe to implement now alongside IDisplayCommand for graceful fallback. |
Display-only setting headers and persisted-key enumeration.
PluginSettingKind additions
| Member | Purpose |
|---|---|
Heading |
Display-only section header in dynamically built settings pages. Carries no value; used to group related controls. |
IPluginSettings additions
| Member | Purpose |
|---|---|
IEnumerable<string> Keys { get; } |
Enumerates every key currently present, in undefined order. Use it to discover prefixed entries — e.g. Keys.Where(k => k.StartsWith("alias:")) — without tracking them in a separate index. |
Getting started
API reference
Advanced
Operations
Release notes