From 734faae72a3910ef67f83c372c83678bdb7405c0 Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Mon, 14 Sep 2026 23:21:54 +0200 Subject: fix(windows): batch input trace diagnostics (#4146) refs #3932 --- src/client/input/windows_vti.rs | 94 +++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 22 deletions(-) (limited to 'src/client') diff --git a/src/client/input/windows_vti.rs b/src/client/input/windows_vti.rs index 0a00c146..95e01d4b 100644 --- a/src/client/input/windows_vti.rs +++ b/src/client/input/windows_vti.rs @@ -24,17 +24,33 @@ pub(super) fn raw_console_reader_loop( let mut handoff = WindowsInputHandoff::default(); while !should_quit.load(Ordering::Acquire) { - match windows_console_input_items(handle, &mut mapper) { + let mut trace = windows_input_trace_enabled().then(WindowsInputTraceBatch::default); + match windows_console_input_items(handle, &mut mapper, trace.as_mut()) { WindowsInputItems::Items(items) => { - process_platform_input_items(items, &mut pump, &mut handoff); + process_platform_input_items(items, &mut pump, &mut handoff, trace.as_mut()); } WindowsInputItems::Idle => { - process_platform_input_items(mapper.idle(), &mut pump, &mut handoff); - handoff.push(pump.idle()); + process_platform_input_items( + mapper.idle(), + &mut pump, + &mut handoff, + trace.as_mut(), + ); + push_platform_input_events(pump.idle(), &mut handoff, trace.as_mut()); } WindowsInputItems::Closed => return, } - if !handoff.try_flush(&event_tx) { + let handoff_open = handoff.try_flush(&event_tx); + if let Some(trace) = trace + .filter(|trace| !trace.raw_keys.is_empty() || !trace.mapped_event_groups.is_empty()) + { + tracing::info!( + raw_keys = ?trace.raw_keys, + mapped_event_groups = ?trace.mapped_event_groups, + "windows input trace: input batch" + ); + } + if !handoff_open { return; } } @@ -45,10 +61,26 @@ fn process_platform_input_items( items: Vec, pump: &mut WindowsInputPump, handoff: &mut WindowsInputHandoff, + mut trace: Option<&mut WindowsInputTraceBatch>, ) { for item in items { - handoff.push(pump.process(item)); + push_platform_input_events(pump.process(item), handoff, trace.as_deref_mut()); + } +} + +#[cfg(windows)] +fn push_platform_input_events( + events: Vec, + handoff: &mut WindowsInputHandoff, + trace: Option<&mut WindowsInputTraceBatch>, +) { + if events.is_empty() { + return; } + if let Some(trace) = trace { + trace.mapped_event_groups.push(events.clone()); + } + handoff.push(events); } #[cfg(windows)] @@ -81,6 +113,13 @@ enum WindowsInputItems { Closed, } +#[cfg(windows)] +#[derive(Default)] +struct WindowsInputTraceBatch { + raw_keys: Vec, + mapped_event_groups: Vec>, +} + #[cfg(windows)] #[derive(Default)] struct WindowsInputHandoff { @@ -94,9 +133,6 @@ impl WindowsInputHandoff { if events.is_empty() { return; } - if windows_input_trace_enabled() { - tracing::info!(?events, "windows input trace: client input events"); - } if self.backpressured { self.push_backpressured(events); } else { @@ -185,6 +221,7 @@ fn windows_mouse_motion_can_replace( fn windows_console_input_items( handle: windows_sys::Win32::Foundation::HANDLE, mapper: &mut WindowsInputMapper, + mut trace: Option<&mut WindowsInputTraceBatch>, ) -> WindowsInputItems { const WAIT_OBJECT_0: u32 = 0; const WAIT_TIMEOUT: u32 = 258; @@ -212,6 +249,9 @@ fn windows_console_input_items( let mut items = Vec::new(); for record in records.iter().take(read as usize) { if let Some(record) = windows_console_input_record_from_os(*record) { + if let (Some(trace), WindowsInputRecord::Key(key)) = (trace.as_deref_mut(), record) { + trace.raw_keys.push(key); + } items.extend(mapper.translate(record)); } } @@ -662,18 +702,6 @@ impl WindowsInputMapper { } fn translate_key(&mut self, key: WindowsKeyRecord) -> Vec { - if windows_input_trace_enabled() { - tracing::info!( - key_down = key.key_down, - repeat_count = key.repeat_count, - virtual_key_code = key.virtual_key_code, - virtual_scan_code = key.virtual_scan_code, - unicode = key.unicode, - control_key_state = key.control_key_state, - "windows input trace: console key record" - ); - } - if !self.key_record_can_emit_event(key) { return Vec::new(); } @@ -1341,7 +1369,7 @@ fn resolve_ctrl_oem_char(key: WindowsKeyRecord) -> Option { None } -#[cfg(any(windows, test))] +#[cfg(windows)] fn windows_input_trace_enabled() -> bool { std::env::var_os("HERDR_WINDOWS_INPUT_TRACE").is_some() } @@ -1558,6 +1586,28 @@ mod tests { .collect() } + #[cfg(windows)] + #[test] + fn windows_input_trace_preserves_mapped_event_groups() { + let groups = vec![ + vec![crate::protocol::ClientInputEvent::FocusGained], + vec![ + crate::protocol::ClientInputEvent::FocusLost, + crate::protocol::ClientInputEvent::FocusGained, + ], + ]; + let mut trace = WindowsInputTraceBatch::default(); + let mut handoff = WindowsInputHandoff::default(); + + for events in &groups { + push_platform_input_events(events.clone(), &mut handoff, Some(&mut trace)); + } + push_platform_input_events(Vec::new(), &mut handoff, Some(&mut trace)); + + assert_eq!(trace.mapped_event_groups, groups); + assert_eq!(handoff.pending, VecDeque::from(groups)); + } + #[cfg(windows)] #[test] fn windows_input_handoff_keeps_input_while_the_client_queue_is_full() { -- cgit v1.2.3