150 lines
7.8 KiB
Markdown
150 lines
7.8 KiB
Markdown
# 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.
|