Initial Lime window manager
This commit is contained in:
149
docs/persistence.md
Normal file
149
docs/persistence.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# Session persistence
|
||||
|
||||
Lime saves the **desk**, separately from your Lisp configuration: Activities,
|
||||
named Views and Viewsets, current and cached drafts, hidden/shown membership,
|
||||
home Activity and shared eligibility, layout order/options/weights, floating rectangles, focus, dirty flags, partial
|
||||
View state, and logical Head selections. It does not serialize CLX objects,
|
||||
fonts, key bindings, callbacks, running processes, or application contents.
|
||||
|
||||
`lime/dev` enables this automatically. The normal location is
|
||||
`$XDG_STATE_HOME/lime/sessions/`, falling back to `~/.local/state/lime/sessions/`.
|
||||
Files are separate for each DISPLAY (for example `display-_3A0.sexp` for `:0`,
|
||||
`display-_3A99.sexp` for `:99`). Your nested test desk does not overwrite your
|
||||
normal desktop's desk. A lifetime writer lock prevents two backends from using
|
||||
one explicit file simultaneously.
|
||||
|
||||
Select a different file or disable persistence with launcher keywords—not an
|
||||
additional Lime environment variable:
|
||||
|
||||
```lisp
|
||||
(lime.dev:run-development-session
|
||||
:config-file #p"/home/adan/.config/lime/config.lisp"
|
||||
:session-file #p"/home/adan/.local/state/lime/my-desk.sexp")
|
||||
|
||||
;; For a disposable test:
|
||||
(lime.dev:start-development-session :display-name ":99" :session-file nil)
|
||||
```
|
||||
|
||||
The lower-level `lime.x11:start-lime` / `start-backend` APIs default to no
|
||||
persistence; pass `:session-file :default` or a pathname to enable it. An
|
||||
explicit `:world` and a session file are mutually exclusive startup sources.
|
||||
Configuration reload does not change the selected session file.
|
||||
Configured startup hooks still run normally; hooks that create named Activities
|
||||
should tolerate those Activities already existing in a restored desk.
|
||||
|
||||
`scripts/run-xephyr-dev` supports `--session-file PATH` and `--no-session`.
|
||||
Without either option it uses the display-specific default.
|
||||
|
||||
## What a restart restores
|
||||
|
||||
On a **Lime-only restart within the same X server**, surviving clients reconnect
|
||||
to their Activities, checkpoints, and drafts—including unmapped hidden clients.
|
||||
The adapter verifies a random server token plus each client's XID and random
|
||||
window token. An XID, title, WM_CLASS, or PID alone is never enough: reused ids
|
||||
must not attach an unrelated application's window to an old checkpoint.
|
||||
|
||||
On **logout, reboot, or X-server replacement**, the names and non-client layout
|
||||
settings survive, but the old windows do not. Missing clients are retired from
|
||||
the saved arrangements; an empty View remains a named checkpoint. This first
|
||||
version does **not** relaunch applications, restore terminal/browser contents,
|
||||
reserve placeholder slots for dead windows, or guess how newly launched
|
||||
windows should fill an old arrangement. Application/session-relaunch rules are
|
||||
a separate feature.
|
||||
|
||||
Current physical Heads remain authoritative. Matching logical Head ids resume
|
||||
their saved selections. Drafts from removed Heads are suspended, not moved to
|
||||
an arbitrary other monitor or deleted; new Heads get ordinary blank drafts.
|
||||
Viewsets retain their logical mappings even when an output is absent. Leases
|
||||
are reconstructed and validated, never accepted as serialized ownership objects.
|
||||
|
||||
## Saving and failures
|
||||
|
||||
Autosave observes model/focus changes on the owner thread. After 750 ms of
|
||||
quiet, or five seconds of ongoing edits, it captures a validated plain-data
|
||||
snapshot. Active pointer drags postpone capture until release. A single worker
|
||||
writes snapshots off the X event thread, keeping only the latest pending one.
|
||||
An unchanged desk produces no disk writes. A crash can lose edits since the
|
||||
last completed autosave; this is a snapshot system, not a per-operation journal.
|
||||
|
||||
Normal shutdown flushes the final valid desk. A normal developer restart saves
|
||||
**before closing X**; if that save fails, restart aborts with the old backend
|
||||
still available for repair. Restart after an event-loop fault instead uses the
|
||||
last good disk snapshot, avoiding an automatic save of partially mutated state.
|
||||
`resume-development-session` still uses the retained in-memory world, not disk.
|
||||
|
||||
For an explicit flush or inspection from SLY:
|
||||
|
||||
```lisp
|
||||
(lime.dev:save-development-session)
|
||||
(lime.dev:with-development-backend (backend)
|
||||
(lime.x11:backend-session-status backend))
|
||||
```
|
||||
|
||||
The status includes the file, restore/missing-client report, last-save time,
|
||||
submitted/completed write generations, and restore/snapshot/write errors. Disk
|
||||
write failures are nonfatal to ordinary operation and retry with five-second
|
||||
backoff; explicit flush reports the error to its caller. Invalid in-memory
|
||||
snapshots block automatic saving until repaired and explicitly saved again.
|
||||
|
||||
Files are mode `0600`. Writes use a private temporary file in the same
|
||||
directory, file `fsync`, atomic rename, and directory `fsync` on Linux. A failed
|
||||
write before rename leaves the old file untouched; readers cannot see a partial
|
||||
replacement. The format is versioned, bounded plain data. Reading never calls
|
||||
`load`/`eval`, disables read-time evaluation, and rejects reader dispatch,
|
||||
circular structures, excessive nesting, and trailing data. Unknown versions,
|
||||
invalid relationships, duplicate leases, or malformed files are rejected before
|
||||
replacing the startup world.
|
||||
|
||||
A rejected file is **not overwritten**: Lime starts a fresh usable desk with
|
||||
autosave blocked and reports the error. Inspect/back up the file first. To
|
||||
deliberately replace it with the current desk:
|
||||
|
||||
```lisp
|
||||
(lime.dev:save-development-session :replace-invalid-p t)
|
||||
```
|
||||
|
||||
This does not override another session's file lock. Stop that other session or
|
||||
choose a different path. Back up important files separately; atomic writes are
|
||||
not a backup/history system. Names and window associations can be private even
|
||||
though the file contains no application buffers or process contents.
|
||||
|
||||
## Installing and testing
|
||||
|
||||
Membership support adds a client structure slot and ASDF components. Relaunch
|
||||
Lime in a fresh Lisp process; configuration reload alone cannot install it.
|
||||
World snapshots use schema version 3. The X11 session envelope uses
|
||||
version 3, with ordered Activity positions, stable local-View shortcut slots,
|
||||
last-arrangement history, and portable/extended Activity navigation policy.
|
||||
Version-1/2 envelopes remain readable. Old Activities with one substantive
|
||||
arrangement become portable; genuinely split arrangements remain extended.
|
||||
This inference happens before missing-client pruning, preserving multi-monitor
|
||||
intent across reboot. Old empty drafts are retained, not recalled or deleted.
|
||||
Version-1 envelopes also receive initial shortcut assignments. Migration does
|
||||
not move a window or alter what the Heads display. Navigation metadata is
|
||||
validated before a saved world is installed. An explicit extended choice stays
|
||||
extended when all its windows close; it is never re-inferred from window count.
|
||||
Version 1 worlds load with all clients private. Version 1/2 worlds consolidate
|
||||
historical drafts into one current arrangement per Activity/Head. Active state
|
||||
wins, then saved navigation history, then the newest generated Presentation
|
||||
id. Actual leases win duplicated window locations; missing checkpoint slots
|
||||
are not live locations. Windows and saved Views are retained, and old source
|
||||
labels are detached where a checkpoint was recalled elsewhere. Migration is
|
||||
performed on a candidate world before installation; version 3 rejects duplicate
|
||||
live locations rather than silently repairing them. Keep a backup before
|
||||
upgrading or downgrading; older Lime builds may not read these schema versions.
|
||||
The same-X-server identity rules above still apply to moved/shared clients.
|
||||
|
||||
```lisp
|
||||
(asdf:test-system "lime/persistence")
|
||||
```
|
||||
|
||||
```sh
|
||||
sbcl --non-interactive --load test/x11-persistence-smoke.lisp
|
||||
sbcl --non-interactive --load test/x11-membership-smoke.lisp
|
||||
```
|
||||
|
||||
The smoke test uses its own Xvfb, temporary state files, and fake client windows.
|
||||
It tests graceful/abrupt restart, hidden clients, dirty checkpoints, partial
|
||||
leases, changed topology/identity, corrupt files, disk failures, and the actual
|
||||
developer restart path. It does not connect to your running desktop.
|
||||
Reference in New Issue
Block a user