# Window Animations Manual Test Plan This is the manual verification checklist for Wry's animation work. Use it after building a test compositor and booting into a normal graphical session. The goal is to catch visual, synchronization, damage, and retained-content problems that unit tests cannot prove from geometry alone. ## Setup - Build and install the `codex-anims-next` branch. - Start with animations enabled and a deliberately slow duration, around `400-700ms`, so phase ordering and damage artifacts are visible. - Test at least one normal Wayland/XDG app, one Xwayland app if available, and one fast-updating app such as a terminal running output, a browser animation, a video, or a GL/Vulkan demo. - Use visible gaps, borders, titlebars, and rounding. These make clipping and damage mistakes much easier to see. - If available, test on both a single-output setup and a multi-output setup. - If logging is convenient, run with debug logging and keep any multiphase fallback messages. A fallback is useful evidence, not automatically a bug. Relevant internal config hooks: - `SetAnimationsEnabled` - `SetAnimationDurationMs` - `SetAnimationCurve` - `SetAnimationCubicBezier` Current curve IDs in code: - `0`: linear - `1`: CSS `ease` - `2`: CSS `ease-in` - `3` or any other unrecognized value: CSS `ease-out` - `4`: CSS `ease-in-out` ## Pass Criteria A test passes when: - layout, focus, hit testing, and configure behavior use the final logical geometry immediately - visible presentation motion is smooth and bounded by the animated frame - no old pixels, trails, black strips, transparent holes, or stale titlebar fragments remain after motion - tiled multiphase movement never overlaps and never moves a single window diagonally during a phase - interruption starts changed windows from their current visual rect without restarting unaffected windows - drag-driven pointer movement remains direct and does not lag behind the cursor - cross-output or cross-scale movements snap instead of animating Record a failure with: - the layout before and after - whether the window was tiled, floating, mono, XDG, Xwayland, or layer-shell - whether the app was GPU/dmabuf-backed or likely SHM, if known - animation duration and curve - whether the failure was visual overlap, diagonal motion, debris, clipping, stale content, a missing retained frame, or an incorrect animation trigger ## Known Current Limits These are acceptable unless they produce worse behavior than described: - Spawn-out is retained-content-only. If the surface cannot be retained safely, it should snap out rather than animate an empty frame. - Async SHM surfaces are not retained yet. GPU/dmabuf-backed app windows are the primary retained-content path for this phase. - A dedicated retained-tree scaling/offscreen fallback is deferred. Retained records currently render through the normal texture and stretch/clamp paths. - Floats may overlap. The no-overlap invariant is for tiled multiphase motion. - Linear fallback may overlap. This should be rare for valid tiled layouts, and the fallback should be scoped to the affected motion group. - Cross-output and cross-scale movements should not animate yet. ## 1. Basic Enable/Disable 1. Disable animations. 2. Move, resize, spawn, close, and toggle floating on a few windows. 3. Confirm all affected windows snap with no delayed presentation state. 4. Enable animations at a slow duration. 5. Repeat the same operations and confirm only eligible paths animate. 6. Disable animations while an animation is in flight. Expected: - disabling animations clears any in-flight visual state - no stale damage remains after disabling - newly enabled animations use the configured duration and curve ## 2. Spawn-In Test newly mapped windows: - tiled XDG window - floating XDG window - Xwayland window, if available - fullscreen window - layer-shell or overlay surface, such as a bar, launcher, menu, notification, or lock/overlay component, if available Expected: - newly mapped tiled and floating app windows animate in - layer-shell, overlay, override-redirect, and fullscreen surfaces do not use the app-window spawn-in path - contents stay clipped to the animated frame - if contents are smaller than the frame during the animation, no empty strips are visible ## 3. Spawn-Out Close windows from these states: - tiled app window - floating app window - Xwayland app window - fast-updating app window - a likely SHM/simple app, if available Expected: - retained app content shrinks out after the live node is gone - there is no black, transparent, or unfilled moving rectangle - if content cannot be retained, the window snaps out cleanly - neighboring tiled windows reflow without debris left in the old area Hard failure: - a destroyed window leaves a moving empty frame - the last frame shows unrelated newer content - screen debris remains after the animation completes ## 4. Linear Tiled Reflow Use a slow duration and a non-linear curve, then repeat with linear. Cases: - open two tiled windows and change split ratio by command - open three tiled windows and resize the active split repeatedly - move focus and issue command-driven swaps - interrupt a resize by issuing another resize before the first completes - create a layout that forces a linear fallback if possible Expected: - final layout is usable immediately - changed windows animate from their current visual rect on interruption - unaffected windows keep their existing timeline - linear fallback is visually smooth, even if overlap occurs - no pointer drag path becomes animated ## 5. Float Movement And Tile/Float Transitions Cases: - command-toggle a tiled window to floating - command-toggle the same window back to tiled - command-move and command-resize a floating window - mouse-drag a floating window - mouse-resize a floating window - double-click/header pointer path if that is part of the local workflow Expected: - command-driven tile-to-float and float-to-tile transitions animate linearly - command-driven floating move/resize animates - mouse or tablet drag/resize remains direct and tracks the pointer - pointer/header paths that are intentionally outside the command-animation gate do not unexpectedly use delayed animation - retained child content remains clipped during tile/float transitions ## 6. Multiphase Horizontal And Vertical Swaps Horizontal: 1. Create two horizontally adjacent tiled windows. 2. Swap their positions. 3. Reverse the swap. Vertical: 1. Create two vertically adjacent tiled windows. 2. Swap their positions. 3. Reverse the swap. Expected: - first phase shrinks into lanes on the orthogonal axis - second phase moves only horizontally or only vertically - third phase grows out of lanes - no phase overlaps windows - no window moves diagonally - reverse direction uses the same visual logic in reverse - titlebars, borders, gaps, and rounded corners remain respected ## 7. Stack Extraction And Return Build this shape: ```text [ A | [ B C ] ] ``` Then move `B` out so the target is: ```text [ A | B | C ] ``` Reverse the operation by putting `B` back into the stack. Expected: - peer/container space opens first - `B` waits until there is a legal horizontal or vertical lane - `B` moves on one axis only - `B` and the affected peer grow together in the final phase when appropriate - reversing the operation is visually equivalent in reverse ## 8. Nested Parent/Child Synchronization Create nested split layouts where a parent changes one axis and children change the other. Use both horizontal-parent/vertical-child and vertical-parent/horizontal-child variants. Expected: - parent/container-space axis changes happen before child-axis changes when the hierarchy metadata gives a deterministic order - child windows do not visually compose parent and child transforms into diagonal motion - any unsupported group falls back as a group rather than partially violating the one-axis rule Hard failure: - a child visibly changes width and height in the same phase - a child moves diagonally because parent and child animation compound - a child clips outside its animated frame ## 9. Mixed-Action Phases Look for layouts where one window can move on one axis while another window scales on a different axis in the same proven phase. Expected: - mixed phases are allowed only when each individual window performs one legal move or scale on one axis - no individual window moves diagonally - no overlap occurs at any point during the phase This is easier to confirm with debug fallback logs plus visual inspection. A fallback here is acceptable if the planner cannot prove the sequence. ## 10. Mono Mode Cases: - enter mono mode with several siblings - exit mono mode - switch active tabs/windows inside mono - move a window into mono - move a window out of mono Expected: - entering/exiting mono may animate where it clarifies hierarchy change - active child animates to the mono geometry - inactive siblings snap invisible - ordinary tab switches inside mono do not animate - no hidden inactive sibling leaves debris or stale retained content ## 11. Interruption And Retargeting Use a long duration, then issue commands mid-animation: - swap, then reverse before completion - resize, then resize in the other direction before completion - move a window out of a stack, then back before completion - start a multiphase group, then change only one window's destination if a command sequence allows it Expected: - affected windows restart from their current visual rect - unaffected windows do not restart if their destination is unchanged - a new valid multiphase plan replaces the old plan cleanly - retained content remains the same frozen content during the retarget - damage covers old, current, and new visual regions ## 12. Damage And Clipping Stress Use a high-contrast wallpaper/background and high contrast window contents. Cases: - fast repeated swaps - repeated spawn-in/spawn-out - rounded corners with large gaps - titlebar-heavy layouts - resize while a terminal is rapidly updating - move/resize over another window's old location - run on different output scales if available Expected: - no trails remain in gaps, borders, or titlebar strips - rounded corners do not reveal old pixels outside the frame - contents never draw outside the animated bounds - final frame exactly matches the steady layout ## 13. Texture Freezing Use fast-updating contents so freezing is obvious. Cases: - tiled GPU/dmabuf-backed app during reflow - floating GPU/dmabuf-backed app during command move/resize - tile-to-float and float-to-tile with dynamic content - spawn-out with dynamic content - likely SHM/simple app, if available Expected: - retained GPU/dmabuf-backed windows freeze visually during animation - spawn-out uses the last retained content, not a blank or unrelated frame - undersized contents stretch/clamp to avoid unfilled frame regions - SHM/unretained surfaces either render live safely or snap where retention is required Record separately: - content continues updating during movement - content freezes but samples the wrong source region - edges show empty/black strips while scaling - spawn-out skips because capture was unavailable ## 14. Cross-Output And Scale Boundaries Cases: - move a tiled window to another output - move a floating window to another output - move between outputs with different scale factors, if available - move a workspace between outputs, if supported locally Expected: - movement snaps instead of animating - no retained content is rendered at the wrong scale - no stale damage remains on the source output - destination output renders the final layout immediately ## 15. Regression Sweep After visual tests, return to normal animation duration and curve. Repeat: - ordinary tiling navigation - workspace switching - fullscreen enter/exit - focus changes - app launch/close loops - suspend/resume or VT switch if convenient Expected: - animation state does not survive across unrelated compositor state changes - no stuck retained frames - no persistent high CPU/GPU use after animations stop - no obvious client throttling after many retained-content animations ## Summary Result Template ```text Commit: Build: Outputs/scales: GPU/session: Animation config: Passed: - Known-limit observations: - Failures: - case: app: layout: expected: actual: reproducible: logs: ```