Your desktop is the solar system.
A workspace model for driftwm —
the Sun at the origin, eight planets in the order you learned as a child,
and everything you already know about them doing the navigating for you.
Nineteen windows, six planets. Every district sits on a card in its planet's colour, so a busy world reads as busy from across the system.
driftwm gives you an infinite canvas: windows live anywhere on an endless plane and the screen is a camera looking at it. It's a lovely idea with one honest problem — an infinite plane is featureless. Pan away from your windows and nothing tells you where you are, which way is home, or where you left that terminal.
sol answers that by making the canvas a place you already know. Not a metaphor you have to learn —
the solar system, the one you can already recite.
The Sun sits at a focus of eight real ellipses, and the planets sit where they really were, one
per orbit, ordered outward: Mercury, Venus, Earth, Mars, the asteroid belt, Jupiter, Saturn, Uranus,
Neptune. Each one is placed by solving Kepler's equation for the date, so it rides its own orbit
line at the angle it actually occupied. mod+1 … mod+8 fly you to them in that same order.
mod+0 returns to the Sun.
And the layout means something:
Distance from the Sun is distance from your attention. Inner planets are immediate and ephemeral, the asteroid belt divides, outer planets are heavy and background.
| 1 Mercury — quick | 2 Venus — comms | 3 Earth — home |
| 4 Mars — ops | · · · asteroid belt · · · | 5 Jupiter — builds |
| 6 Saturn — media | 7 Uranus — spare | 8 Neptune — archive |
Those roles are only defaults; what matters is that "further out" always means "further from what I'm doing right now".
mod+0 always comes back here — the Sun, home base, with Mercury a hop away and Earth just below.
The Sun is the one place that holds nothing: it is the view with the whole inner system in it, and a
window parked there would be in the way of every arrival and every departure. Windows live on the
eight planets, and a window put down at the origin belongs to Mercury.
The camera moves only when you ask it to. Every automatic camera move driftwm offers is switched off: new windows don't drag the view, opening a window doesn't reset your zoom, closing one doesn't pan somewhere, and nothing wanders while you're reading.
When the view does move, it travels. Every flight is an eased path streamed at 90 Hz, so you always see where you went — which is what makes a large canvas navigable instead of disorienting.
Press mod+/ at any time for this card on screen.
| Go | Carry | ||
|---|---|---|---|
mod+1 … mod+8 |
fly to a planet | mod+ctrl+1 … 8 |
send the focused window to a planet |
mod+0 |
fly to the Sun | A crowded planet | |
mod+left / mod+right |
sunward / outward | mod+a |
tidy this planet into a grid |
mod+u |
whole system view | mod+n / mod+p |
step through its windows |
mod+e |
the overview | mod+shift+left / right |
move it a slot along the grid |
mod+tab |
fly-to menu | Do | |
mod+; |
the command line | mod+return mod+space |
terminal · launcher |
| drag a window | to another planet | mod+q mod+f mod+m |
close · fullscreen · maximise |
| Modes | mod+t |
focus — this planet, tiled over the screen | |
mod+shift+t |
solo — one window, a column wide | mod+shift+p |
present — nothing of sol showing |
mod+shift+n |
night — the evening palette | mod+shift+d |
dark — the Sun turned down |
Drag to pan, scroll to pan, pinch to zoom — and when you're zoomed out past 45%, clicking any window flies you to it.
Four fingers travel the system, borrowing the muscle memory you already have for switching desktops. Three fingers still pan and two still zoom, so nothing you already do changes:
| swipe left / right | the next planet outward / sunward |
| swipe up | the whole system |
| swipe down | back to the Sun |
| pinch in / out | pull back to the system · drop into where you are |
| four-finger hold | tidy this planet |
On a mouse, every click borrows a habit you already have, and does what that habit expects:
- The numbers in the bar are buttons. Click
③and you are on Earth — the same gesture as clicking a workspace number in any bar since i3. This is where "click to switch" has always lived, so it is the only place a plain click means travel. - Right-click acts on what is under the pointer, as right-click has since desktops began: tidy this planet, step through its windows, new terminal here — the oldest right-click item there is, and since new windows land under the cursor, "here" is exactly where you clicked. Travel sits behind one labelled door, "Go to a planet…", instead of being what the menu is. It opens down and to the right of the pointer, flips up or left rather than hang off an edge, and closes when you click away — the three things that make a context menu one, and the three a launcher cannot do. Each item carries its keyboard equivalent down the right, the way a menu teaches you to stop needing it.
- It is the same menu at every zoom, and it answers for what you pointed at. Right-click Jupiter from the whole-system view and the menu says Jupiter — its windows, its tidy, its step, and Go to Jupiter first — at the size it always is, under your hand, where you clicked.
- Middle-click the sky opens the overview — middle-on-the-root has meant the window list since X11 had root menus, and the overview is the window list.
- On a window, sol binds nothing without a modifier. Middle-click is paste and stays paste;
alt+leftmoves andalt+rightresizes, as they do on every Linux desktop. - From far out, clicking a window flies you to it — direct manipulation: touch the thing you are looking at.
driftwm has no double-click trigger, so "double-click a planet to go there" is one habit the canvas cannot borrow; the bar's buttons and click-to-fly cover the same instinct.
Ten terminals on Earth used to be ten terminals in a heap, each one 28 pixels down and to the right of the last. Now a planet holds a district, and sol knows what is in it:
mod+atidies it into justified rows around the planet, shaped to your screen, with the name plate lifted clear above. The planet's own disc is reserved as a cell, so windows flow around it and it is never buried — from across the system a district reads as a lit world nested in its work. Rows rather than a ring of moons because rows are compact — orbits here are 820 apart while a terminal is 700 wide — and because a block shaped like your screen frames at a zoom where the windows are still usable, not merely visible.- The district gets a card: a rounded panel in the planet's colour, painted behind the windows
by the shader.
sol arrangewrites the rectangles intosol.glslbetween two markers and reloads the config, which keeps your camera, windows and focus exactly as they were. - The focused window comes forward and the rest sit a touch back, at 90% opacity.
mod+3frames the whole district when it no longer fits at working zoom, so you arrive seeing everything Earth is holding. Pressmod+3again from that overview and you drop into your window to get to work; press it again and you are back to the overview.mod+n/mod+pstep through them one at a time, flying to each.- The bar and the overview count them, so a busy planet reads as busy from across the system.
A window belongs where you put it. mod+ctrl+3 and mod+a write that down, which matters because a
tidy district is wider than the gap between planets — without it the far corner of Earth's grid
would defect to Mercury.
Everything above is about a canvas you look at. A mode is the other thing a desktop is sometimes asked to be: a screen you work inside. sol has one standing rule — the camera moves only when you ask it to — and a mode does not get to break it. Entering one is the asking, one key in and the same key out, and nothing survives the exit.
That last part is the whole design. A mode writes down every window's position and size before it
touches anything, so leaving is a restore rather than a reconstruction. sol arrange could always
put a district back by recomputing its grid; nothing can recompute the size a window happened to
have when you pressed the key. The bar names the mode you are in beside the place you are on,
because a mode you cannot see you are in is a fault rather than a feature.
Only one is ever on, and flying to another planet ends it — a mode belongs to the place it was entered on.
mod+t — focus. This planet's windows, resized to tile the whole screen, and the world behind
them gone to a shade of its own colour. Rows rather than a fixed grid, because a grid leaves holes:
three windows in a 2×2 is a quarter of the screen showing nothing. The rows are filled evenly and a
short row shares the full width, so every pixel belongs to somebody — which is the point of a mode
that fills the screen. How many rows is settled by the same test mod+a applies to a district: the
split whose cells come closest in shape to the screen they are on. Two windows land side by side,
three land two over one, four land square. sol focus 2 tiles only the two most recent and the rest
wait a screen below, still open and still one mod+n away.
What is left of the planet is its colour, washed over the sky and showing through the gaps between the tiles — enough to know which world you are standing on without any of it competing with the work. The gaps are 8 pixels and so is the margin, so the block reads as even; and the windows lose most of their corner radius for the duration, because the 24 they wear on the canvas exists to make them read as nested inside their district's card, and a mode has no card to be concentric with. At 24 the corner stops being a corner and becomes a bite showing the shade through. It goes to 8 and comes back to whatever you had it set to on the way out.
Moving between tiles. The arrows mean what they look like they mean: mod+left and mod+right
already mean sunward and outward on the canvas, and on a filled screen they keep meaning "that way"
— one tile, not one orbit. mod+up and mod+down join them. Nothing flies: everything is already
in front of you, and travelling to something already on screen is motion for nothing. mod+n and
mod+p still step in the order the grid reads, which is what you want when you are going through
all of them rather than reaching for one. Outside a mode the same arrows are driftwm's own
center-nearest, which is the same idea for a canvas.
Closing one closes the gap. A tiling that keeps a hole where a window used to be is not a
tiling, so the district changing under a mode re-draws it: close one of four and the remaining three
re-split into two over one and fill the screen again. It works the other way too — a window opening
while focus is on lands on the tiling rather than being filed onto the canvas behind it, which is
where mod+a's grid would have put it and is somewhere you cannot see. A mode whose last window
closes has nothing left to be a mode about, and ends.
The record is amended rather than rebuilt each time. A window that was already there keeps the size and the place it will be handed back — that is the one thing nothing can recompute — and a window that has just arrived is recorded with the geometry it opened with, which is what it should return to. So you can open and close things all the way through a focus session and still leave it with everything the size it was.
On a screen that cannot hold them — or a window that will not fit one. Below about 460×300 a tile is visible but not workable, so focus holds as many as fit at a size worth having and says how many it left — twelve terminals on a 13-inch laptop is twelve unreadable rectangles, and the honest answer is four you can work in and eight waiting. A 1366×768 laptop takes four, a 1600×900 takes six, and anything from 1080p up takes all twelve. The floor is a size rather than a fraction of the screen because a small laptop and a 27-inch iMac disagree about how many windows fit and agree about how small is too small.
The window gets a say too, and it overrules. A client declares a minimum size and driftwm clamps a resize to it, so a window asked for a twelfth of a screen can come back three times that and sit across its neighbours rather than in its cell — GIMP will not go under about 644×716, Inkscape 704×686, and twelve real applications was twenty-four overlaps. There is no way to ask a window its minimum in advance, so sol finds out the only way available: it lays the screen out, reads back what was actually committed, writes that down against the application, and lays out again with fewer and larger tiles. One extra pass the first time you focus a planet holding an app it has never met, and none ever after. Terminals shrink to almost nothing, which is exactly why this went unnoticed until a screen full of real programs met it.
mod+shift+t — solo. One window, in a column you can read a line in, with the sky turned nearly
off. The natural other half of focus: focus is everything here at once, solo is this and nothing. It
is a column rather than a maximise because maximising a text window on a 27-inch screen gives you a
200-character line, which nobody has ever wanted — the width that helps is the one a page has always
had. That width is an absolute measure, not a fraction: 1180 pixels is about a hundred characters
whatever it is sitting on. But it is also capped at 82% of the screen, because on a 1366-wide laptop
the absolute number stops being a column and becomes the whole screen with a margin — a maximise
wearing solo's name.
mod+shift+p — present. What to press before you share your screen. The bar and the footer go,
the name plates go with them, notifications are held, and the sky goes black — so what lands in the
capture is your window on a dark background, which is what everyone else's desktop looks like and
exactly what a projector should get. mod+n turns the deck: a district is a slide sequence once it
is one window at a time on a black screen.
mod+shift+n — night, and mod+shift+d — dark. The two palettes, and they answer opposite
questions.
Night asks what the sky should look like at eleven at night. It is not a dimmer — a dimmer just
makes a cold picture darker. The blue end comes down hardest and what is left is walked towards
amber, which is what an evening actually does to a room. sol night auto takes it from the clock.
Dark asks what it should look like when you want nothing on it competing for you at all. The Sun is turned down at the source rather than in the finished picture: dimming the render would grey the Sun along with everything else and leave it the brightest grey on the canvas, whereas taking the light out of the star itself lets the corona go first, then the limb, then the core — the system going cold from the outside in. The rest of the sky follows it towards its own grey. What survives longest is the difference between one planet's colour and the next, which is the one thing here you navigate by; and the name plates keep their colour throughout, because they are windows rather than sky, so a dark system is still a readable one.
Dark is not focus wearing another name. Focus takes the canvas away and gives you the screen; dark leaves the canvas exactly where it is and turns the lights off in it — sitting at the Sun with dark on is still the whole solar system, still navigable, just cold.
Turning one on turns the other off. They are two answers to the same question and mixing them gives neither: one walks the picture towards amber, the other towards grey, and together they cancel into a muddy nothing.
Both take on, off, or a number, and both are written into the shader itself — the same file the
canvas is drawn from — so they survive a reboot without sol owning a settings file to keep them in.
Both are read back before any mode writes its shade, so entering focus at midnight does not turn the
evening off.
What made this possible. driftwm's IPC gained Resize, and it is the whole reason this chapter
exists. Until it did, a district could be arranged but never fitted — the stack at twelve exists
precisely because windows could not be made smaller. Sizes here are the window's visual frame, the
same thing state reports, so a layout computed from what sol can see tiles exactly whether the
windows are server-decorated, client-decorated or bare.
Two things had to be learned the hard way and are worth writing down. A window is moved before it
is sized, because a district that has been through mod+a is a snap cluster, and a window that
grows into the neighbour it is snapped to gets relocated beside the cluster rather than left where
it was put — with the camera panned after it. Moving first means every window is already standing in
its own cell with a gap around it, so nothing ever grows into anything. And the layout is marked as
sol's own work for as long as it takes driftwm's geometry animation to finish arriving, or the
watcher that catches windows you drag between planets sees four windows move at once and files every
one of them back into the grid it was just lifted out of.
On a driftwm without Resize, the modes say so and refuse; everything else works exactly as it did.
sol doctor reports it in as many words.
Drag a window onto another planet and let go. driftwm has always let you move a window anywhere on the canvas; what it cannot know is that Jupiter is a place. So sol watches for the drop, and finishes the journey:
- The room makes space first. Jupiter's grid slides apart to open the slot nearest where you dropped, while the window waits where you left it.
- Then it crosses, on a path that bows away from the Sun. It is making a transfer between two orbits, and a transfer is not a straight line.
- The district it left closes the gap behind it, and both cards reflow.
Drop it back inside its own district instead and it takes the slot you dropped it on, shuffling the
others around it — which is how you order a grid. mod+shift+left / mod+shift+right move the
focused window one slot without touching the mouse.
Underneath all of it is one change: windows travel now. They used to jump to their new positions;
they are streamed along an eased path at 90 Hz, the same way the camera has always moved. mod+a is
worth watching for that reason alone.
A grid can only grow so far before framing it puts the windows below the size at which they are any use. Shrinking them to fit is not the answer even now that driftwm can resize: a window on the canvas is the size you gave it, and a grid that quietly halved everything to make room would be sol deciding how big your editor is. What is left is to stop laying all of them out.
So the grid holds the twelve most recently used and the rest collapse into a stack at its
corner: offset, dimmed, each one a step further back than the last. They are still there, still
counted, still reachable with mod+n — and touching one brings it to the front while the
least-recent drops onto the pile. The bar and the plate read home · 12 + 5, so you can see the
shape of it without going and looking.
The order a drag or mod+shift+left writes is the same order that decides this, so how a district is
arranged and how recently you used things are one mechanism rather than two.
The canvas is a single GLSL fragment shader: the Sun with its corona, eight orbit ellipses drawn with the Sun at a focus — so Mercury's is visibly off-centre and Venus's is not — planets lit with a real day/night terminator facing the Sun, Saturn's ring, a sparse asteroid belt, and a parallaxed starfield. Each world wears the face its own kind has — the gas giants banded along their latitudes, crowding towards the poles the way projected latitudes really do; the rocky ones blotched where their surfaces differ; Venus almost blank, because Venus is almost blank. Every orbit is a soft hairline with a faint bloom and no hard edge, held at a constant weight in screen pixels so it never thickens as you pull back. Each planet pools its colour into the space around it, and each district gets a rounded card in that colour behind its windows. It costs no windows and no meaningful CPU.
It also gets out of your way: at working zoom the astronomy fades to a whisper behind your windows, and blooms as you pull back — so the same canvas is a clean workspace up close and a map from far away.
The overview (mod+e) is the whole system at a size that can actually answer something: every
orbit drawn as a fine dotted ellipse, every district as a card, every window as a mark inside it,
and a pane naming what the planet you picked is holding. 1…8 fly, ↑``↓ pick a window, ⏎
drops you into it, a tidies the planet, and clicking anywhere flies there. Press mod+e again, or
esc, and it is gone.
It replaced a small live map that sat in the bottom-right corner of every screen. That map was forty cells by twenty for the entire solar system — four hundred canvas units to the cell, which put neighbouring orbits two cells apart, squashed a whole district into two rows, and left nowhere to say what any window was. It drew the shape of a system you already knew by heart and none of what you would open a map for, and it did it on top of your windows, all day. Asking for it is what makes it worth having: nothing is parked, so it can take the room it needs to be legible.
The bar borrows the grammar of the macOS menu bar, because the mapping turns out to be exact:
the planet you are standing on is the frontmost application. So ☉ sits where sits and opens
what opens, the place is named in bold where the app name goes — "Earth", "Saturn", "the
system", "deep space", in that planet's colour — and what it holds ("home · 10") reads as that app's
menus. Status sits on the right, monochrome and quiet, then Control Centre, then the clock last.
In the middle is the strip: all eight planets at once, ①②③④ ┊ ⑤⑥⑦⑧, each in its own colour with
a small tally, dim when empty, underlined where you are. It is ordinal rather than spatial on
purpose — nobody flies by angle, they fly by number — and the belt keeps its place in the middle as
the divide it is. Each number is a button: click it and you are there, exactly as a workspace
number has worked in every bar since i3. Scroll anywhere on the strip to travel sunward and outward,
the way you scroll a volume icon.
Only one module in the bar ever asks driftwm anything. sol here runs once a second, and leaves the
other modules' answers in a file for them to read, so eight planets' worth of tally costs one query.
The ☉ menu is the one at the far left, and it holds what holds: the operations that belong
to the machine rather than to anything on it — lock screen, sleep, restart, shut down, log out. It
drops from the bar, left-aligned under the glyph, because that is what a menu-bar menu does. The
three that would lose unsaved work end in an ellipsis and ask again before doing anything, so
nothing below that line happens on a single misclick. The commands are systemd's and swaylock's;
POWER at the top of that section in bin/sol is the only place that knows them.
The context menu is what right-clicking the sky means, and what right-clicking ☉ gives you: a
menu for the place under your pointer — tidy it, step through it, open a terminal here — with
travel behind one labelled "Go to a planet…" item. mod+tab skips straight to that switcher:
every place, what it holds, type to filter.
The menu is not a window on the canvas. It was one, and everything that is true of a menu had to be bought back one fix at a time: its font divided by the zoom so it would not grow, a fade to hide the animated move into place, a pinned twin for the altitudes where driftwm stops delivering clicks to canvas windows at all. Four fixes deep it was still a different menu at every zoom — because a menu is a screen-space object and the canvas is not screen space.
So sol-menu is a layer-shell surface, the same kind of surface the bar is, which the canvas
neither scales nor swallows clicks for. There is no zoom arithmetic left in it, no pinned fallback
and nothing to undo: one menu, the same size and in the same place relative to your hand, at 7% and
at 100%. It flips up or left rather than hang off an edge, closes when you click away — a
full-screen transparent overlay is what a menu grab is — and fades in over a tenth of a second where
it landed. It also carries an ✕, which no desktop's context menu does: clicking away is the way
out and always was, but a way out you cannot see is one you have to be told about, so there is one
you can see. It is the same grey as the keyboard hints until the pointer is on it.
What that costs is knowing where the pointer is, because a layer surface is not told. driftwm has no IPC for the cursor, and a surface that maps under a motionless pointer is sent no enter event to learn from. But driftwm centres the first window of a spawn on the cursor that started it — so the menu opens a 1×1 window nobody can see, reads back where the compositor put it, and closes it again. That is the same trick the old menu ran on itself; the difference is that the thing being measured is no longer the thing being shown, which is what frees the menu from the canvas.
And because the pointer is now known as a point rather than inferred from the camera, the menu can answer for what is under it. Right-click Jupiter from the whole-system view and the menu says Jupiter — Go to Jupiter, tidy it, step through its windows — instead of shrugging at "the system" because the camera was over the Sun. Point at the sky between the orbits and it is the system again, because that is what you pointed at.
It still closes when the view moves: a menu belongs to the moment it was opened in, and one drawn for the place under your pointer is stale once you have flown somewhere else.
interact_min — the zoom at which driftwm decides a canvas window is too small to touch, and turns
a click into "fly to that window" — is untouched at 0.45. It is what makes click-to-fly and clicking
a name plate work from far out, and the menu no longer has an opinion about it either way.
The footer is the same bar upside down, and it is one surface with two registers — the way
vi's status line has been its command line for fifty years. At rest it reports: the header says
where you are, this strip says what sol last did — arrange 3 · just now — the event a shade
brighter than its timestamp, a signal in the colour of the district that sent it, and a clock
that coarsens instead of ticking. It reserves its 28 pixels at the screen edge rather than
floating over your windows.
Press mod+; and the same 28 pixels listen. Nothing opens and nothing arrives: the cursor
lights after the ☉ ▸ that was always shaped like a prompt, and the echo yields its half of the
line to a ghost that says what enter will do before you press it — type jup and the right
edge reads go to Jupiter · mod+5, carrying the keybinding the way the menu's accelerator column
does, at the exact moment the intent exists. The line falls through command → place → window: a
bare place name is taken as goto, because "jupiter" is what you would type if nobody had told
you a verb was expected, and a word that is neither is a window to find — sla reads fly to
"Slack" · Venus before you commit. Tab completes into the same slot, up-arrow recalls across
sessions, and a typo keeps the line and turns the ghost corrective — an error costs a glance,
not a retype.
Ask it a question and the answer lands where things persist: here closes the prompt and leaves
here → Earth · home · 10 in the echo. The one case a line cannot hold — list, doctor — grows
the strip the rows it needs and gives them back on the next keypress, which is the minibuffer's
move, and emacs has kept it for forty years because it is right.
The strip is a layer-shell surface, the same species as the menu, so it holds the keyboard only while addressed — and on a machine without GTK it execs the waybar-and-foot pair it replaced, so the bottom edge is never simply missing.
Name plates float beside each planet, naming it and counting what it holds — "3 E A R T H
home · 10". They're ordinary windows on purpose: driftwm limits how far you can zoom out to half the
fit of the real windows on the canvas, so the plates are what hold the canvas open wide enough for
the whole-system view to exist — and they double as click-to-fly targets. Keeping the tally live
costs nothing: sol here, which the bar already runs once a second, leaves the lines in a file and
each plate just reads its own.
Press mod+/ for the card, any time:
A notification is a message about a thing, delivered on top of whatever you were doing. sol has something better to point at: the place the thing happened. A signal is light from a place.
sol signal "tests green" # this district calls, with a message
sol signal --at mars "review ready" # that one
sol signal --urgent "deploy failed" # a red call
sol signal -- make -j8 # run it, then call by its exit codeWhen a district calls, the call is transmitted: a drop lands on the planet and sends what a drop
actually sends — a train of crests in the district's colour, each weaker than the last, from a
centre that bobs as it settles, broadening as they travel. The leading crest crosses the canvas at
one fixed speed, so a call from the next orbit reaches you at once and one from Neptune takes
visibly longer, which is the same sentence the whole layout speaks. An urgent call hits harder and
rebounds more. Zoomed into your work, an off-screen call still crosses your view as an arc entering
from the side it came from: you feel the direction the way you hear a door open behind you. The
water is drawn on a click-through layer surface — exactly as interactive as light. Then what persists is
quiet: the district's name plate wears an ember — ✳ 4 M A R S ops · 3 — its chip in the bar
keeps its colour and gains a star (amber for routine, red for urgent), the map marks it, and the
footer says it once: ✳ Mars · tests green. Nothing slides in, nothing covers your work, and the
camera does not move — the one rule holds for signals too. They may call; they never drag.
mod+s answers. It flies you to the oldest call, urgent ones first. And arrival is the
acknowledgement: arriving at a district clears its calls, however you arrive — mod+s, mod+4,
a four-finger swipe, a drag. Arriving, not merely being there: a camera parked on Mars overnight
does not eat the calls Mars makes while you sleep. There are no dismiss buttons and no unread pile
to manage, because the camera only ever moves when you move it, so the camera arriving is your
attention arriving. A dark sky means nothing waits.
This is what makes sol a desktop for running agents. Give each one a district — the terminal with
the refactor on Mars, the one writing tests on Jupiter — put sol signal done in the agent's
finish hook (or export SOL_AT=mars in its shell and let every call from there attribute itself),
and the question that actually burns while five things run — which one needs me? — is answered
from orbit, by glancing at the sky. The -- form exists because a deferred call asks "where am I"
at the wrong moment: make && sol signal done fires after you have flown away, so sol signal -- make reads the place when the work starts, colours the call by the exit code, and exits with
that code so pipelines cannot tell it was there.
sol signal with nothing to say lists what is still calling; sol answer with nothing calling
says so and stays put.
On Debian or Ubuntu, take the .deb from the
latest release:
sudo apt install ./sol_1.1.0_all.deb # apt, so it pulls the dependencies
sol setup # the config, into your home directoryAfter that, sol is in your login screen, beside Plasma and anything else installed — pick it from the session list the way you would pick any other desktop, and pick your old one to go back. Nothing about installing sol takes your machine over.
sol setup is a separate step on purpose. A package is installed once for a machine and a config
belongs to a person — the one here has your monitor's connector name in it — so nothing is written
under your home directory until you ask for it. It never overwrites: an existing
~/.config/driftwm is moved aside with the date on it.
Anywhere else, or from a checkout:
git clone https://github.com/bachata-dev/sol
cd sol
./install.shinstall.sh does both halves at once, because the person running it is the person being
configured. It installs to /usr/local, where things you put there yourself belong; the package
installs to /usr and owns what it puts there. Either way sol doctor will tell you what is
missing.
driftwm is not in Debian, so the package cannot declare it as a dependency — a dependency on a
package that does not exist makes a .deb uninstallable rather than informative. Install it from
its own repository. Everything in sol exits cleanly without
it and sol doctor says so in as many words. The screen modes additionally want a driftwm new
enough to have a Resize in its IPC, and refuse with an explanation on one that does not.
Three files the package installs that have nothing to do with the canvas, and everything to do with sol being a desktop you can choose rather than a thing you boot specially:
/usr/share/wayland-sessions/sol.desktopis what a login screen reads. Without it there is no way to pick sol except booting specially — a strange thing to ask of someone trying it, and a stranger thing to ask of someone who wants to go back to KDE afterwards./usr/lib/systemd/user/sol-session.targetis the handle that starts a session.graphical-session.target— the thing every portal, notification daemon and keyring agent waits for — refuses to be started by hand and may only be pulled up by something that depends on it. So this is the thing that depends on it, andsol session-readystarts it from driftwm's autostart, by which point there is a display to name./usr/share/xdg-desktop-portal/sol-portals.conftells portals which backend this desktop uses. Without it "Share screen" in a browser lists nothing at all and a file dialog takes four seconds to open — and neither of those reports an error, which is what makes it worth a file.
Portals are keyed by XDG_CURRENT_DESKTOP, and the value that decides anything is the one the
session manager holds, not the one in your shell. driftwm sets driftwm for the processes it
spawns, so sol session-ready sets sol:driftwm explicitly rather than importing what it
inherited. sol doctor reports the value portals will actually read.
Then set your display in the MACHINE-SPECIFIC block of ~/.config/driftwm/config.toml (connector
name and HiDPI scale — find yours in driftwm msg state) and start driftwm from your display
manager's session list, or on a spare VT:
sudo driftwm-up # defaults to VT 3The installer also offers to boot into sol: autologin on tty1, and a line in your profile that
hands the console to sol-session. It is off by default, because it makes the machine log itself
in — the right trade only when physical access to it already means everything.
It matters for more than convenience. logind grants "restart this machine" to the active session
on a local seat, so a desktop started some other way — by a root systemd-run, or with a second
desktop still autologging in on another VT — is a desktop whose ☉ menu quietly does nothing when
you click Restart. sol doctor says so in as many words. If the compositor fails to start,
sol-session drops you back to the console prompt with the tail of its log rather than looping.
sol installs as a module at /usr/local/lib/sol with a seven-line stub on the path, rather than
as one big script. Python caches compiled bytecode for what it imports and never for a script it was
handed, so a script run directly re-parses all 190KB on every keypress: 55ms, of which 43ms is the
parser and 11ms is the interpreter existing at all. Imported once and compiled at install time, the
same command answers in 24ms. Nothing about sol runs on a timer, so this was never a load question —
it is the difference between a keybinding that feels instant and one that nearly does, paid again on
every press, every menu item and every click in the bar.
Requirements: driftwm, foot, python3, awk;
python3-gi (GTK 3) and gtk-layer-shell for the ☉ context menu and the footer's own strip —
without them right-click has nothing to open and the footer falls back to its waybar-and-foot
pair, and everything else works; python3-gi-cairo as well for the signal ripples —
without it the embers and the bar still say everything, just without the water; optionally waybar, fuzzel, mako; JetBrains Mono
plus a Nerd Font for the bar glyphs.
Omarchy keybindings light up automatically if omarchy is installed, and are
harmless if not.
Everything the keybindings do is available directly, and the whole toolkit is one command:
sol goto 4 # or: sol goto mars, sol goto ops, sol goto sun
sol hop out # next planet outward; `sol hop in` goes sunward
sol system # frame the whole solar system
sol arrange # tidy this planet's windows; `sol arrange all` does every one
sol focus # fill the screen with this planet's windows; `sol focus 2` takes two
sol solo # one window, a column wide, the sky nearly off
sol present # one window, no chrome, nothing of sol showing
sol mode # which mode is on; `sol mode off` leaves it
sol night # dim the sky the way an evening does; on, off, auto, or a number
sol dark # turn the Sun down; the colour goes out of the system with it
sol next # step to the next window here; `sol prev` goes back
sol send 8 # send the focused window to Neptune; `--stay` to not follow
sol back # the camera to where it was before the last flight (it toggles)
sol find # fly to a window by name, wherever it is; `sol find slack` skips the menu
sol tidy # gather the lost, arrange every planet, sweep the overgrown — one verb
sol gather # bring every adrift window in, to home; `sol gather 8` files them on Neptune
sol sweep # drain this planet's stack to the archive; `sol sweep 5 2` names both
sol homes # which app lives on which planet
sol tour # a narrated first flight; touch anything and it yields
sol doctor # is this machine set up right? paste it into bug reports
sol list # what is where; --json for the same census as one object
sol plate 3 # the line Earth's name plate is showing
sol here # where am I?
sol menu # the context menu, at the pointer
sol menu system # the ☉ menu: lock, sleep, restart, shut down, log out
sol menu go # the switcher: every place, type to filter
sol map # the overview (runs inside a terminal; sol-map toggles it)
sol bar strip # one line of the bar — where, holding, strip, or last
sol shift back # move the focused window a slot along its grid
sol watch # catch windows dragged between planets (runs from autostart)
sol setup # install the config into your home directory (after a .deb)
sol --version # which sol this is; `sol doctor` prints it too
sol help # the keybinding cardThe read commands answer in two voices. Prose is for a person at a prompt; add --json and the
same census comes back as one object on stdout, for a script that has to act on the answer rather
than read it:
sol list --json # every window, grouped by planet, plus anything adrift
sol here --json # where the camera stands, what mode is on, who is calling
sol homes --json # which app is filed on which planet
sol doctor --json # every check with its status; `healthy` says it in one wordNothing is decided twice: both voices are the same command reading the same state, so neither can
drift from the other. sol here --json is the one worth polling — place, mode, window count and
the call queue (urgent first, then oldest, the order mod+s flies) in a single answer.
And the whole CLI is offered over the Model Context Protocol:
sol-mcp is a stdio MCP server in which every tool is one sol command run exactly as a keypress
would run it — nothing in it knows how to move a window; it only knows how to ask. Register it
with whatever speaks MCP; for Claude Code:
claude mcp add sol -- sol-mcpThen the agent living on Mars can finish its refactor and say so — signal with at: mars, and
the call crosses the canvas like every other call. An assistant told "set up my usual layout:
agents on Mars, builds on Jupiter, comms on Venus" can read the census, find each window, carry it
there and tidy up after itself — list_windows, find_window, send_focused, arrange — with
every step the same motion a hand would have made, under the same rule everything here obeys: the
camera moves only when asked. It is stdlib Python like the rest of sol, and it finds driftwm the
way every sol command does, so it works wherever sol does.
Put an app on a planet and every window it opens goes there — once, when it opens, on the same arc a drag would take, with the footer saying so. A window you later move stays where you moved it: a home is where a thing starts, not a leash.
# ~/.config/driftwm/sol.toml
[homes]
slack = 2 # by number
firefox = "comms" # or by role
code = "jupiter" # or by nameThe key matches any part of the window's app_id, because a browser calls itself
firefox on one machine and org.mozilla.firefox on the next. sol homes prints what
is filed where. This is sol's own file rather than a section of driftwm's config, because
driftwm rejects unknown tables — a stray one there fails the whole parse on reload.
Nothing below runs unless you turn it on, because each one moves the camera without being asked.
sol-planetarium— after four idle minutes, glides the camera in a slow orbit of the system (~8 minutes per revolution) and hands it straight back on any key or mouse movement. Add"sol-planetarium"toautostartin the config.sol-spin— draw a circle with the mouse to travel outward (clockwise) or sunward (counter-clockwise). Root systemd service, since it reads/dev/input:sudo systemctl enable --now sol-spin. It also stamps input activity, which is how the planetarium tells real idleness from you simply not panning.
- Different sky —
python3 tools/sol-positions.py 2027-03-01solves the orbits for that date and prints the planet table forbin/sol, the orbit constants forconfig/sol.glsl, and the name-plate placements for the config. The layout is a snapshot of the real sky, so you can set it to a date that means something. - Roles — the labels ("home", "ops", "builds") are just strings in the autostart lines.
- Sizes — every disc comes from one rule in
tools/sol-positions.py: the cube root of the body's real radius, with Earth at 105. ChangeEARTH_PXto scale them all, or the exponent for a different compression. - Shader — every colour and coefficient is a named
constat the top ofsol.glsl. Saving the file is not enough on its own: pressmod+shift+cto reload the config and the shader comes with it. The only linessolever writes are the district rectangles between the two── districts ──markers; delete the markers and you simply get no cards. - The bar and the menu —
waybar.jsoncandwaybar.cssfor the bar. The context menu isbin/sol-menu, and its whole appearance is the CSS block at the top of that file.fuzzel.inistyles the switcher (mod+tab) only; delete it and that falls back to whatever fuzzel already does. - No trackpad? —
tools/sol-hand.pyis an emulated hand: an absolute mouse, a relative mouse, a four-finger touchpad and a keyboard, all through/dev/uinput, real enough that libinput runs its actual gesture engine over them. Every swipe, pinch, click, drag and keypress above was verified with it, over SSH, before it ever met a physical hand.
Real: the order, the angles, the eccentricities and the perihelion directions. Each planet is placed by solving Kepler's equation for the date, so it sits on its own ellipse where it actually was, and the Sun sits at a focus of all eight.
Two things are deliberately not to scale, because true scale is unusable:
- Orbit spacing is uniform — 1000, 1820, 2640 … 6740. In reality Neptune orbits 78× further out than Mercury; here it is 6.7×. Even spacing keeps the whole system reachable and makes every hop between neighbours take about the same time.
- Discs are the cube root of the real radii, scaled so Earth is 105 across. One rule for every body, so the ranking is exactly right — Jupiter is the giant, Mercury the pebble, and the Sun dwarfs all of it — without the giants swallowing their own orbits. At true scale, with Neptune's orbit where it is, Earth would be a hundredth of a pixel.
The two scales are also independent of each other: relative to its own orbit, every planet is drawn hundreds of times too large. Distance means "how far from what I'm doing", not kilometres.
./uninstall.sh # removes the commands and the daemon; your config stays putIt also takes back the boot hook if you asked for one — the profile block and the autologin drop-in go together, since a console that logs itself in for a desktop that is no longer there is worse than either alone.
Developed and daily-driven on Debian 13 with AMD graphics and a 4K display. It should work anywhere
driftwm runs; display scale, fonts, and the /dev/input bits are where another machine is most
likely to need a nudge. Issues and PRs welcome.
- driftwm by Klim Kostiuk — the infinite-canvas compositor that makes any of this possible
- Several palette hues began in Tokyo Night by enkia
GPL-3.0, matching driftwm. See LICENSE.










