EvanZhu0721 / 公开仓库
SBMS
SBMS complete the calculation rules for the default logical desktop topology relationship and the physical dimensions of the displays in the Windows multi-screen collaboration system.
文件
../ architecture.md2.3 KBconfiguration.md4.9 KBgeometry.md2.4 KBlogical-desktop-size-observation.md3.4 KBmapping-plan.md4.9 KBdocs/configuration.md4.9 KB
1
# Configuration profiles2
3
SBMS stores up to three persistent configuration profiles in4
`%LOCALAPPDATA%\SBMS\config-profiles-v1.json`. The complete profile collection,5
the active profile identifier, every profile revision and every nested6
`AppConfig` are committed as one pretty-printed JSON document. Writes use a7
cross-process mutex, a same-directory temporary file, `sync_all`, and a8
write-through atomic replacement.9
10
The first profile operation imports the existing `config-v2.json` as the11
`default` profile. The legacy file is left unchanged. If the old file is12
malformed, unsupported or semantically invalid, initialization fails without13
creating or replacing the profile store. A missing old file produces the14
normal default configuration.15
16
Profile identifiers contain 1–32 ASCII letters, digits, underscores or17
hyphens. They are compared case-insensitively. A collection contains one to18
three profiles and exactly one active profile. Display IDs inside a profile are19
Windows device identities and may need to be changed after importing onto a20
different computer.21
22
## CLI interface23
24
```text25
sbms config path26
sbms config list27
sbms config show [<profile>]28
sbms config save <profile>29
sbms config import <profile> <file.json> [--replace] [--activate]30
sbms config export <profile> <file.json> [--force]31
sbms config activate <profile>32
sbms config delete <profile>33
sbms config reload34
sbms config set-target <monitor-device-path>35
sbms config clear-target36
sbms config reset37
```38
39
- `path` prints the profile-store path.40
- `list` prints a JSON array containing each profile ID, revision and active41
state.42
- `show` prints the active profile, or the named profile, as a standalone43
`AppConfig` JSON document.44
- `save` copies the active configuration into the named profile. It creates a45
new profile or replaces the named profile.46
- `import` reads a standalone `AppConfig` JSON file, strictly validates its47
schema, version and semantics, and commits it only after validation succeeds.48
Existing profiles require `--replace`; `--activate` also makes the imported49
profile active.50
- `export` writes a standalone `AppConfig` JSON file. Existing destinations are51
rejected atomically unless `--force` is present. The profile-store path can52
never be used as an export destination, even with `--force`.53
- `activate` atomically changes the active profile and requests a live reload.54
- `delete` rejects the active profile and unknown profiles.55
- `reload` requests that the running tray reload the already-active profile.56
- `set-target`, `clear-target` and `reset` update only the active profile and57
request a live reload.58
59
A successful configuration commit is not rolled back when the tray is not60
running. CLI output reports `reload=not-running`; the configuration is loaded61
on the next tray or mapping start.62
63
## JSON exchange format64
65
Import and export use the existing standalone `AppConfig` version 2 format,66
not the internal multi-profile collection:67
68
```json69
{70
"version": 2,71
"groups": [72
{73
"id": 0,74
"route": {75
"kind": "mirror",76
"target_id": "<monitor-device-path>"77
}78
}79
],80
"selected_group_id": 081
}82
```83
84
Unknown fields, unsupported versions, empty or oversized group collections,85
duplicate group IDs or mirror targets, invalid geometry and invalid selected86
group IDs are rejected before any persistent state changes.87
88
## Hot reload contract89
90
The CLI signals the running tray only after the profile transaction has been91
committed. When no mapping is running, the tray reloads the profile and replaces92
its editable in-memory drafts in one UI-thread operation. Existing telemetry is93
preserved for unchanged group IDs.94
95
When a mapping is running or changing state, reload is deferred until the96
controller reports both `running=false` and `busy=false`. Reload never restarts,97
stops or mutates the active mapping. It changes the configuration used by the98
next mapping start.99
100
Each profile has a monotonically increasing revision. UI saves use a101
profile-ID-and-revision compare-and-swap. A stale UI draft therefore cannot102
overwrite a profile imported or activated by another process; the user must103
reload before saving again.104
105
## Rust interface106
107
The public API is exported from `sbms::config`:108
109
- `ConfigProfileStore::default_store()` locates the collection.110
- `list`, `load_active` and `load_profile` return validated persistent state.111
- `save_profile`, `save_active_as`, `activate`, `delete` and `update_active`112
perform locked atomic transactions.113
- `save_active_if_revision` is the compare-and-swap save operation for a pinned114
UI session.115
- `ConfigStore::load_strict` validates standalone import files; `save` writes116
standalone exports atomically.117
- `sbms::control::signal_config_reload` and `listen_for_config_reload` expose118
the cross-process live-reload notification.119
120
The profile collection is the runtime source of truth after initialization.121
`config-v2.json` remains only as the preserved migration source.122