test(resize): extract + unit-test the live-reconfigure gate (H1 gamescope / H5 per-client-mode)
The Linux §6 on-glass matrix can validate the gamescope must-REJECT behavior only on native-gamescope hardware (the NVIDIA dev box fails headless GBM allocation — a nested Hyprland/sway/gamescope output comes up 0×0), so pin the gate down deterministically instead: extract the inline `live_reconfig_ok` decision into a pure `reconfig_allowed(compositor, per_client_mode)` and test it — gamescope rejects in every identity mode, a per-client-mode policy rejects on every backend, and all other compositors (plus the synthetic protocol-test source) with the default identity accept. Also fmt-normalizes the re_add block from the prior commit (whitespace only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -573,24 +573,28 @@ impl VirtualDisplayManager {
|
||||
};
|
||||
// SAFETY: `dev` is the handle `ensure_device()` returned above; `re_add` touches the
|
||||
// live topology under the held `state` lock. `mon` is owned here (removed from the map).
|
||||
let new_mon = match unsafe { self.re_add(dev, &mut inner, slot, &mon, mode, client_hdr) }
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
// The re-arrival failed — put the OLD monitor back so the session keeps
|
||||
// streaming its current mode (the control task already acked the switch; the
|
||||
// rebuild reuses the old target and Fix 2's corrective ack tells the client the
|
||||
// resolution didn't change). Its `gen`/`refs` are intact, so leases stay valid.
|
||||
inner.slots.insert(slot, SlotState::Active { mon, refs });
|
||||
return Err(e).context("mid-stream resize re-arrival");
|
||||
}
|
||||
};
|
||||
let new_mon =
|
||||
match unsafe { self.re_add(dev, &mut inner, slot, &mon, mode, client_hdr) } {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
// The re-arrival failed — put the OLD monitor back so the session keeps
|
||||
// streaming its current mode (the control task already acked the switch; the
|
||||
// rebuild reuses the old target and Fix 2's corrective ack tells the client the
|
||||
// resolution didn't change). Its `gen`/`refs` are intact, so leases stay valid.
|
||||
inner.slots.insert(slot, SlotState::Active { mon, refs });
|
||||
return Err(e).context("mid-stream resize re-arrival");
|
||||
}
|
||||
};
|
||||
// `re_add` preserved `gen`, so both the old session's lease and this new one match on
|
||||
// release. +1 ref for the new (build-then-drop overlap) lease.
|
||||
let out = self.output_for(slot, &new_mon, quit);
|
||||
inner
|
||||
.slots
|
||||
.insert(slot, SlotState::Active { mon: new_mon, refs: refs + 1 });
|
||||
inner.slots.insert(
|
||||
slot,
|
||||
SlotState::Active {
|
||||
mon: new_mon,
|
||||
refs: refs + 1,
|
||||
},
|
||||
);
|
||||
// The width changed — re-arrange the group so auto-row siblings don't overlap the
|
||||
// resized display (no-op for a single member).
|
||||
self.apply_group_layout(&mut inner);
|
||||
@@ -1053,7 +1057,10 @@ impl VirtualDisplayManager {
|
||||
// SAFETY: `dev` is the live control handle (this fn's contract); `&old.key` borrows the
|
||||
// still-owned `MonitorKey`, alive across the synchronous IOCTL.
|
||||
if let Err(e) = unsafe { self.driver.remove_monitor(dev, &old.key) } {
|
||||
tracing::warn!(old_target = old.target_id, "re-arrival REMOVE failed (continuing to ADD): {e:#}");
|
||||
tracing::warn!(
|
||||
old_target = old.target_id,
|
||||
"re-arrival REMOVE failed (continuing to ADD): {e:#}"
|
||||
);
|
||||
}
|
||||
// Let the OS finish the ASYNC monitor departure before the ADD — a back-to-back REMOVE→ADD
|
||||
// races the teardown and the ADD is rejected under churn (same 400 ms settle as the reconnect
|
||||
|
||||
Reference in New Issue
Block a user