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

675 lines
34 KiB
Markdown
Raw Permalink 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.

# Configuration
Lime’s configuration is a user-owned Common Lisp file—not a source-tree fork
and not a collection of patches. Load `lime/x11`, then start through
`lime.config:start-lime` with the file path you want to own, for example
`~/.config/lime/config.lisp`.
```lisp
;;;; ~/.config/lime/config.lisp
(in-package #:cl-user)
(lime.config:defconfiguration my-lime
(:options :default-keybindings t
:default-modifiers '(:mod-4)
:default-layout :master-stack
;; New tiles append at the bottom of master/stack, but receive
;; focus; closing one returns focus toward master.
:new-client-focus t
:new-tiled-client-position :back
:close-focus-direction :previous)
(:command terminal
:label "Terminal"
:description "launch Alacritty"
:category "Launch"
:key #\t
:action (lambda (backend)
(declare (ignore backend))
(uiop:launch-program '("alacritty") :output :interactive)))
(:command reload-config
:label "Reload configuration"
:description "reload this file without restarting Lime"
:key #\r
:action #'lime.config:reload-configuration)
(:command palette
:label "Command palette"
:description "open Lime's command picker"
:action #'lime.x11:backend-open-command-palette)
;; This replaces the starter map's Super-t binding with TERMINAL.
(:bind #\t terminal :modifiers '(:mod-4))
(:bind #\y palette :modifiers '(:mod-4))
(:bind #\r reload-config :modifiers '(:mod-4 :shift))
;; Rules are ordered. The first match styles an ordinary newly mapped
;; client; strings are case-insensitive exact ICCCM WM_CLASS matches.
(:rule :class "mpv" :float t :center t)
(:rule :class "Pavucontrol" :float t :center t)
(:font :family "monospace" :size 14 :weight :normal)
(:bar :height 22
:status-providers (list (lime.x11:make-clock-status-provider)))
(:appearance :border-width 3
:focused-border "#ffac54" :unfocused-border "#121f25"
:inner-gap 5 :outer-gap 5))
```
```lisp
(asdf:load-system "lime/x11")
(lime.config:start-lime
:display-name ":99"
:config-file #P"/home/adan/.config/lime/config.lisp")
```
`Super-y`, the clickable `Lime` bar item, and a command’s optional
`:key` all meet in the same command registry. `Super-p` is deliberately kept
for the immediate dmenu_run-like PATH-program picker. `:key` places a command under the
palette’s `User` which-key branch; it does not make an X key grab by itself.
Use `:bind` for that. Commands are ordinary functions, so anything beyond this
small DSL is just Common Lisp calling Lime's public command functions.
The program picker caches PATH executable names. It refreshes when PATH or a
directory modification date changes, or after five minutes on the next open
(covering executable-permission edits). For immediate rediscovery from the REPL,
use `(lime.x11::%path-program-names :refresh-p t)`. Opening shortcuts queue rapid
follow-up keystrokes safely until the menu takes over, including cold launches.
## Numbered navigation
The starter map binds `Super-1` through `Super-9`, then `Super-0`, to Activity
positions 1–10 in the Activity bar order. Add Shift to select View slots 1–10
**inside the current Activity**.
For a complete personal keymap with starter bindings disabled, opt in with:
```lisp
(:navigation-keys)
;; Or use another modifier family:
;; (:navigation-keys :modifiers '(:mod-1))
```
This clause expands into ordinary named commands and bindings. It inherits
`:default-modifiers` unless explicitly overridden. For individual custom keys,
commands can call `backend-select-activity-slot` or `backend-select-view-slot`.
The clause uses command ids `:navigate-activity-1` … `:navigate-activity-10`
and `:navigate-view-1` … `:navigate-view-10`.
Numbers are durable navigation metadata, not identities or a ten-Activity
limit. The bar shows Activity names only, such as `Work`; the picker retains each
Activity/View's number for reference. The bar lists Activities in slot order.
Saved Views are arrangements to recall, not subworkspaces: no View name,
Draft label, or dirty marker appears in the bar. Active text
uses the configured `:accent-color` (falling back to `:focused-foreground`);
inactive text uses `:foreground`, without changing background colors.
Click a name to use the same portable/extended navigation as the number keys.
Clicking the already active name restores client
focus without resetting its arrangement. Long lists retain the active tab
where space permits and use a clickable `…` Activity picker for overflow;
title and status areas remain bounded. Custom commands can open the same
picker with `backend-open-activity-picker`.
Activity numbers are contiguous positions: deleting an Activity shifts later
ones left, and new Activities append to the end. Existing order is otherwise
preserved, including across restarts; older sparse assignments are compacted
in their existing bar order. No Activity identity, window, View, or lease changes.
On narrow bars, the `…` picker shows the full order, including clipped tabs.
Drafts have no View number. View slots remain stable and local: new Views take
the lowest free slot and deleting one does not renumber the others. Previously
unnumbered sessions start with Scratch on 1, then Activities alphabetically;
existing Views use library order. Beyond position/slot 10, use name search or
reassign a shortcut. Empty shortcuts do nothing. Session persistence saves
navigation metadata across restarts; without it, the order lasts for this runtime.
New Activities are portable: selection brings their current arrangement here,
including unsaved edits. If already displayed elsewhere, the two monitors swap
arrangements; an inactive Activity changes only the selected monitor. Numbered selection of the current Activity
or View is a no-op, not a reset. Selecting another View recalls its checkpoint.
The menu's explicit `Recall view...` restores a checkpoint even when it was the
last one recalled. For portable Activities, recalling a checkpoint on another
monitor first brings that Activity's arrangement there; lease conflicts are
checked before either step, so cancelling does not swap monitors. A View still
cannot be mounted twice. Extended Activities retain independent Head-local
recall and explicit partial/take lease choices.
Both `backend-activate-activity` and `backend-switch-to-activity` use the same
current working arrangement. Activity navigation never reloads a checkpoint
or retries an old window claim. Moving a window updates its previous live
location even while that Activity is undisplayed. Hidden windows remain hidden
when an arrangement travels; ordinary navigation never gathers the whole pool.
`Super-y → a → e` chooses a monitor to **extend** the current Activity onto.
Its existing portion stays put; a new portion is empty, or an existing portion
resumes. That Activity then navigates independently per Head, even when one
portion becomes empty. `Super-y → a → p` makes the current portion portable
again once other portions are empty and inactive; it preserves saved Views
and hidden windows. Both actions are also searchable commands.
Noninteractive callers use `backend-extend-activity-to-head` and
`backend-make-activity-portable`; `backend-activity-navigation-mode` reports
`:portable` or `:extended`. The startup Scratch desk retains its initial blank
per-Head portions; newly created named Activities default to portable.
Sending a window to a portable Activity targets its real arrangement, even on
another monitor. It appears there if that arrangement is active, otherwise it
is queued there; focus stays with the sending monitor. Extended destinations
still use the sending monitor's portion.
`Super-y → v` contains saved-arrangement actions: `s` saves a new named
checkpoint, `o` recalls one, and `u` chooses a saved view in the current Activity
to replace. Updating requires a named confirmation (Cancel is selected first)
and rejects stale choices if the desk changes. The starter `Super-Shift-s`
binding opens this same picker via `backend-open-view-update-picker`. Advanced
Lisp callers can still explicitly use `backend-update-view` or
`backend-update-current-view`; the UI no longer silently targets the last recall.
For interactive monitor sends, bind a command calling
`(lime.x11:backend-send-focused-client-relatively backend :next)` (the starter
`Super-Shift-e` binding). It transfers same-Activity/shared windows immediately
and opens a Cancel-first confirmation for a private cross-Activity move.
Successful sends follow the window with keyboard focus and route subsequent
commands/new windows to its destination Head. Cancelling leaves focus unchanged.
The same entry point works inside configured menu commands. Strict non-prompting
`backend-transfer-focused-client-*` APIs still reject private cross-Activity
transfers; they never silently change home membership.
Shortcuts can be reassigned through the REPL; occupied assignments swap.
Activity positions must be within 1 through the Activity count, so reordering
cannot create a gap. View slots may still be any positive integer:
```lisp
(lime.dev:with-development-backend (backend)
(lime.x11:backend-set-activity-slot backend "work" 2)
(lime.x11:backend-set-view-slot backend "work/docs" 3)
(lime.x11:backend-navigation-slots backend))
```
## Browser launch profiles
The built-in Waterfox command and the PATH picker use the browser's normal
profile selection, like running `waterfox` in a terminal. Testing alongside
another graphical session can explicitly opt into an isolated profile:
```lisp
(lime.x11:backend-launch-browser backend :test-profile-p t)
```
That test profile lives in Lime's XDG state directory (`lime/waterfox-profile/`)
and intentionally has separate tabs, settings, and extensions. It is never
selected implicitly for normal desktop use.
Super-y opens the compact key map first: `w → +Windows` enters the Window
group, for example. Space or Tab opens command search, Backspace goes up a
menu level, and Escape closes. Tab in search returns to the root map. Large
User groups can be paged with PgUp/PgDn. The dropdown uses the bar's configured
background, foreground, focus, separator, and edge colors, plus the shared
font below; it no longer has a separate hard-coded black/white appearance.
## Configuration versus session state
Your configuration defines policy and commands. Activities, saved Views,
Viewsets, and drafts are automatically stored separately when using `lime/dev`.
Choose `:session-file :default`, a pathname, or `nil` on the session launcher;
the lower-level `lime.config:start-lime` defaults to no session persistence.
Reloading this configuration does not reload or replace the desk. See the
[persistence guide](persistence.md), including the distinction between restarting
Lime and relaunching applications after logout.
## Status widgets
Opt into managed providers in your own configuration; no music/audio services
are started by Lime's starter configuration:
```lisp
(:bar :height 22
:status-providers
(list (lime.x11:make-music-status-provider
:source :mpd :sources '(:mpd "spotify")
:mpd-host "127.0.0.1" :mpd-port 6600 :maximum-width 320
:formatter (lambda (data)
(lime.x11:format-music-status data
:playing-icon "" :paused-icon "" :icon-gap " ")))
(lime.x11:make-volume-status-provider
:foreground nil ; inherit the bar's color, or set "#RRGGBB"
:formatter (lambda (data)
(lime.x11:format-volume-status data
:icon "" :muted-icon "" :icon-gap " ")))
(lime.x11:make-clock-status-provider
:formatter (lambda (data)
(lime.x11:format-clock-status data
:icon "" :icon-gap " " :twenty-four-hour-p nil)))))
```
Subscriptions publish **data**, not display strings. The configuration owns
the ordered provider list, formatters, icons, gaps, optional per-widget colors,
width limits, and click actions. Rendering still owns the overall left/title/
right bar geometry; this is not yet an arbitrary bar-layout DSL.
Each `:formatter` is a fast function of one cached data plist:
| Provider | Snapshot fields |
| --- | --- |
| Music | `:state` (`:playing`, `:paused`, `:stopped`, `:unknown`), `:title` (string/NIL), `:source` (`:mpd` or MPRIS player name) |
| Volume | `:volume` (first-channel integer percentage), `:muted-p`, `:sink` (output name) |
| Clock | `:universal-time`, `:minute`, `:hour` (0–23), `:day`, `:month`, `:year`, `:weekday` (Monday=0), `:daylight-p`, `:timezone` (CL hours west of GMT) |
The clock publishes at minute resolution, even if a custom formatter decodes
seconds from the initial timestamp. NIL source data means unavailable: the
widget is hidden and its formatter is not called until data arrives.
A formatter may return a string, NIL to hide available data, or a
`head-bar-segment`. For example, this configuration can color muted volume
differently without making the source know anything about themes:
```lisp
:formatter
(lambda (data)
(lime.x11:make-head-bar-segment
:text (lime.x11:format-volume-status data :icon-gap " ")
:foreground (if (getf data :muted-p) "#9f9584" "#d8c9ad")))
```
Segment foreground/width/click overrides take precedence over the provider's
defaults; NIL fields inherit those defaults. Provider `:foreground nil`
inherits the bar foreground. Colors must be `#RRGGBB`. Invalid formatter
output or a formatter error hides only that provider and records a diagnostic;
other widgets and subscriptions keep running. Formatters run on the X thread,
outside worker locks, on a defensive copy. They must not block or do I/O.
The `format-*-status` functions are optional presentation helpers in
`src/x11/status-format.lisp`, not part of the subscription protocols. Supply
your own formatter for a different date format, stopped-track display, labels,
or icons (NIL/empty icons omit both icon and gap). Helpers default to one space
for music and two for volume/date. `:twenty-four-hour-p` now belongs to
`format-clock-status`, not `make-clock-status-provider`.
Pass `:icon-foreground "#76929f"` to any of these helpers for a colored icon
with an independently colored label. With this option the helper returns a
`head-bar-segment` instead of a string; the label still inherits the provider
or bar foreground. Without it, the existing string/NIL behavior is unchanged.
For example:
```lisp
:formatter (lambda (data)
(lime.x11:format-volume-status data
:icon-foreground "#76929f" :icon-gap " "))
```
Custom segments can use `:prefix-length` (Lisp character count, not UTF-8
bytes) and `:prefix-foreground` for the same effect. The icon and label remain
one shaped item and one click target; fitting never colors truncation dots as
part of the icon. Text is still plain text, not markup.
Music uses a native MPD connection with `idle player playlist`, or a long-lived
`playerctl --follow` process for an MPRIS player name such as `"spotify"` or
`"brave"`. Default formatting hides stopped players; a custom formatter can
show their retained data. Disconnected players disappear. Paused tracks retain
their pause indicator. The song's pixel budget truncates at grapheme boundaries,
leaving room for the following widgets. Volume uses `pactl subscribe` and
queries the default output only initially and on relevant events. It follows
mute, volume, output changes, and hotplug; the first channel percentage matches
the old dwm script. Queued bursts are coalesced without a debounce delay.
The clock shows local date/time and recomputes the next wall-clock minute
boundary after every wake, not a repeating sleep measured from startup.
It is a date/time widget, not a calendar/appointment application.
The external event bridges need `pactl` (PulseAudio or PipeWire-Pulse) and
`playerctl` (only when using MPRIS); MPD controls use `mpc`. The built-in desktop
sources currently target SBCL/Linux. MPD host/port/password are explicit
constructor options, not inferred by reading a shell script. MPRIS and audio
inherit the session's D-Bus/audio environment; a nested Xephyr session will
normally share the host user's player and volume. No controls are invoked
merely by displaying the widgets.
Click music to play/pause; click volume to toggle mute. Each constructor accepts
an optional `:click-action` function of `(backend head-id)` to customize this.
The clock has no default click action. Bind these commands through your usual
configuration command registry:
```lisp
(lime.x11:backend-cycle-music-source backend) ; :mpd -> "spotify" -> :mpd
(lime.x11:backend-music-command backend :toggle) ; also :next / :previous
```
The widget and controls share the same selected source. An optional `:id`
supports multiple music providers; commands accept the corresponding `:id`.
Reloading a newly evaluated music constructor resets its source to the
configuration's `:source`. Your dwm scripts remain independent.
Managed providers share one cached snapshot across Heads. Subscription failures
clear stale data and reconnect with bounded 1–30 second backoff. Queries have
timeouts, and reload/removal/quit cancel and join old workers, kill/reap their
owned helpers, and close sockets. Set `:status-providers nil` explicitly to
remove all providers; omitting the option preserves the previous setting.
To inspect connection errors through SLY:
```lisp
(lime.dev:with-development-backend (backend)
(lime.x11:backend-status-provider-report backend))
```
Fast plain `(backend head-id)` provider functions returning `head-bar-segment`
values still work. They run during drawing, so must not perform blocking I/O
and do not schedule their own refresh. For a Lisp-only scheduled source,
`make-status-provider` accepts `:id`, `:run`, `:click-action`, and optional
`:formatter`, `:foreground`, and `:maximum-width`. Its worker function uses
`status-publish` (scalar/string/list data, or NIL for unavailable),
`status-wait` (interruptible deadline), and `status-cancelled-p`; it must honor
cancellation, never mutate the WM/backend, and never draw from its thread.
The built-in I/O sources additionally register short resource-cancellation
callbacks through the internal runtime boundary.
Publication and formatting copy data trees, so mutating a producer's input or
a formatter's argument cannot change the worker cache. Formatters are never
called by the publisher. Generic providers default to the identity formatter,
so existing string-publishing providers continue to work. Reports expose both
`:value` (raw data) and `:text` (formatted output), plus source/formatter errors.
Changing a formatter on an existing provider can redraw the cache without
restarting its worker; evaluating new constructor objects during config reload
still replaces those providers using the normal lifecycle.
## Fonts
The bar and dropdown share one font setting, independent of window layouts:
```lisp
(:font :family "monospace" :size 14 :weight :normal)
```
These are the defaults. `:size` is a pixel size (1–96), not points; `:weight`
is `:normal` or `:bold`. Family names are installed Fontconfig font families,
not X11 core-font names or Xft patterns such as `Dina:size=11`.
Comma-separated families give a preferred fallback order, for example:
```lisp
(:font :family "Dina, Fira Code Nerd Font, Noto Color Emoji"
:size 15 :weight :bold)
```
Pango/Cairo supplies antialiasing, Unicode shaping, bidirectional text, and
system font fallback. Japanese, emoji (including color emoji), and Nerd Font
icons work when suitable fonts are installed. A missing family falls back
to a system font; a character missing from every installed font may still
show a missing-glyph box. Lime no longer converts Unicode to `?`.
Font changes reload live, including an already-open dropdown. Its rows resize
to the font; the bar keeps its explicitly configured `:height`, so increase
that too for larger fonts. Fitting uses measured glyphs and truncates only at
grapheme boundaries, preserving combining accents and joined emoji. This is
rendering support, not an input-method/IME implementation.
The first upgrade from the old core-font renderer changes native resources
and structure definitions: restart the Lisp/Xephyr test session once. Later
font **configuration** changes use the ordinary configuration reload.
## Cursors
The desktop arrow, clickable bar/menu hand, and cooperating applications share
a configurable Xcursor theme:
```lisp
(:cursor :theme nil :size nil) ; defaults: follow the system
;; Or choose an installed theme and nominal pixel size explicitly:
(:cursor :theme "Adwaita" :size 24)
```
Use just one `:cursor` clause. `:theme` is a theme directory name, not a font
or file path; `:size` is an integer from 8 to 256, or NIL for automatic sizing.
Explicit config wins over `XCURSOR_THEME` / `XCURSOR_SIZE`, which win over the
current X screen's `Xcursor.theme` / `Xcursor.size` Xresources. No environment
variables are required. Without those, the theme is Xcursor's `default`
(including `default/index.theme` inheritance); size uses `Xft.dpi` when
available, otherwise 24px. The theme supplies its nearest available size.
Installed cursor directories, `XCURSOR_PATH`, and theme inheritance are resolved
by libXcursor. A missing asset/theme may fall back to the system default theme.
If libXcursor, ARGB-capable X Render, or usable cursor images are unavailable,
Lime falls back to built-in X cursors. `:theme :core` requests those explicitly.
Inspect the selected settings and each role's source/fallback reason with
`(lime.x11:backend-cursor-report backend)`; the reported theme is the requested
search theme, not proof that an asset came from that directory.
Theme images retain alpha and hotspots. Animated assets use X Render's server
animation support (or their first frame on older servers), not a WM timer.
Explicit `:theme` / `:size` values also become **session-local application
defaults**. Lime publishes cursor-only XSETTINGS for GTK and compatible toolkits,
updates `Xcursor.theme` / `Xcursor.size` in the current X screen's resources,
and passes `XCURSOR_THEME` / `XCURSOR_SIZE` to programs launched through Lime.
You still configure these in Lisp; no environment setup is required. Lime does
not rewrite GTK/Qt files, dconf, the global launch environment, or another X
session. Application-specific cursors (text, resize, drag, etc.) retain their
roles; applications that deliberately override the theme can still do so.
Lime never replaces an existing XSETTINGS manager and yields if one takes over.
The `:session` entry in `backend-cursor-report` reports `:published`,
`:external-manager`, or `:following-system` under `:xsettings`. With an external
manager, configure its cursor theme too if its toolkit settings disagree.
Only explicit values are published: a NIL field follows its ordinary defaults.
Returning both fields to NIL, choosing `:theme :core`, or exiting Lime releases
its manager and restores the Xresources it still owns, preserving intervening
external edits. Some applications cache cursor settings and need reopening;
in particular, GTK 3 can retain a removed setting until restart even though
changes between explicit themes/sizes update live.
After the initial structural upgrade/restart, theme/size changes reload live,
including a hovered widget or an open dropdown. New cursor resources are
prepared first; a later config failure restores the old resources, while a
successful reload releases them. Session publication participates in the same
rollback. No activity or view is recreated.
## Bar appearance
The bar has three regions: `Lime | activities | layout` on the left,
a left-aligned expanding title section, and optional right-aligned status
providers. Only the title section changes colors with Head focus. Its two
one-pixel edges are deliberately quieter than the colored text separators.
Bar colors and padding can be reloaded without restarting or changing a saved View:
```lisp
(:bar :height 22 :padding-x 10
:background "#060a0c" :foreground "#ffdbb0"
:focused-background "#121f25" :focused-foreground "#ffac54"
:unfocused-background "#0b1216" :unfocused-foreground "#a8adb0"
:separator-color "#b88755"
:attention-color "#ffac54"
:attention-marker "!" :attention-indicator-p t
:title-edge-color "#40535d")
```
These are the defaults. Use `:title-edge-color nil` for the edge-free variant.
Use `:logo-color "#768565"` to color the clickable `Lime` label independently
of the Activity and layout labels. NIL (the default) inherits `:foreground`.
Optional `:accent-color` separates menu key hints, hover/selection markers, and
which-key headings from the focused title's foreground. `:menu-label-color`
separates submenu labels from the bar's separators. Both default to NIL to
preserve the old colors (`:focused-foreground` and `:separator-color`,
respectively; headings retain `:foreground`). For cream titles and labels
with yellow hints, add:
```lisp
:focused-foreground "#dbc9a6"
:accent-color "#ffb054"
:menu-label-color "#dbc9a6"
```
These new roles and separately colored status prefixes require one fresh Lime
process when upgrading from the older renderer. Subsequent color-only config
edits can be reloaded normally.
The attention indicator appears as `! N` ahead of the optional status widgets
only while windows request attention. It counts windows across **all** Activities
and appears on every visible Head bar, including when their Activity is parked.
Click it, or use `Super-y → w → a`, to search requesting windows by Activity
and current title. Activity-search details and hidden-window entries also mark
requests. No new global keybinding is installed.
`:attention-color` and `:attention-marker` control that badge; the marker is
1–16 printable characters. `:attention-indicator-p nil` hides the bar badge,
not the protocol handling or menu. The public cached-data queries
`backend-attention-client-ids` (optional `:activity-id`) and
`backend-client-needs-attention-p` are available to custom Lisp UI/providers.
`backend-open-attention-picker` opens the picker, and `backend-attend-client`
performs the same visit without a menu (`:policy :strict` by default).
See [attention behavior](attention.md) for acknowledgement and lease rules.
`:padding-x` is horizontal text padding in pixels (0–128): the outer left and
right margins and the title section's inset
inside its hairlines. The bar vertically centers a stable sample of the font's
letter body, rather than its invisible line spacing. All fields share a
baseline, so changing titles or adding descenders does not make text jump.
Accents and descenders retain their normal positions relative to that baseline;
increase `:height` if the chosen font or fallback glyphs need more room.
Colors must be `#RRGGBB`; invalid settings fail before changing the running
configuration. A failed installation restores the old bar appearance along
with bindings and window styling. Lisp callers can use `MAKE-HEAD-BAR-STYLE`
and the configuration's `BAR-STYLE` slot directly.
Status providers still supply their own text and optional click actions.
The bar draws contrasting ` | ` delimiters between their items; those
delimiters and truncation dots are not clickable. Appearance settings alone
do not enable providers; opt into the widgets above separately. Providers may use text,
Unicode symbols, or icons covered by the configured/fallback fonts.
## Default layout
`:options :default-layout` chooses `:master-stack` (the default), `:monocle`,
or `:manual` for a **new** working arrangement. This includes the initial
Scratch drafts, new Activities, and newly extended portions. Bringing or resuming
an existing draft or recalling a saved View keeps its layout. Reloading this
option changes future drafts only; it never rearranges your current work.
The configuration is read before startup drafts are created. Without a config
file, `start-lime`/`start-backend` also accept `:default-layout`; with a file,
the file's setting wins.
## New-window and close policy
The `:options` clause also keeps client-flow preference out of the layout
engine. `:new-client-focus` controls whether ordinary newly managed windows
take focus. `:new-tiled-client-position` is `:back` (append after the existing
stack) or `:front` (become master-most). `:close-focus-direction` is
`:previous` (toward master in placement order) or `:next` (farther down the
stack); reaching either edge falls back to the closest client on the other
side. These are the fallback defaults for manual and master/stack: `t`,
`:back`, and `:previous`.
Each layout can override only the flow choices it needs:
```lisp
(:layout-flow :monocle
:new-tiled-client-position :front
:close-focus-direction :next)
```
That is Lime's built-in monocle policy already: each new tiled client becomes
the visible one, and closing it advances to the next underlying client. A
`:layout-flow` clause is useful when you want a different monocle cycle, or a
separate policy for `:manual` or `:master-stack`.
## Resizing a window within a column
Master/stack gives each tiled placement a relative weight, initially `1`.
Two windows weighted `2` and `1` take two-thirds and one-third of their column.
The master area and stack normalize separately, leaving their shared divider
unchanged. This also works with several master windows, and along the other
axis when the layout is rotated.
Configure the commands and step size in your own keymap, for example:
```lisp
(:command grow-tile
:label "Grow window in column"
:action (lambda (backend)
(lime.x11:backend-adjust-focused-tile-weight backend 1/4)))
(:command shrink-tile
:label "Shrink window in column"
:action (lambda (backend)
(lime.x11:backend-adjust-focused-tile-weight backend -1/4)))
(:command reset-tile-size
:label "Reset window column share"
:action (lambda (backend)
(lime.x11:backend-set-focused-tile-weight backend 1)))
(:bind #\d grow-tile :modifiers '(:mod-4 :shift))
(:bind #\n shrink-tile :modifiers '(:mod-4 :shift))
(:bind #\o reset-tile-size :modifiers '(:mod-4 :shift))
```
The adjust command defaults to weights between `1/4` and `4`; callers can
override `:minimum-weight` and `:maximum-weight`. Reset affects the focused
window only. A sole window still fills its column.
Weights belong to placements in the current draft and are checkpointed by
normal View save/update operations. They follow windows through tile moves,
float/unfloat, and layout changes. Monocle ignores the weights. Hiding removes
the placement; showing the window again creates a default-weight placement
unless a saved View restores its previous arrangement.
## Borders and gaps
The `:appearance` clause configures window styling independently of layout
policy. Sizes are non-negative integer pixels and colors are `#RRGGBB` strings.
`:inner-gap` is the space **between** tiled outer frames, not a margin on each
window; `:outer-gap` is the space between tiles and the Head's work-area edge
(below the bar). Border widths are included inside the allotted rectangle,
so they cannot make neighboring tiles overlap.
The implementation lives in `src/x11/appearance.lisp`; the render planner
combines it with ICCCM hints, while your configuration owns the actual values.
Only the globally focused client gets `:focused-border`; windows on other
Heads use `:unfocused-border`. Floating windows have borders but no tiling
gaps. Smart gaps remove both gaps and borders whenever a Head displays just
one tiled window, in manual, master/stack, or monocle. Floats above it keep
their borders and do not count as tiles; explicitly hidden clients and tiles
leased to other Heads do not count either. A second visible tile restores the
configured borders and gaps automatically. Monocle always resolves just one
tile, so it remains undecorated even with several clients in its cycle.
These tiles still respect the bar's work area and normal size hints. True
fullscreen bypasses decoration, the bar reservation, and size hints. Size hints may
leave additional unused space, but cannot enlarge a tile beyond its slot.
On very small tiles, excessive gaps/borders shrink to leave usable content.
Changing these settings and reloading configuration repaints existing windows
without changing layout weights, saved Views, leases, or draft dirty state.
Setting a size to `0` disables that component. Without an `:appearance` clause,
the adapter retains its borderless/gapless defaults; default border colors
are white (`#ffffff`) and gray (`#444444`) if only a width is specified.
## Reloading
`lime.config:reload-configuration` rereads the source file selected at start.
It validates commands, bindings, rules, appearance, and bar inputs before changing the
running backend. If a subsequent installation operation fails, Lime restores
the previous binding set, appearance, and bar settings; Activities, Views, Presentations,
leases, and Viewsets are never re-created or changed by a reload. `:startup`
hooks run only at initial `start-lime`, never on reload.
Because a config file is real Lisp, arbitrary top-level side effects are still
the author’s responsibility. Keep top-level work to helper definitions and
`defconfiguration`; place deliberate launches in a `(:startup (lambda
(backend) ...))` clause when that behavior is wanted.
## Rules, deliberately for styling first
`(:rule ...)` supports `:name`, `:class`, and `:title` matchers (a string or a
one-argument predicate), plus a full `:predicate` function of `(backend
client-id icccm-info)`. Its current actions are `:float t/nil` and `:center t`.
Centering requires floating. The first matching rule wins.
Ordinary clients default to tiled, except fixed-size windows (equal positive
minimum and maximum width **and** height in `WM_NORMAL_HINTS`), which start
centered and floating. This handles startup/updater windows without floating
the application's resizable main window. A matching `:float nil` rule can
override that default. These defaults apply only when a client is first
managed; later hint updates do not reclassify existing placements. The usual
`:new-client-focus` setting applies to fixed-size ordinary clients too.
Rules apply only to ordinary new clients. ICCCM transient dialogs keep their
parent’s Head, Activity, and float behavior; dialog-like EWMH clients retain
their floating policy too. Rules intentionally do **not**
mount a View, change an Activity, or transfer a lease: matching an application
class must not silently alter Lime’s task model. Activity-aware launch/routing
can grow later as a separate policy with explicit collision semantics.