Initial Lime window manager
This commit is contained in:
674
docs/configuration.md
Normal file
674
docs/configuration.md
Normal file
@@ -0,0 +1,674 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user