aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJJ Liebig <jonathan.liebig@gmail.com>2026-09-14 23:21:54 +0200
committerGitHub <noreply@github.com>2026-09-14 23:21:54 +0200
commit734faae72a3910ef67f83c372c83678bdb7405c0 (patch)
treeacda132c59057d8db51164e9c26be5f7a980aa4b /src
parente61206cc9d4e13688d2baed4a866c2bcd6ec0736 (diff)
fix(windows): batch input trace diagnostics (#4146)
refs #3932
Diffstat (limited to 'src')
-rw-r--r--src/client/input/windows_vti.rs94
1 files changed, 72 insertions, 22 deletions
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<PlatformInputItem>,
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<crate::protocol::ClientInputEvent>,
+ 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)]
@@ -83,6 +115,13 @@ enum WindowsInputItems {
#[cfg(windows)]
#[derive(Default)]
+struct WindowsInputTraceBatch {
+ raw_keys: Vec<WindowsKeyRecord>,
+ mapped_event_groups: Vec<Vec<crate::protocol::ClientInputEvent>>,
+}
+
+#[cfg(windows)]
+#[derive(Default)]
struct WindowsInputHandoff {
pending: VecDeque<Vec<crate::protocol::ClientInputEvent>>,
backpressured: bool,
@@ -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<PlatformInputItem> {
- 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<char> {
None
}
-#[cfg(any(windows, test))]
+#[cfg(windows)]
fn windows_input_trace_enabled() -> bool {
std::env::var_os("HERDR_WINDOWS_INPUT_TRACE").is_some()
}
@@ -1560,6 +1588,28 @@ mod tests {
#[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() {
use crate::protocol::{
ClientInputEvent, ClientKeyCode, ClientKeyKind, ClientKeySource, ClientMouseButton,