win-cause

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.

It needs i3 and the picom fork; see Install. The source is on GitHub. The code was written with AI; see a note before you read on.

Why a window moved

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.

Setup

The tile styles go in two rules in picom.conf:

plugins = ["win-cause.so"];
rules = ({
  match = "!override_redirect && window_type = 'normal'";
  @include "win-cause/tile.conf"
}, {
  match = "!override_redirect && window_type = 'normal' && !I3_FLOATING_WINDOW@";
  @include "win-cause/tile-geometry.conf"
});

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.

Themed

One idea carried through every change: moving, opening, closing, fullscreen and the move to another monitor.

Tile variants

Tile's moves, with a different open, close or swap.

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.

The full list is in the README.

Limits

  • 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.