A working window manager — written entirely in pixels.
Most shaders are pure math: the same picture every frame, with no memory of the last one. This one is different. It remembers where you left each window — which is the whole reason you can pick them up and drag them. Go ahead, grab a title bar in the panel on the left.
The trick is
There are two buffers, one per window — near-identical twins. Each one keeps its window’s
On the frame you press the mouse, each buffer measures the
999).+100 so the next stage knows to tug gently instead of locking on.Packing three states into one float channel is the trick — anything below 50 is a hard grab, 50–900 is a soft grab (minus the 100 tag), and 999 means inactive.
Buffer A and Buffer B run this negotiation with the roles swapped: Buffer A checks the other window first (so it yields in an overlap), Buffer B checks itself first (so it wins) — which is what makes B behave as the top window.
if (dOther <= 0.0) C.ba = vec2(999.0); // yield
else if (dMe <= 0.0) C.ba = myPos - cm; // hard grab
else C.ba = (myPos - cm) + 100.0; // soft grab (+100 tag)The window doesn’t snap to the cursor — it chases it. Each frame the stored position is blended a little way toward the target, with a blend weight that’s strongest right under the cursor and falls off with distance:
float w = iMouse.z > 0. ? max(0.01, exp(-35.*pow(d,1.9))) : 0.1;
if (isSoft) w *= 0.25 * exp(-3.0 * distToMouse); // soft grab: weaker, fades with range
C.rg = mix(C.rg, target, w);A hard grab (you clicked the window itself) locks on firmly — pixels under your cursor snap to the target, the rest follow a beat behind. A soft grab (you clicked empty space) only tugs, and the tug weakens the further the window sits from your cursor, so a distant window drifts over lazily while a near one comes quickly to hand. It’s all just a mix() per pixel.
The final pass is wonderfully dumb. It knows nothing about the mouse, focus, or dragging. It just reads the two positions out of the buffers and stamps a window at each — back window first, then the front one over it.
One
Each part is a soft smoothstep over a distance, so everything stays crisp at any resolution.
common.glsl. It returns the signed distance to a rounded box: negative inside, zero on the edge, positive outside. Both the buffers (for hit-testing) and the Image pass (for drawing) lean on it.
vec2 q = abs(p) - s + r;
return min(max(q.x,q.y),0.) + length(max(q,0.)) - r;