feat(vdisplay): KWin per-slot output naming for persistent scaling (Stage 3)
The KWin backend names its output Virtual-punktfunk-<id> from the client's stable identity slot, so KWin persists per-output config (scale/mode) by name in kwinoutputconfig.json and reapplies that client's scaling on reconnect — the KDE scaling ask. Also fixes the latent clash where two concurrent sessions both used Virtual-punktfunk (topology name-matching now uses the per-slot name). - identity::global() + resolve_slot(fp, mode, default) — the shared persisted map (Windows manager dropped its own field; both use the global — never same-process). Default identity is per-platform: PerClient on Windows, Shared on Linux, so unconfigured hosts keep today's behavior (Linux = single 'punktfunk' name). - KwinDisplay carries the client fp (set_client_identity), computes the per-slot name, threads it through the stream_virtual_output name + the topology helpers (set_custom_refresh / apply_virtual_primary[_only] / other_enabled_outputs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -169,10 +169,10 @@ pub(crate) struct VirtualDisplayManager {
|
||||
/// The current IDD-push session's stop flag; a new connection signals the prior one to release its
|
||||
/// monitor before the fresh one is created (was the `IDD_SESSION_STOP` global in `punktfunk1`).
|
||||
idd_session_stop: Mutex<Option<Arc<AtomicBool>>>,
|
||||
/// Persistent per-client (cert-fingerprint) → stable monitor-id map. A monitor CREATE resolves the
|
||||
/// connecting client's id here, so the client keeps the same EDID serial + IddCx ConnectorIndex across
|
||||
/// reconnects and Windows reapplies its saved per-monitor config (DPI scaling). See [`super::identity`].
|
||||
identity_map: Mutex<super::identity::DisplayIdentityMap>,
|
||||
// The per-client stable monitor-id map is now the process-wide `super::identity::global()`
|
||||
// (shared with the Linux KWin backend's per-slot naming — never same-process). A monitor CREATE
|
||||
// resolves the client's id via `identity::resolve_slot`, so it keeps the same EDID serial + IddCx
|
||||
// ConnectorIndex across reconnects and Windows reapplies its saved per-monitor DPI scaling.
|
||||
}
|
||||
|
||||
static VDM: OnceLock<VirtualDisplayManager> = OnceLock::new();
|
||||
@@ -188,7 +188,6 @@ pub(crate) fn init(driver: Box<dyn VdisplayDriver>) -> &'static VirtualDisplayMa
|
||||
state: Mutex::new(MgrState::Idle),
|
||||
setup_lock: Mutex::new(()),
|
||||
idd_session_stop: Mutex::new(None),
|
||||
identity_map: Mutex::new(super::identity::DisplayIdentityMap::load()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -528,20 +527,13 @@ impl VirtualDisplayManager {
|
||||
// Resolve the connecting client's STABLE per-client monitor id (so Windows reapplies its saved
|
||||
// per-monitor config — DPI scaling — on reconnect); `None`/anonymous → 0 = the driver
|
||||
// auto-allocates the lowest-free id (the original slot-based behavior). The `identity` policy
|
||||
// picks the key: per-client (fingerprint) or per-client-mode (fingerprint + resolution).
|
||||
let per_client_mode = matches!(
|
||||
crate::vdisplay::policy::prefs()
|
||||
.configured_effective()
|
||||
.map(|e| e.identity),
|
||||
Some(crate::vdisplay::policy::Identity::PerClientMode)
|
||||
);
|
||||
let preferred_id = client_fp
|
||||
.map(|fp| {
|
||||
let key =
|
||||
super::identity::identity_key(fp, (mode.width, mode.height), per_client_mode);
|
||||
self.identity_map.lock().unwrap().resolve(&key)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
// picks per-client vs per-client-mode; Windows defaults to PerClient (its historical behavior).
|
||||
let preferred_id = super::identity::resolve_slot(
|
||||
client_fp,
|
||||
(mode.width, mode.height),
|
||||
crate::vdisplay::policy::Identity::PerClient,
|
||||
)
|
||||
.unwrap_or(0);
|
||||
// SAFETY: `create_monitor`'s own `# Safety` contract guarantees `dev` is the live control
|
||||
// handle; we forward it unchanged to `add_monitor`, whose precondition is exactly that.
|
||||
// `resolve_render_pin()` returns an `Option<LUID>` by value (plain `Copy`), so no borrowed
|
||||
|
||||
Reference in New Issue
Block a user