Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
PhosphorAnimationLayer::SurfaceAnimator Class Reference

Concrete PhosphorLayer::ISurfaceAnimator driving show/hide via phosphor-animation Profiles. More...

#include <phosphor-animation/include/PhosphorAnimation/SurfaceAnimator.h>

Inheritance diagram for PhosphorAnimationLayer::SurfaceAnimator:
[legend]

Classes

struct  Config
 Per-role profile + scale tuning bundle. More...
 

Public Member Functions

 SurfaceAnimator (PhosphorAnimation::PhosphorProfileRegistry &registry, Config defaults)
 Construct against an explicit registry with caller-supplied defaults.
 
 SurfaceAnimator (PhosphorAnimation::PhosphorProfileRegistry &registry)
 Convenience: registry-only ctor with empty default config.
 
 SurfaceAnimator (PhosphorAnimation::PhosphorProfileRegistry &registry, PhosphorAnimationShaders::AnimationShaderRegistry *shaderRegistry, Config defaults)
 Full constructor with shader registry for Phase 6 transition effects.
 
 ~SurfaceAnimator () override
 
 SurfaceAnimator (const SurfaceAnimator &)=delete
 
SurfaceAnimatoroperator= (const SurfaceAnimator &)=delete
 
void registerConfigForRole (const PhosphorLayer::Role &role, Config cfg)
 Override the configuration for one Role.
 
Config configForRole (const PhosphorLayer::Role &role) const
 Read-only config lookup.
 
void setDefaultConfig (Config cfg)
 Mutable access to the default (unregistered-role fallback).
 
Config defaultConfig () const
 
void setAnimationShaderRegistry (PhosphorAnimationShaders::AnimationShaderRegistry *registry)
 Install the animation shader registry for Phase 6 shader transitions.
 
void beginShow (PhosphorLayer::Surface *surface, QQuickItem *rootItem, const PhosphorLayer::Role &configRole, CompletionCallback onComplete)
 Role-override show/hide.
 
void beginHide (PhosphorLayer::Surface *surface, QQuickItem *rootItem, const PhosphorLayer::Role &configRole, CompletionCallback onComplete)
 
void setEnabled (bool enabled)
 Global animation enable/disable.
 
bool isEnabled () const
 
Dynamic shader uniforms

Cross-runtime feature parity with overlay shaders for the per-frame uniforms that don't come from the per-effect static metadata.

Most are auto-driven by the animator and need no consumer wiring:

