15 KiB
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:
(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:
./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:
./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:
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:
(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 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:
(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 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:
(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:
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: user policy, styling, commands, and providers.
- Live development: SLY, reloads, restarts, and error recovery.
- Persistence: saved state, window identity, and migration limits.
- Attention: urgency, acknowledgement, and explicit visits.
- Model: activities, views, presentations, leases, and invariants.
- Architecture: boundaries between the model, X11, commands, and UI.
- X11/REPL walkthrough: 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.