314 lines
15 KiB
Markdown
314 lines
15 KiB
Markdown
# Lime
|
||
|
||
Lime is an experimental **X11 window manager written in Common Lisp**, organized
|
||
around what you're doing: work, a project, a game, or whatever else needs its own
|
||
set of windows.
|
||
|
||
Keep related windows in a named **activity**, show only what you need, and save
|
||
useful arrangements as optional **views**. Each monitor can switch activities
|
||
independently. Configure and extend Lime in Lisp, with a live REPL available for
|
||
development.
|
||
|
||
Lime is undergoing real daily-use testing on Linux with SBCL. It manages a real
|
||
desktop, but remains a young project: compatibility, recovery, and interaction
|
||
details are still being refined. Start in Xephyr and keep a known-good session
|
||
available before trying it as your main window manager.
|
||
|
||
## The workflow
|
||
|
||
### Activities hold your windows
|
||
|
||
Create an activity such as `work`, `lime`, or `chill`, then open the applications
|
||
you need. There is no fixed activity count and no requirement to save a view.
|
||
New activities start with a blank working arrangement.
|
||
|
||
New activities are **portable**. Select one on a monitor—called a **Head** in
|
||
Lime—and its current arrangement comes there, including unsaved edits. If it
|
||
is already displayed elsewhere, the two monitors swap their arrangements.
|
||
If it was inactive, only the chosen monitor changes. There is no empty
|
||
monitor-specific copy to hunt through.
|
||
|
||
Need more room? `Super-y → a → e` explicitly extends the current activity onto
|
||
another monitor. Its portions can then switch away and return independently.
|
||
New portions start empty; windows are never duplicated or gathered implicitly.
|
||
This choice persists even if a portion becomes empty. `Super-y → a → p` makes
|
||
it portable again once the other portions are empty and no longer displayed.
|
||
|
||
Hide a window when you don't need it in the current arrangement. It stays in
|
||
the activity, available from the window picker. Hiding is distinct from monocle:
|
||
monocle cycles through its participating windows, while hidden windows sit out.
|
||
|
||
### Views are optional checkpoints
|
||
|
||
Suppose your `lime` activity contains an editor, documentation browser, two
|
||
terminals, and a test application. Save a `code` view showing the editor and a
|
||
terminal; save a `debug` view showing the test application and its logs.
|
||
|
||
These are saved arrangements of the activity's windows, not separate window
|
||
buckets. Recalling a view restores its requested windows, layout, and focus on
|
||
one Head. Editing the arrangement does not overwrite the checkpoint; updating
|
||
a saved view is explicit. New windows are not added to older checkpoints
|
||
automatically. Deleting a view does not close its windows or delete the activity.
|
||
|
||
An activity works just fine with no saved views at all. The bar shows activity
|
||
names, with the current one highlighted; checkpoint actions live in the menu.
|
||
For coordinated multi-monitor recalls, a **Viewset** saves a mapping of Heads
|
||
to views and applies it as one checked transition.
|
||
|
||
### One window, one place
|
||
|
||
A real window can appear on only one Head at a time. Internally, a **lease**
|
||
records which current arrangement owns its display. If a requested window is
|
||
already in use elsewhere, Lime asks whether to open without it or move it here.
|
||
A saved view cannot be mounted on two Heads simultaneously.
|
||
|
||
Windows normally belong to one home activity. You can move them to another, or
|
||
make them **shared** so any activity can request them—useful for a music player.
|
||
Sharing does not duplicate a window or make it appear everywhere. Making it
|
||
private again keeps it in the activity where you're using it, preserves its
|
||
placement, and removes references from other activities, including their saved
|
||
views. That change requires confirmation.
|
||
|
||
## What works today
|
||
|
||
- **Tiling and floating:** master/stack by default, monocle, per-window floating,
|
||
tiled reordering and sizing, mouse move/resize, configurable new-window/focus
|
||
policy, borders, and gaps. A sole visible tile has no gaps or border; floats
|
||
retain their borders. Manual tiling trees remain available through Lisp and
|
||
saved views, but are excluded from the everyday layout cycle.
|
||
- **Independent monitors:** RandR discovery, portable activity swaps and explicit
|
||
multi-Head arrangements,
|
||
numbered activity/view navigation, and window transfers that follow the moved
|
||
window with focus. Private cross-activity transfers ask before changing home
|
||
membership. Display changes offer an explicit reconciliation plan.
|
||
- **Everyday client handling:** close requests, focus protocols, size hints,
|
||
floating dialogs and fixed-size startup windows, fullscreen, and quiet
|
||
attention indicators. Compatibility covers a selected ICCCM/EWMH subset.
|
||
- **Keyboard and mouse UI:** a clickable per-Head bar, a which-key-style command
|
||
dropdown, searchable pickers, and a fuzzy PATH launcher. The launcher caches
|
||
executable discovery; shortcut-to-menu keyboard handoff preserves rapid
|
||
follow-up typing, including during a cold refresh.
|
||
- **Configurable presentation:** Pango/Cairo text with Unicode/font fallback,
|
||
colors, and session-local cursor themes. Optional music, volume, and clock providers
|
||
publish data to Lisp formatters and click actions. Music/audio updates use
|
||
subscriptions; the clock wakes on minute boundaries.
|
||
- **Live development and saved desks:** reloadable Lisp configuration,
|
||
function-level source reloads through SLY/Slynk, supervised error recovery,
|
||
and session autosave. A Lime-only restart can reconnect surviving X windows
|
||
to their arrangements.
|
||
|
||
## Try it safely in Xephyr
|
||
|
||
Use a nested X server first. **Do not start Lime on a display already managed
|
||
by another window manager.**
|
||
|
||
You need:
|
||
|
||
- Linux/X11, SBCL, ASDF, and Quicklisp loaded by your SBCL startup file.
|
||
- Xephyr for this nested test, and a test client such as `xterm`.
|
||
- Native Cairo, Pango/PangoCairo, GLib, and GObject libraries, plus suitable fonts.
|
||
- Slynk available to ASDF for `lime/dev` (normally supplied through SLY or your
|
||
Lisp dependency setup). Optional libXcursor enables cursor themes.
|
||
|
||
From the checkout, in an SBCL REPL, register the local systems and load the
|
||
development dependencies once:
|
||
|
||
```lisp
|
||
(require :asdf)
|
||
(asdf:load-asd (truename "lime.asd"))
|
||
(ql:quickload "lime/dev")
|
||
```
|
||
|
||
The ASDF definitions also declare CLX, Babel, CFFI, Bordeaux Threads, and usocket
|
||
(0.8.8 or newer). If Slynk is not available through Quicklisp, make SLY's Slynk
|
||
ASDF system discoverable before loading `lime/dev`.
|
||
|
||
Then, from a terminal in your existing graphical session:
|
||
|
||
```sh
|
||
./scripts/run-xephyr-dev --no-config --no-session --repl-port 4006
|
||
```
|
||
|
||
This starts Xephyr on `:99` with a blank `Scratch` activity, Lime's starter
|
||
bindings, no personal config, and no session restore/autosave. It uses port
|
||
`4006` here to avoid the usual
|
||
development REPL on `4005`. Choose another free display/port if needed:
|
||
|
||
```sh
|
||
./scripts/run-xephyr-dev --help
|
||
```
|
||
|
||
Click inside Xephyr and use its `Ctrl-Shift` input-grab toggle when you want
|
||
shortcuts to reach the nested session instead of your host WM. Release the grab
|
||
with the same toggle. To create test windows from a host terminal:
|
||
|
||
```sh
|
||
DISPLAY=:99 xterm
|
||
DISPLAY=:99 xterm
|
||
```
|
||
|
||
Quit Lime with `Super-Shift-q`, or stop the runner with Ctrl-C in its host
|
||
terminal. The runner tears down its nested server; windows in that test server
|
||
will close.
|
||
|
||
Nested X separates displays, **not application profiles or desktop services**.
|
||
A browser/editor may reuse an existing process on your main desktop, and audio
|
||
or D-Bus services may be shared. Use simple test clients or explicitly isolated
|
||
application profiles when needed.
|
||
|
||
### Starter controls
|
||
|
||
These are the bundled defaults, not requirements for your personal keymap.
|
||
`Super` means Mod4, usually the Windows/logo key. The `st` and Waterfox launch
|
||
bindings require those programs; `Super-p` can launch other installed programs.
|
||
|
||
| Binding | Action |
|
||
| --- | --- |
|
||
| `Super-p` | Fuzzy-search PATH programs and launch one |
|
||
| `Super-y` | Open the which-key command menu |
|
||
| `Super-Shift-Return` / `Super-b` | Launch `st` / Waterfox |
|
||
| `Super-t` / `Super-h` | Focus next / previous visible window |
|
||
| `Super-Shift-t` / `Super-Shift-h` | Reorder the focused tiled window |
|
||
| `Super-Return` | Promote the focused tile to master |
|
||
| `Super-Tab` | Cycle master/stack and monocle |
|
||
| `Super-d` / `Super-n` | Shrink / grow the master area |
|
||
| `Super-Space` / `Super-f` | Toggle floating / fullscreen |
|
||
| `Super-i` / `Super-Shift-i` | Show hidden/shared windows / hide focused window |
|
||
| `Super-1…9,0` | Select activity positions 1–10 in bar order |
|
||
| `Super-Shift-1…9,0` | Select saved-view slots 1–10 within the activity |
|
||
| `Super-e` / `Super-Shift-e` | Focus next Head / move window there and follow |
|
||
| `Super-Shift-s` | Choose a saved view to update, with confirmation |
|
||
| `Super-Shift-c` / `Super-Shift-q` | Close focused window / quit Lime |
|
||
|
||
Super + left/right mouse drag moves/resizes a float; Super + middle click
|
||
toggles floating. Activity numbers close gaps after deletion; view slots stay
|
||
stable. The first ten shortcuts are not a limit on how many you can create.
|
||
|
||
In the command menu, press a shown key or click its entry. `+` marks a submenu;
|
||
Backspace goes up, Space/Tab opens search, and Esc closes. Useful paths include
|
||
`a → n` to create an activity, `v → s` to save a view, `v → o` to recall one,
|
||
`w → m` to move a window to another activity, and `w → s` to share/make private.
|
||
|
||
## Make it yours
|
||
|
||
Configuration is a user-owned Lisp file, separate from both Lime's source and
|
||
saved session state. For example, save this as `~/.config/lime/config.lisp` to
|
||
keep the starter map but use `xterm` and add a live config-reload binding:
|
||
|
||
```lisp
|
||
(in-package #:cl-user)
|
||
|
||
(lime.config:defconfiguration my-lime
|
||
(:options :default-keybindings t :default-layout :master-stack)
|
||
(:command terminal
|
||
:label "Terminal" :key #\t
|
||
:action (lambda (backend)
|
||
(lime.x11:backend-launch-program backend '("xterm"))))
|
||
(:bind #\Return terminal :modifiers '(:mod-4 :shift))
|
||
(:command reload-config
|
||
:label "Reload configuration"
|
||
:action #'lime.config:reload-configuration)
|
||
(:bind #\r reload-config :modifiers '(:mod-4 :shift)))
|
||
```
|
||
|
||
Run the Xephyr script without `--no-config` to load that file when it exists,
|
||
or select one with `--config PATH`. Without `--no-session`, developer sessions
|
||
autosave to the XDG state directory in separate files per display. Explicit
|
||
`--session-file PATH` is also supported.
|
||
|
||
The [configuration guide](docs/configuration.md) covers commands, bindings,
|
||
layout policies, window rules, fonts, borders/gaps, bar colors, cursor themes,
|
||
and optional status providers. Configuration reload preserves the current desk.
|
||
|
||
## Develop while it runs
|
||
|
||
The separate `lime/dev` system runs X events on one owner thread and exposes a
|
||
loopback-only Slynk REPL. Connect SLY to `127.0.0.1:4006` for the example above
|
||
(the runner's default is `4005`). Serialize live backend work through its API:
|
||
|
||
```lisp
|
||
(lime.dev:with-development-backend (backend)
|
||
(lime.x11:backend-status backend))
|
||
|
||
(lime.dev:reload-development-configuration)
|
||
(lime.dev:reload-development-code)
|
||
```
|
||
|
||
Ordinary function-level edits can reload without replacing your activities or
|
||
windows. Structure changes remain an explicit restart boundary. Unexpected
|
||
event-loop errors pause dispatch, release input grabs, and preserve the control
|
||
REPL for inspection and explicit recovery. See the
|
||
[development guide](docs/development.md) before running on a real X session.
|
||
|
||
## Current boundaries
|
||
|
||
- **X11 only.** Lime is not a Wayland compositor or a complete desktop environment.
|
||
- **Partial ICCCM/EWMH coverage.** There is no conventional numbered-desktop
|
||
emulation for external pagers. External docks can be mapped, but their strut
|
||
reservations do not yet define Lime's work areas. Hotplug reconciliation is
|
||
explicit rather than automatic.
|
||
- **Views capture arrangements, not applications.** Persistence reconnects
|
||
verified surviving windows on the same X server. It does not relaunch programs
|
||
or restore browser/terminal contents after logout or reboot.
|
||
- **Some interfaces are still low-level.** There is no interactive layout
|
||
designer, dedicated all-floating layout, or per-window titlebar decoration.
|
||
Manual trees remain an advanced API. Window rules style new clients rather
|
||
than automatically assigning activities or taking leases.
|
||
- **Application cursor behavior varies.** Explicit cursor config is published
|
||
to the X session, but some apps need reopening or override the theme. Lime
|
||
respects an existing XSETTINGS manager and does not rewrite toolkit configs.
|
||
- **Lisp configuration is trusted code.** Load only configuration you trust;
|
||
validation/rollback of WM settings cannot undo arbitrary Lisp side effects.
|
||
Keep backups of important session files before structural upgrades.
|
||
|
||
## Tests
|
||
|
||
The model, UI state machine, persistence, and adapter-policy tests use ASDF and
|
||
FiveAM without a running X server. Adapter font tests still require the native
|
||
text libraries. From an SBCL REPL in the checkout:
|
||
|
||
```lisp
|
||
(require :asdf)
|
||
(asdf:load-asd (truename "lime.asd"))
|
||
(ql:quickload '("lime/test" "lime/ui/test" "lime/persistence/test"
|
||
"lime/x11/test" "lime/dev/test"))
|
||
(dolist (system '("lime" "lime/ui" "lime/persistence" "lime/x11" "lime/dev"))
|
||
(asdf:test-system system))
|
||
```
|
||
|
||
Inspect FiveAM's pass/fail summary, not just the Lisp process exit status.
|
||
|
||
Real-X smoke tests create and tear down their own Xvfb servers; they do not use
|
||
your current display. With Xvfb and the Lisp dependencies installed, run one or
|
||
all from the checkout:
|
||
|
||
```sh
|
||
sbcl --non-interactive --load test/x11-launcher-smoke.lisp
|
||
|
||
for smoke in test/x11-*-smoke.lisp; do
|
||
sbcl --non-interactive --load "$smoke" || break
|
||
done
|
||
```
|
||
|
||
These cover burst keyboard input, clickable menus, numbered navigation,
|
||
membership and monitor moves, floating gestures, appearance, fonts/cursors,
|
||
status subscriptions, attention, persistence, and fault recovery. The font
|
||
tests need Japanese/emoji fonts for their samples; cursor tests additionally
|
||
need Adwaita and test-only libXfixes. The session-settings smoke test also needs
|
||
Python 3 with PyGObject and GTK 3. Status tests use fake services rather than
|
||
controlling your real music or audio.
|
||
|
||
## Further reading
|
||
|
||
- [Configuration](docs/configuration.md): user policy, styling, commands, and providers.
|
||
- [Live development](docs/development.md): SLY, reloads, restarts, and error recovery.
|
||
- [Persistence](docs/persistence.md): saved state, window identity, and migration limits.
|
||
- [Attention](docs/attention.md): urgency, acknowledgement, and explicit visits.
|
||
- [Model](docs/model.md): activities, views, presentations, leases, and invariants.
|
||
- [Architecture](docs/architecture.md): boundaries between the model, X11, commands, and UI.
|
||
- [X11/REPL walkthrough](docs/xephyr-demo.md): detailed nested-server exercises.
|
||
|
||
The current focus is daily-use polish and robustness: fixing real application
|
||
compatibility issues, smoothing common interactions, and filling session
|
||
integration gaps while keeping the core model independent of X11 and the UI.
|