iTimeDelta — real-time seconds between SurfaceAnimator driver ticks (matches overlay's wall-clock convention). • iFrame — per-leg frame counter, resets to 0 on each fresh attach; post-incremented per push so the first frame reports 0. • iMouse — driven by a QQuickHoverHandler installed on each attached shader item, mirroring the overlay path's MouseArea { hoverEnabled } / HoverHandler pattern. The handler reports cursor position in shader-item-local pixels (matches iResolution); when the cursor leaves the shader's bounding box, iMouse is set to (-1, -1) — same off-region sentinel the overlay path uses. • iDate — auto-populated by ShaderNodeRhi's scene-data sync, throttled to 1 Hz refresh. • iTimeHi — auto-computed wrap counterpart of iTime by ShaderNodeRhi's std140 split. Always 0 on this path since iTime stays in [0,1] and never wraps. Animation shader authors should NOT read iTimeHi; the field exists only to keep the std140 layout aligned with the overlay UBO so a single effect.frag source compiles against either runtime.

The single uniform that NEEDS consumer wiring is setAudioSpectrum below — there is no centralised QML producer for CAVA data the way there is for hover events.

void setAudioSpectrum (const QVector< float > &spectrum)
 Push the latest CAVA / audio-spectrum sample to every active animation shader item.
 
ISurfaceAnimator
void beginShow (PhosphorLayer::Surface *surface, QQuickItem *rootItem, CompletionCallback onComplete) override
 Begin a show transition for surface.
 
void beginHide (PhosphorLayer::Surface *surface, QQuickItem *rootItem, CompletionCallback onComplete) override
 Begin a hide transition for surface.
 
void cancel (PhosphorLayer::Surface *surface) override
 Interrupt any in-flight animation for surface.
 
- Public Member Functions inherited from PhosphorLayer::ISurfaceAnimator
 ISurfaceAnimator ()=default
 
virtual ~ISurfaceAnimator ()=default
 

Additional Inherited Members

- Public Types inherited from PhosphorLayer::ISurfaceAnimator
using CompletionCallback = std::function< void()>
 Invoked when a show/hide animation completes.
 

Detailed Description

Concrete PhosphorLayer::ISurfaceAnimator driving show/hide via phosphor-animation Profiles.

Phase 5 of the phosphor-animation roadmap. Replaces every overlay's hand-rolled QML ParallelAnimation { id: showAnimation } block with a single library-driven runtime that resolves curve + duration from PhosphorProfileRegistry. Live profile reloads (drop a JSON, see it apply on next show) flow through the registry's existing watcher path — the animator re-resolves the path on every beginShow / beginHide and captures the resolved Profile by value into the underlying MotionSpec. In-flight animations therefore keep the Profile they started with through to completion; reloads apply to the next dispatch, not to a currently-running tick. (Verified by the profile_reload_during_flight_does_not_affect_inflight regression test in pal_test_surface_animator.)

Per-role configuration

The animator carries a default Config plus a per-Role override map. Consumers register configs at construction:

SurfaceAnimator anim(registry);
anim.registerConfigForRole(PzRoles::LayoutOsd,
{.showProfile = QStringLiteral("osd.show"),
.showScaleProfile = QStringLiteral("osd.pop"),
.showScaleFrom = 0.8,
.hideProfile = QStringLiteral("osd.hide"),
.hideScaleTo = 0.9});
Concrete PhosphorLayer::ISurfaceAnimator driving show/hide via phosphor-animation Profiles.
Definition SurfaceAnimator.h:121

Each surface's role is read from surface->config().role at dispatch time. Surfaces whose role has no registered override use defaultConfig.

Animations

beginShow / beginHide drive the QQuickItem's opacity property (always, 0↔1) and optionally its scale property (when a scale-profile is configured). Both run in parallel against a shared steady-clock so frame timing aligns. Each property animation is a PhosphorAnimation::AnimatedValue<qreal> ticked at ~60 Hz by an internal QTimer (decoupled from QQuickWindow::beforeRendering so offscreen QPA tests work).

Cancellation & supersession

cancel(surface) stops in-flight animations for that surface only. The Phase-5.1 dispatch in Surface calls cancel on every Hidden→Shown / Shown→Hidden transition that supersedes a still- running animation, so the SurfaceAnimator does not have to detect overlap itself — the phosphor-layer side guarantees a clean cancel point before each beginShow / beginHide.

Re-entrancy contract

The animator's internal driver invokes a track's onComplete synchronously from inside its own tick loop. Consumers MUST NOT delete the SurfaceAnimator (or anything that owns it) from inside an onComplete callback — the tick loop continues iterating over the track map after invocation, and a destroyed animator strands the loop on freed memory. Deleting the Surface whose animation just completed is safe; the library cancels the animator's tracking entry on Surface destruction. If a callback genuinely needs to tear down the animator, defer the deletion via QTimer::singleShot(0, ...) or a QMetaObject::invokeMethod(..., Qt::QueuedConnection).

Lifetime

Heap-allocated by the consumer (typically the daemon's OverlayService) and passed to SurfaceFactory::Deps::animator. Must outlive every Surface created from that factory; the destructor cancels any still-tracked surface state but does NOT cascade-delete the surfaces themselves.

Thread safety

GUI-thread only. Every method touches QQuickItem / QHash state without synchronisation; the underlying AnimatedValue<T> runtime asserts the same. Construction and registerConfigForRole are typically called once during process / OverlayService startup but must still run on the GUI thread to satisfy the QHash invariant.

Constructor & Destructor Documentation

◆ SurfaceAnimator() [1/4]

PhosphorAnimationLayer::SurfaceAnimator::SurfaceAnimator ( PhosphorAnimation::PhosphorProfileRegistry registry,
Config  defaults 
)
explicit

Construct against an explicit registry with caller-supplied defaults.

The animator takes the registry by reference; the registry must outlive the animator. Composition roots (daemon / editor / settings) own one PhosphorProfileRegistry instance and thread it through; tests construct a per-fixture registry and pass it here. There is no default-singleton fallback — every caller injects their own dependency.

◆ SurfaceAnimator() [2/4]

PhosphorAnimationLayer::SurfaceAnimator::SurfaceAnimator ( PhosphorAnimation::PhosphorProfileRegistry registry)
explicit

Convenience: registry-only ctor with empty default config.

Equivalent to SurfaceAnimator(registry, Config{}) but spelled out as a separate overload so unity-build paths don't get confused by a default-arg Config{} on the primary ctor (the previous shape Config defaults = Config{} triggered a parse ambiguity in the unity TU between the value-initialised default arg and a function-declarator interpretation).

◆ SurfaceAnimator() [3/4]

PhosphorAnimationLayer::SurfaceAnimator::SurfaceAnimator ( PhosphorAnimation::PhosphorProfileRegistry registry,
PhosphorAnimationShaders::AnimationShaderRegistry shaderRegistry,
Config  defaults 
)

Full constructor with shader registry for Phase 6 transition effects.

Same DI contract as the registry-only ctors — caller owns the AnimationShaderRegistry and threads it through; pass nullptr to disable shader transitions. (No singleton fallback; see SurfaceAnimator(registry, defaults) above for the rationale.)

◆ ~SurfaceAnimator()

PhosphorAnimationLayer::SurfaceAnimator::~SurfaceAnimator ( )
override

◆ SurfaceAnimator() [4/4]

PhosphorAnimationLayer::SurfaceAnimator::SurfaceAnimator ( const SurfaceAnimator )
delete

Member Function Documentation

◆ beginHide() [1/2]

void PhosphorAnimationLayer::SurfaceAnimator::beginHide ( PhosphorLayer::Surface surface,
QQuickItem *  rootItem,
CompletionCallback  onComplete 
)
overridevirtual

Begin a hide transition for surface.

The Surface is transitioning Shown → Hidden. The animator should fade/slide the content, then invoke onComplete. The library does not delay window->hide() waiting for this — the current contract is "animate for visual polish, but the surface is considered hidden the moment hide() is called".

Implements PhosphorLayer::ISurfaceAnimator.

◆ beginHide() [2/2]

void PhosphorAnimationLayer::SurfaceAnimator::beginHide ( PhosphorLayer::Surface surface,
QQuickItem *  rootItem,
const PhosphorLayer::Role configRole,
CompletionCallback  onComplete 
)

◆ beginShow() [1/2]

void PhosphorAnimationLayer::SurfaceAnimator::beginShow ( PhosphorLayer::Surface surface,
QQuickItem *  rootItem,
CompletionCallback  onComplete 
)
overridevirtual

Begin a show transition for surface.

The Surface has just become visible (Hidden → Shown). The animator can mutate rootItem (opacity, scale, y-offset) and drive an animation. Invoke onComplete when the transition finishes; the library uses this as a no-op today but a future version may defer compositor sync until the completion signal arrives.

Implements PhosphorLayer::ISurfaceAnimator.

◆ beginShow() [2/2]

void PhosphorAnimationLayer::SurfaceAnimator::beginShow ( PhosphorLayer::Surface surface,
QQuickItem *  rootItem,
const PhosphorLayer::Role configRole,
CompletionCallback  onComplete 
)

Role-override show/hide.

The surface's own role is used for layer-shell-protocol concerns (anchors, kbd, layer); the configRole argument is used for animation-config resolution — replacing the implicit configFor(surface->config().role) lookup the no-arg variants do. Required by the unified PassiveOverlayShell pattern: one shell wl_surface hosts multiple per-content slots (OSD, zone-selector, snap-assist, picker), each animated with their own per-content motion + shader profiles. Without role override they'd all share the shell's role-config, which would homogenise transitions across content types.

Pre-shell, every content had its own Surface and the no-arg variant resolved the right config from the surface's role directly. Post-shell, the surface's role is the shell's (PassiveShell) and we need to override per-content per-show.

◆ cancel()

void PhosphorAnimationLayer::SurfaceAnimator::cancel ( PhosphorLayer::Surface surface)
overridevirtual

Interrupt any in-flight animation for surface.

Called when the Surface is destroyed or the animation is superseded by an opposite transition (show-while-hiding).

Implements PhosphorLayer::ISurfaceAnimator.

◆ configForRole()

Config PhosphorAnimationLayer::SurfaceAnimator::configForRole ( const PhosphorLayer::Role role) const

Read-only config lookup.

Returns the registered config for role if one was set, otherwise defaultConfig.

◆ defaultConfig()

Config PhosphorAnimationLayer::SurfaceAnimator::defaultConfig ( ) const

◆ isEnabled()

bool PhosphorAnimationLayer::SurfaceAnimator::isEnabled ( ) const

◆ operator=()

SurfaceAnimator & PhosphorAnimationLayer::SurfaceAnimator::operator= ( const SurfaceAnimator )
delete

◆ registerConfigForRole()

void PhosphorAnimationLayer::SurfaceAnimator::registerConfigForRole ( const PhosphorLayer::Role role,
Config  cfg 
)

Override the configuration for one Role.

The role's scopePrefix is used as the registration key. Lookup is longest-prefix-match (with ‘’-'boundary) against the surface'srole.scopePrefix, so consumers can register a config against a stable base role (e.g.PzRoles::LayoutOsdwith prefix"plasmazones-layout-osd") while the surfaces themselves carry per-instance roles derived via withScopePrefix("plasmazones-layout-osd-{screenId}-{gen}")` for compositor scope uniqueness. Without prefix matching, the unique per-instance suffix would make every lookup miss and silently fall back to the default config.

◆ setAnimationShaderRegistry()

void PhosphorAnimationLayer::SurfaceAnimator::setAnimationShaderRegistry ( PhosphorAnimationShaders::AnimationShaderRegistry registry)

Install the animation shader registry for Phase 6 shader transitions.

May be called after construction (the daemon creates the registry after the overlay service). Null disables shader transitions.

◆ setAudioSpectrum()

void PhosphorAnimationLayer::SurfaceAnimator::setAudioSpectrum ( const QVector< float > &  spectrum)

Push the latest CAVA / audio-spectrum sample to every active animation shader item.

Mirrors the overlay path's OverlayService::onAudioSpectrumUpdatedaudioSpectrum QML property write — animation shaders programmatically attached by SurfaceAnimator have no QML scene to bind through, so the consumer (typically the daemon's OverlayService) wires its IAudioSpectrumProvider::spectrumUpdated signal here. The spectrum is also re-pushed at attach time so a shader that installs mid-stream sees the latest data on its first frame instead of zero-initialised silence.

Pass an empty vector to clear (e.g. when audio visualization is disabled). Called at the audio framerate (typically 30-60 Hz); ShaderEffect::setAudioSpectrum no-ops on identity so an unchanged spectrum costs nothing.

◆ setDefaultConfig()

void PhosphorAnimationLayer::SurfaceAnimator::setDefaultConfig ( Config  cfg)

Mutable access to the default (unregistered-role fallback).

Useful when the consumer wants to bind every overlay's default behaviour without enumerating roles.

◆ setEnabled()

void PhosphorAnimationLayer::SurfaceAnimator::setEnabled ( bool  enabled)

Global animation enable/disable.

When disabled, beginShow and beginHide snap the rootItem to the target opacity (1.0 / 0.0) and fire the completion callback synchronously — no opacity tween, no shader leg, no scale leg. Mirrors the kwin-effect's global gate: a single animationsEnabled setting kills every animation on both runtimes when toggled off.


The documentation for this class was generated from the following file: