238 lines
11 KiB
Markdown
238 lines
11 KiB
Markdown
# Window Animations Plan
|
|
|
|
This document is the working plan for Wry's window animation system. It records
|
|
the decisions already made, the implementation phases, and the risks that must
|
|
be handled deliberately.
|
|
|
|
## Accepted Decisions
|
|
|
|
- The first landed slice is linear interpolation only, disabled by default.
|
|
- Animation is presentation-only. Logical layout, input hit testing, focus, and
|
|
Wayland configure state use final geometry immediately.
|
|
- Pointer drag and resize initiated by the mouse or tablet do not animate.
|
|
- Linear animations restart only for windows whose destination changes. Other
|
|
in-flight windows keep their existing timelines.
|
|
- Spawn-in uses scale and position for newly mapped tiled and floating app
|
|
windows. Layer-shell, overlay, override-redirect, and fullscreen surfaces do
|
|
not use this path. Spawn-out uses retained visual content after the live node
|
|
is gone, when a stable retained surface tree can be captured before unmap or
|
|
destroy.
|
|
- Command-driven tile-to-float and float-to-tile transitions may animate.
|
|
Protocol drag/drop paths do not.
|
|
- The no-overlap multiphase system is a separate phase after the linear path is
|
|
working and testable.
|
|
- Content freezing will use retained per-surface texture references, not a full
|
|
offscreen snapshot as the default design.
|
|
- Retained records should keep using the existing renderer behavior for now,
|
|
including clipping and edge stretch/clamp behavior for undersized contents. A
|
|
dedicated retained-tree scaling path is deferred to a later polish phase.
|
|
- The multiphase animation concept is original to Wry. Hy3 is relevant only as
|
|
partial inspiration for tiling style and titlebar/grouping behavior.
|
|
- Mono mode should mostly avoid animations. Exceptions are windows entering or
|
|
exiting mono mode, where a visual transition can clarify the hierarchy change.
|
|
|
|
## Texture Freezing Decision
|
|
|
|
The approved freezing design is to capture a renderable surface tree at animation
|
|
start:
|
|
|
|
- texture references
|
|
- source sample rects
|
|
- target sizes
|
|
- alpha and color metadata
|
|
- subsurface offsets and stacking order
|
|
- enough synchronization/release state to keep referenced buffers alive safely
|
|
|
|
This is lighter than rendering every toplevel into a compositor-owned offscreen
|
|
texture, and it should handle normal GPU-backed windows without an extra full
|
|
window copy. It also gives spawn-out a path: capture the surface tree before the
|
|
toplevel is logically destroyed, then animate the retained records after the live
|
|
node is gone.
|
|
|
|
Tradeoffs:
|
|
|
|
- Retained references can delay buffer release. For dmabuf clients this can
|
|
increase memory pressure or throttle clients if many large windows animate.
|
|
- SHM buffers still matter for simple clients, fallback paths, some utilities,
|
|
and cursor-like surfaces. They are probably not the common case for large app
|
|
windows, but the implementation must still treat SHM texture flipping as a
|
|
correctness issue.
|
|
- The release/sync contract must be explicit. A retained texture must not be
|
|
released back to the client while the compositor may still render it.
|
|
- True offscreen snapshots remain a possible fallback for cases where retained
|
|
references cannot safely preserve the rendered content.
|
|
|
|
## Phase 1: Linear Presentation Animations
|
|
|
|
Goal: add the smallest correct animation layer without changing layout semantics.
|
|
|
|
Implementation shape:
|
|
|
|
- Add animation state owned by `State`.
|
|
- Track per-toplevel animation entries keyed by `NodeId`.
|
|
- Store logical target rect, current presentation rect, previous damaged rect,
|
|
start time, duration, and curve.
|
|
- On command-driven tiled layout geometry changes, animate from current
|
|
presentation rect to new final rect.
|
|
- On interruption, restart only the affected window from its current
|
|
presentation rect.
|
|
- Drive frames from the existing output latch/presentation event flow.
|
|
- Damage the union of previous presentation rect, current presentation rect, and
|
|
final logical rect.
|
|
|
|
Initial scope:
|
|
|
|
- Tiled reflow animation.
|
|
- Floating command-driven moves and resizes are animated. Pointer and tablet
|
|
drag/resize paths still snap directly to the live cursor position.
|
|
- Cross-output and cross-scale movements snap for now.
|
|
- Linear mode may overlap windows during swaps. That is expected for the classic
|
|
interpolation mode; no-overlap is Phase 3.
|
|
- Live client buffers are rendered in Phase 1. Retained content freezing is
|
|
deferred, but animated windows must still be clipped to their presentation
|
|
bounds and must preserve the existing stretch behavior for undersized contents.
|
|
- Spawn-out is retained-content-only. If the surface cannot be retained safely
|
|
the window snaps out instead of animating an empty frame.
|
|
- No multiphase no-overlap planner.
|
|
|
|
Tests:
|
|
|
|
- rect interpolation is direction-independent
|
|
- interruption restarts only changed windows
|
|
- unchanged in-flight windows keep their original timeline
|
|
- drag-driven floating movement bypasses animation
|
|
- damage includes old, current, and final rects
|
|
- command-driven tile-to-float and float-to-tile transitions use linear motion
|
|
- command-driven floating moves and resizes animate without affecting pointer
|
|
drag/resize behavior
|
|
- pointer/header double-click unfloat bypasses the command-animation gate
|
|
|
|
## Phase 2: Retained Texture Freezing
|
|
|
|
Goal: freeze visual contents during movement and enable spawn-out.
|
|
|
|
Initial retained-record implementation status:
|
|
|
|
- Tiled animation can retain GPU/dmabuf-backed XDG and Xwayland surface trees.
|
|
- Spawn-in animation can retain GPU/dmabuf-backed XDG and Xwayland surface trees
|
|
for both tiled windows and floating child contents.
|
|
- Tile-to-float and float-to-tile transitions retain GPU/dmabuf-backed child
|
|
contents while the presentation geometry changes.
|
|
- Spawn-out captures retained app-window contents before XDG/Xwayland unmap or
|
|
destroy, then renders a detached shrinking presentation record until the
|
|
animation completes.
|
|
- Retained records hold both `GfxTexture` and `SurfaceBuffer` references so the
|
|
existing buffer release/sync path remains authoritative.
|
|
- Single-pixel buffers can be retained as color records.
|
|
- Retained records render through the same texture and stretch/clamp paths used
|
|
by live surfaces. This is the expected Phase 2 behavior.
|
|
- Async SHM textures are not retained yet because Wry's per-surface SHM
|
|
front/back textures can be reused by later commits while an animation is still
|
|
running. Those surfaces fall back to live rendering until an explicit offscreen
|
|
copy fallback exists.
|
|
|
|
Implementation shape:
|
|
|
|
- Add a retained render-record tree for toplevel surfaces.
|
|
- Capture records before movement animations that require freezing.
|
|
- Capture records before destroy/unmap for spawn-out.
|
|
- Render retained records through the normal renderer primitives where possible.
|
|
- Extend event/sync handling so retained buffers remain valid until the animation
|
|
is complete.
|
|
|
|
Deferred/future polish:
|
|
|
|
- Whether retained records participate in frame callbacks or presentation
|
|
feedback. Default assumption: no, because they are compositor animation frames,
|
|
not client commits.
|
|
- How to fall back when a buffer cannot be safely retained.
|
|
- A distinct retained-tree scaling render path for true spawn-in/spawn-out
|
|
content scaling. If added, start with retained GPU-backed records only, keep
|
|
the animated frame as the clip boundary, and avoid live SHM scaling until there
|
|
is an explicit snapshot/copy fallback.
|
|
|
|
## Phase 3: Multiphase No-Overlap Animations
|
|
|
|
Goal: implement Wry's staged no-overlap planner while preserving the rule that
|
|
windows never overlap.
|
|
|
|
Core rules:
|
|
|
|
- Each phase is a discrete animation using the full curve.
|
|
- A phase performs only one action kind per window: move or scale.
|
|
- Movement and scaling are split by axis.
|
|
- No diagonal motion.
|
|
- A window or synchronized group owns its own timeline.
|
|
- New layout changes interrupt only windows/groups with changed destinations.
|
|
- Current hierarchy and target hierarchy both matter. The planner must know
|
|
whether a window is ascending toward a higher-level/toplevel position,
|
|
descending into a container, or moving between containers at the same depth.
|
|
- If some child windows require fewer phases than their parent/container
|
|
context, parent/container-space changes generally happen first so space exists
|
|
before the child moves into it. This rule can be overridden only when the
|
|
non-overlap invariant still clearly holds.
|
|
- Windows that become peers in the target hierarchy may synchronize later
|
|
phases even if they were not peers in the source hierarchy.
|
|
|
|
Important parent/child synchronization issue:
|
|
|
|
The planner must not let a parent container and child window animate independent
|
|
axes at the same time in a way that violates the visual rules. For example, a
|
|
parent scaling horizontally while a child scales vertically can accidentally
|
|
produce a diagonal or multi-axis motion in screen space.
|
|
|
|
Preferred approach:
|
|
|
|
- Plan in terms of leaf toplevel visual rectangles first.
|
|
- Treat containers as constraints and grouping boundaries, not as independently
|
|
animated visual actors.
|
|
- Derive every leaf's per-phase rect from one phase schedule so parent and child
|
|
effects cannot compose into forbidden motion.
|
|
- Add container-level grouping only after the leaf planner proves correct.
|
|
- Include hierarchy-transition metadata in the planner input: source parent,
|
|
target parent, source depth, target depth, and whether the window is ascending,
|
|
descending, or staying at the same hierarchy level.
|
|
- For mono containers, suppress ordinary in-mono focus/tab changes. Animate only
|
|
transitions into mono, out of mono, or across the mono boundary.
|
|
|
|
Tests:
|
|
|
|
- horizontal swaps shrink, move, then grow without overlap
|
|
- extraction from a stack creates space before moving the extracted window
|
|
- nested containers do not produce simultaneous cross-axis motion
|
|
- interruption restarts only affected phase groups
|
|
- reversing direction produces equivalent motion in reverse
|
|
- child waits for parent/container-space phases when moving upward into a
|
|
toplevel peer position
|
|
- mono-mode tab switches do not animate, while entering/exiting mono can animate
|
|
|
|
## Configuration
|
|
|
|
Phase 1 should expose a disabled-by-default setting for:
|
|
|
|
- enabled/disabled
|
|
- duration
|
|
- curve preset or cubic bezier
|
|
|
|
Initial TOML shape:
|
|
|
|
```toml
|
|
[animations]
|
|
enabled = false
|
|
duration-ms = 160
|
|
curve = "ease-out"
|
|
# or:
|
|
curve = [0.25, 0.1, 0.25, 1.0]
|
|
```
|
|
|
|
Bezier curves are analyzed when configuration is applied and stored as a
|
|
piecewise curve that is cheap to evaluate during rendering. Custom curves use
|
|
CSS cubic-bezier semantics: `(0, 0)` and `(1, 1)` are implicit, while the four
|
|
configured numbers are `x1`, `y1`, `x2`, and `y2`. The x control points must be
|
|
between `0` and `1`.
|
|
|
|
## Existing Note
|
|
|
|
`docs/animation-integration.md` appears to document a prior animation attempt
|
|
whose `src/animation/` implementation is not present in this checkout. Treat
|
|
this plan as the current source of truth until implementation docs are updated.
|