A picom plugin that tells your animation scripts why a window moved, so each reason can have its own animation.
Window animations are the hard part of this project. Only the window manager knows why a window moved, so this plugin talks to i3 directly, and works only with i3. And keeping each window's picture right while it moves took deeper machinery in picom: knowing when a program has drawn, waiting for a window's first picture, renaming moves, shows and hides, and holding animations so the windows of one change stay on one clock. That machinery lives in the picom fork. Much of it exists for one window manager's sake, so whether picom wants it is an open question. See Under the hood.
picom sees a window move, but not why. Under i3 a window moves because another one opened or closed, because it went fullscreen or floating, or because you resized, swapped or moved it. win-cause follows the X events picom reads, asks i3 to say when it is done, and gives the move a name. Your script for that name decides how it looks. A window with no script for a reason gets its usual geometry animation.
moved-by-open, moved-by-close
Windows that moved because a new window appeared, or because one went away.
moved-by-fullscreen, moved-by-float
The window that went in or out of fullscreen; the window that became floating, and the windows that take its place.
covered-by-fullscreen, uncovered-by-fullscreen
The other windows on its workspace, which i3 hides while a window is fullscreen and shows again after. They don't go anywhere, so the styles keep them in place under the window that grows over them or turns back.
Any name you choose
For what i3 doesn't announce, a key binding names the reason: win-cause run flip -- i3-msg swap container with mark swap. Every window that command moves starts the trigger flip.
For a window that opens or closes, the plugin also tells the script where it splits from its neighbours, so it can grow out of that line and go back into it. picom itself stays neutral: it only sees trigger names. Neither i3 nor picom knows what a card flip is.
The first rule has the named reasons, and the opening and closing window, for every window: a window that turns floating is already floating when it moves. The second has the same motion for moves the plugin can't name (resize, swap, move, layout, floating off), but leaves floating windows out, so dragging one with the mouse isn't slowed down. Every other style below goes in the same two rules, with its own files in place of tile's.
Settings are in the style's parts/tile.conf. To change one, copy that file to ~/.config/picom/win-cause/parts/tile.conf: picom looks there first, and make install doesn't write there. Save picom.conf once afterwards, so picom reads the copy.
Styles
Every video plays the same session on a 16:9 monitor beside a vertical one: windows open one by one, the browser widens, swaps sides, goes fullscreen and back, a terminal floats and comes back, the layout turns, the browser moves to the vertical monitor, both monitors switch workspace and back, and everything closes again. Each style comes with a workspace switch from ws-direction in the same spirit.
Recorded at 60 fps on a real NVIDIA screen, each demo window in a colour of its own. Pauses between steps are trimmed to the same length; the animations themselves are unedited.
Tile
The style in daily use. Each window moves as a rectangle, all windows of a change on one curve and one clock.
tile
Each rectangle goes the straight way to its new place, easing in and out. A picture is never stretched. Workspaces: slide.
# the two rules of Setup
@include "win-cause/tile.conf"
@include "win-cause/tile-geometry.conf"
tile, curved
Windows that trade places pass each other instead of through each other: they curve around where their old and new places overlap, and lift off their way where they don't. Workspaces: cube with six sides, a copy of cube.conf with three settings changed.
# your copy of win-cause/parts/tile.conf
var-path = 1;
var-lift = 0.08;
# your copy of ws-direction/cube.conf, in each of its parts
var-sides = "max(6, plugin-ws-direction-faces)";
var-fit = 0.2;
var-length = 0.32;
Themed
One idea carried through every change: moving, opening, closing, fullscreen and the move to another monitor.
ink
While a window moves it becomes a block of its own median colour, which for most windows is their background's; its content comes back at the new place. A solid colour has nothing to stretch. Workspaces: ink.
# the two rules of Setup, with
@include "win-cause/ink.conf"
@include "win-cause/ink-geometry.conf"
origami
Windows turn like hinged panels: a window that changes size turns half a turn, a swap folds over the line between the two places, and a window unfolds when it opens and folds away when it closes. Workspaces: cube.
# the two rules of Setup, with
@include "win-cause/origami.conf"
@include "win-cause/origami-geometry.conf"
cube
Windows are the sides of boxes: they tip over their edges to move, turn into view and away, and a swap folds the pair round a corner. Workspaces: cube.
# the two rules of Setup, with
@include "win-cause/cube.conf"
@include "win-cause/cube-geometry.conf"
paper
Like paper on a desk: windows tilt into their motion, drop in swinging from their top edge, and tip over and fall away when they close. Workspaces: paper.
# the two rules of Setup, with
@include "win-cause/paper.conf"
@include "win-cause/paper-geometry.conf"
cards
A deck of cards: windows lift and slide, swap by turning over, are dealt in from a corner and slide away face down. Workspaces: push.
# the two rules of Setup, with
@include "win-cause/cards.conf"
@include "win-cause/cards-geometry.conf"
# and the swap rule and key of card flip
step back
The windows of a monitor shrink together, rearrange, and come back, like a camera pulling back for a moment. Workspaces: zoom-pan.
# the two rules of Setup, with
@include "win-cause/step-back.conf"
@include "win-cause/step-back-geometry.conf"
domino
Every change ripples through the windows from left to right, each starting a little after its neighbour. Workspaces: cascade.
# the two rules of Setup, with
@include "win-cause/domino.conf"
@include "win-cause/domino-geometry.conf"
Tile variants
Tile's moves, with a different open, close or swap.
card flip
Swapped windows turn over like cards and land on each other's side. Workspaces: flip.
# tile's two rules, and a rule of its own
animation-triggers = ["flip"];
rules = (..., {
match = "!override_redirect && window_type = 'normal'";
animations = ({ @include "win-cause/flip.conf" });
});
# i3 config: mark one window, then swap with it
bindsym $mod+m mark --toggle swap
bindsym $mod+s exec --no-startup-id win-cause run flip -- i3-msg swap container with mark swap
spring
Moves go a little past their place and settle back, on a damped spring. Opening and closing windows grow and shrink on the same spring. Workspaces: push.
# the two rules of Setup, with
@include "win-cause/spring.conf"
@include "win-cause/spring-geometry.conf"
pop
Windows grow out of their middle when they open and shrink into it when they close. Workspaces: pop.
# the first rule of Setup, with
@include "win-cause/pop.conf"
# the second keeps tile-geometry.conf
fold
Windows unfold from their split line when they open and fold away into it when they close. Workspaces: fold.
# the first rule of Setup, with
@include "win-cause/fold.conf"
# the second keeps tile-geometry.conf
warp
A moving window sinks into its old place and bursts out at its new one. Workspaces: warp.
# the two rules of Setup, with
@include "win-cause/warp.conf"
@include "win-cause/warp-geometry.conf"
twirl
Windows spin in when they open and spin out when they close. Workspaces: twirl.
# the first rule of Setup, with
@include "win-cause/twirl.conf"
# the second keeps tile-geometry.conf
How tile draws a window
Each window of a change has a rectangle that goes from where the window was to where it is. All of them move on one curve, at one time, and start and end without speed. An edge two windows share stays shared, so the gap a window leaves closes as it goes, and nothing overlaps.
i3's border or title bar, when the window has one, is drawn on the rectangle's edges in every frame, never stretched across it.
The content is never scaled or blended. It is drawn at its own size and cut where it is larger than the rectangle. The part it doesn't cover is filled with the median colour of the content, which for most windows is exactly their background's, so a terminal's rectangle is dark and a white page's is white.
Under the rectangle sits a backdrop in that colour, so while it moves, a program that hasn't drawn its new size yet doesn't show what lies behind it.
A window that opens waits until its program has drawn a first picture with content, and the windows that make room for it wait with it. Then they all start together, so no part of the animation runs while there is nothing to show.
A window that opens grows out of the line it split from, while its neighbours make room on the same curve. A window that closes goes back into its line, and its place is cut where the windows that take it reach, so no part of it shows what lies behind.
A change that comes while one runs goes on from where every rectangle is, with the speed it has. No window jumps in front of another while they pass.
Main settings
var-length (0.25)
How long a change takes, in seconds.
var-ease
The curve. One that starts and ends without speed keeps the motion smooth where changes meet.
var-path (0) and var-lift (0.08)
The straight or the curved way, and how far a window lifts off its way in a swap.
var-interrupt (1)
A change during a change: 1 keeps each rectangle's speed, 0 starts the curve again from where it is.
var-picture (var-picture-best)
Which picture a moving window shows: 1 the one it had when the change started, 0 its newest. By default whichever covers more of the rectangle, so a window that grows shows its new picture as soon as it is drawn, and one that shrinks keeps its old picture, cut by the rectangle, until it lands.
var-fill (1) and var-backdrop (1)
How much of the fill and of the backdrop is drawn: 0 lets what lies behind show.
Needs i3: the plugin asks i3 to say when a change is done, which other window managers don't answer.
i3 gives each window its new size at the start of a change, so a program lays out its content for the end size at once. The style cuts or fills the picture to fit the rectangle. Resizing step by step during the animation was tried and dropped; see Under the hood.
Resize, swap, move, layout changes and floating off show nothing beforehand, so they keep geometry, unless win-cause run names them.
An opening starts when the program has drawn, so a slow program opens late: on the recording's NVIDIA screen a terminal took 0.4 to 0.8 s from launch to its first picture, Chrome 1.2 to 2 s.
The first change after picom reloads its config gets no name.