Files
lime/README.md
2026-10-02 16:35:17 -05:00

314 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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