fix(windows/vdisplay): cursor-render state sticky per TARGET across monitor generations
On-glass: 're-setup on swap-chain assign -> 0x0' fired AFTER 'enable=0 stored' — duplicate/successor monitor entries (re-arrival churn) kept their default-true flag and re-declared the hardware cursor right after the composite flip's re-commit, undoing it. The desired state now lives in a per-target static (CURSOR_FORWARD_DESIRED): every arrival inherits it (no generation can resurrect a declare the session turned off), and the flip stamps EVERY live entry matching the target, declaring against whichever has the live worker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,6 +181,24 @@ fn union_modes(into: &mut Vec<Mode>, from: &[Mode]) {
|
|||||||
/// [`MODE_LIST_CAP`] modes.
|
/// [`MODE_LIST_CAP`] modes.
|
||||||
static MODE_HISTORY: Mutex<Vec<(u32, Vec<Mode>)>> = Mutex::new(Vec::new());
|
static MODE_HISTORY: Mutex<Vec<(u32, Vec<Mode>)>> = Mutex::new(Vec::new());
|
||||||
|
|
||||||
|
/// Desired cursor-render state per OS TARGET id (`IOCTL_SET_CURSOR_FORWARD`, the mid-stream
|
||||||
|
/// flip): `false` = composite (do NOT declare the hardware cursor). Kept OUTSIDE the monitor
|
||||||
|
/// entries because those are churned freely (match-window re-arrival resizes, sibling-session
|
||||||
|
/// slot re-creates) — a fresh entry inherits this at ARRIVAL, so no generation can resurrect a
|
||||||
|
/// declare the session turned off. Absent target = default `true` (declare at delivery).
|
||||||
|
static CURSOR_FORWARD_DESIRED: Mutex<Vec<(u32, bool)>> = Mutex::new(Vec::new());
|
||||||
|
|
||||||
|
/// The desired cursor-forward state for `target_id` (default `true`).
|
||||||
|
fn cursor_forward_desired(target_id: u32) -> bool {
|
||||||
|
CURSOR_FORWARD_DESIRED
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(|e| e.into_inner())
|
||||||
|
.iter()
|
||||||
|
.find(|(t, _)| *t == target_id)
|
||||||
|
.map(|(_, on)| *on)
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
/// Record a departing monitor's advertised list for its id ([`MODE_HISTORY`]).
|
/// Record a departing monitor's advertised list for its id ([`MODE_HISTORY`]).
|
||||||
fn remember_modes(id: u32, modes: &[Mode]) {
|
fn remember_modes(id: u32, modes: &[Mode]) {
|
||||||
let mut hist = MODE_HISTORY.lock().unwrap_or_else(|e| e.into_inner());
|
let mut hist = MODE_HISTORY.lock().unwrap_or_else(|e| e.into_inner());
|
||||||
@@ -534,18 +552,36 @@ pub fn resetup_cursor(object: iddcx::IDDCX_MONITOR) {
|
|||||||
/// host logs and keeps its current behavior. Flag update UNDER the lock; DDI call OUTSIDE it
|
/// host logs and keeps its current behavior. Flag update UNDER the lock; DDI call OUTSIDE it
|
||||||
/// (it can re-enter the mode callbacks, which take this lock).
|
/// (it can re-enter the mode callbacks, which take this lock).
|
||||||
pub fn set_cursor_forward(target_id: u32, enable: bool) -> bool {
|
pub fn set_cursor_forward(target_id: u32, enable: bool) -> bool {
|
||||||
// The flip is STATE, not an edge on one monitor generation: set the flag on the matching
|
// The flip is STATE, not an edge on one monitor generation: persist the desired state
|
||||||
// hw-cursor entry even when it has no live worker yet (a re-arrived monitor before the
|
// per TARGET (fresh entries inherit it at arrival) and stamp EVERY live entry matching
|
||||||
// host's channel re-delivery) — the flag then steers the next delivery/re-setup. Only a
|
// the target — duplicate generations coexist during re-arrival churn, and stamping only
|
||||||
// present worker gets the immediate declare/un-declare DDI call.
|
// the first let a sibling's still-true flag re-declare on its next swap-chain assign
|
||||||
|
// (observed on-glass: `re-setup -> 0x0` AFTER `enable=0 stored`). Only a present worker
|
||||||
|
// gets the immediate declare DDI call.
|
||||||
|
{
|
||||||
|
let mut desired = CURSOR_FORWARD_DESIRED
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(|e| e.into_inner());
|
||||||
|
match desired.iter_mut().find(|(t, _)| *t == target_id) {
|
||||||
|
Some(slot) => slot.1 = enable,
|
||||||
|
None => desired.push((target_id, enable)),
|
||||||
|
}
|
||||||
|
}
|
||||||
let found = {
|
let found = {
|
||||||
let mut lock = lock_monitors();
|
let mut lock = lock_monitors();
|
||||||
lock.iter_mut()
|
let mut any = false;
|
||||||
.find(|m| m.target_id == target_id && m.hw_cursor)
|
let mut declare_on: Option<(Option<iddcx::IDDCX_MONITOR>, Option<isize>)> = None;
|
||||||
.map(|m| {
|
for m in lock
|
||||||
m.cursor_forward_on = enable;
|
.iter_mut()
|
||||||
(m.object, m.cursor_worker.as_ref().map(|w| w.data_event()))
|
.filter(|m| m.target_id == target_id && m.hw_cursor)
|
||||||
})
|
{
|
||||||
|
any = true;
|
||||||
|
m.cursor_forward_on = enable;
|
||||||
|
if m.cursor_worker.is_some() {
|
||||||
|
declare_on = Some((m.object, m.cursor_worker.as_ref().map(|w| w.data_event())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
any.then_some(declare_on.unwrap_or((None, None)))
|
||||||
};
|
};
|
||||||
let Some((object, worker_ev)) = found else {
|
let Some((object, worker_ev)) = found else {
|
||||||
return false; // no hw-cursor monitor with this target at all
|
return false; // no hw-cursor monitor with this target at all
|
||||||
@@ -759,11 +795,15 @@ pub fn create_monitor(
|
|||||||
arrival_out.OsAdapterLuid.HighPart,
|
arrival_out.OsAdapterLuid.HighPart,
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
|
let inherit = cursor_forward_desired(target_id);
|
||||||
let mut lock = lock_monitors();
|
let mut lock = lock_monitors();
|
||||||
if let Some(m) = lock.iter_mut().find(|m| m.id == id) {
|
if let Some(m) = lock.iter_mut().find(|m| m.id == id) {
|
||||||
m.target_id = target_id;
|
m.target_id = target_id;
|
||||||
m.adapter_luid_low = luid_low;
|
m.adapter_luid_low = luid_low;
|
||||||
m.adapter_luid_high = luid_high;
|
m.adapter_luid_high = luid_high;
|
||||||
|
// The mid-stream render flip survives entry churn: the fresh entry starts at the
|
||||||
|
// session's DESIRED state, not the declare-at-delivery default.
|
||||||
|
m.cursor_forward_on = inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((id, target_id, luid_low, luid_high))
|
Some((id, target_id, luid_low, luid_high))
|
||||||
|
|||||||
Reference in New Issue
Block a user