Nomos Engineering

Why a window manager should not remember the shape of your window

Every window manager I tried carried a window’s proportions across monitors. Dropping that idea turned out to remove most of the code, not add to it.

Terminal Terminal tall and narrow here… …wide and short here

A window manager has one job that everyone agrees on: put the window where I say. The disagreements start one level down, at what “where” means. Most tools treat a window as an object with a remembered size that travels with it. Nomos treats the destination as the object with the size, and the window as something that takes the shape of wherever it lands.

That sounds like a detail of implementation. It is not. It decides how much configuration the user has to hold in their head, and it decides how much of the code exists only to reconcile two sources of truth.

The shape that follows you around

Drag a terminal from a landscape monitor onto a portrait one and macOS keeps its proportions. You get a tall, narrow terminal on a screen where wide and short is the only sensible shape, and you resize it by hand at the other end. Do this a dozen times a day and you are not managing windows, you are re-managing them.

The usual fix is per-display memory: remember the shape the window had the last time it visited this screen. It works until the second window of the same application shows up, or until you rotate a monitor, or until an application resizes itself on launch and poisons the memory. Every one of those cases needs a rule, and the rules interact.

The bug reports were never “it put my window in the wrong place.” They were “it put my window in the right place at the wrong size,” which is much harder to reason about.

Geometry belongs to the zone

The alternative is to say a window has no size of its own while it is under management. A zone is a rectangle on a specific display, defined as a fraction of that display. Moving a window means picking a zone and asking the window to become that rectangle. There is nothing to remember and nothing to reconcile.

The consequences are pleasant:

  • A terminal moved onto the portrait monitor comes out wide and short, because that is what the zone there is.
  • To the window manager, two windows of the same application are interchangeable, because neither carries state.

The cost is that “restore my window to how it was” stops being a meaningful request. In exchange you get a different command — put this window back into the zone it is already in — which is what people actually wanted most of the time.

Zones cannot overlap by construction

A zone is a leaf of a tree of splits, and a split always accounts for the whole of what it divides. There is no syntax in which a gap or an overlap can be written down, so there is no validation pass and no error to report:

[[display]]
name = "XG32UCWG"
orientation = "landscape"

  [display.split]
  axis = "vertical"      # top-to-bottom cut, divides width
  at = 0.5

This is the part I would defend hardest. A configuration format that can express an invalid state will eventually be given one, and then the program needs an opinion about it. Making the state inexpressible is cheaper than handling it.

One ring, not one list per screen

With geometry settled, ordering becomes the interesting problem. The obvious approach is to derive an order from screen coordinates — left to right, top to bottom. It reads well in a design document and badly on a real desk, where a portrait monitor sits at an angle and a laptop screen is below and to the left of everything.

So the order is authored, not derived. Zones from every display are threaded onto a single ring, in an order you set by hand. One key walks the ring and wraps at the end. You never think about which monitor you are on, which is the whole point: the monitor is a hardware fact, and the ring is a description of how you work.

1 2 3 4 5 landscape · 2560 × 1440 portrait · 1440 × 2560 wraps back to 1
The ring threaded through a landscape and a portrait display. Walking it never asks which screen you are on.

What it cost to measure

To know whether the model held up, I had to measure rather than trust it:

  1. Ask for the frame, wait for the window to settle, read back what it actually became.
  2. Record the discrepancy per application, not per window.
  3. Treat anything above one point as a bug in my sequencing rather than in the application.

On a two-monitor desk the discrepancy came out at half a point or less. That number matters less than what producing it forced: a size-then-position-then-size sequence, borrowed from Rectangle, that reliably lands a window where it was asked to go.

Where this leaves the design

Dropping remembered geometry removed per-display state, the migration path when displays change, and the rules for which window inherits which shape. What replaced it is one sentence a user can hold: a window becomes the shape of the place you send it to.

Nomos is in early access. If you keep a portrait monitor on your desk and have been resizing the same terminal twice a day, write to me — that is exactly the desk I want it tested on.