From 2022bbcca03649cf2b12abb70bd1b73549cb424b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E5=8D=9A=E6=96=87?= <1323515022@qq.com> Date: Sat, 31 Oct 2026 17:35:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(agent-core):=20InputInjector=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20mouse=5Fmove=5Frel=20=E9=BB=98=E8=AE=A4=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E4=B8=8E=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 默认实现不注入但打印日志(与 v3 的 text() 同一条纪律):Linux/X11 后端本轮 未实现相对移动,用户用相对模式操作 Linux 被控端时「指针纹丝不动」不能是静默的。 相对事件不得被降级成绝对移动——单测显式断言默认实现不产生任何注入调用 (否则 Linux 端会把「相对增量」当成「绝对坐标」解释,指针乱跳)。 --- agent-core/src/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/agent-core/src/lib.rs b/agent-core/src/lib.rs index f30de67..adf6056 100644 --- a/agent-core/src/lib.rs +++ b/agent-core/src/lib.rs @@ -63,6 +63,16 @@ pub trait InputInjector: Send + Sync + 'static { units.len() ); } + + /// 协议 v4 的相对鼠标移动(单位:被控端桌面物理像素)。 + /// + /// 与 [`InputInjector::text`] 同一条纪律:**默认实现不注入,但必须留下可见痕迹**。 + /// Linux/X11 后端本轮未实现相对移动(XTest 的相对语义要自己维护指针状态, + /// 与 Windows 的 `GetCursorPos`+绝对注入不同构,另立循环)。用户用相对模式 + /// 操作 Linux 被控端时「指针纹丝不动」不能是静默的,日志必须说明是没实现。 + fn mouse_move_rel(&self, dx: i16, dy: i16) { + eprintln!("[agent-core] 相对鼠标注入未实现(忽略 dx={dx} dy={dy})"); + } } /// 把一条协议输入事件分发到注入后端。 @@ -85,6 +95,9 @@ pub fn dispatch_input(inj: &dyn InputInjector, ev: InputEvent) { } inj.text(&units); } + // v4:相对移动原样下发(含负值)。clamp 与像素换算由注入后端负责—— + // 只有它知道被控端桌面的真实像素尺寸(`agent-core` 是平台无关层)。 + InputEvent::MouseMoveRel { dx, dy } => inj.mouse_move_rel(dx, dy), } } @@ -588,6 +601,47 @@ mod tests { ); } + /// v4:相对移动必须被分发,**负值原样到达后端**(负值是相对语义的核心, + /// 绝对坐标是无符号的,这条路径是新引入的有符号通道)。 + #[test] + fn rel_move_reaches_backend_with_signed_deltas() { + struct RelInjector { + got: Mutex>, + } + impl InputInjector for RelInjector { + fn mouse_move(&self, _x: u16, _y: u16) {} + fn mouse_button(&self, _b: MouseButton, _down: bool) {} + fn wheel(&self, _dy: i32) {} + fn key(&self, _s: u16, _e: bool, _d: bool) {} + fn mouse_move_rel(&self, dx: i16, dy: i16) { + self.got.lock().unwrap().push((dx, dy)); + } + } + let inj = RelInjector { + got: Mutex::new(Vec::new()), + }; + dispatch_input(&inj, InputEvent::MouseMoveRel { dx: 12, dy: -34 }); + dispatch_input(&inj, InputEvent::MouseMoveRel { dx: -1, dy: 0 }); + assert_eq!( + *inj.got.lock().unwrap(), + vec![(12, -34), (-1, 0)], + "相对增量(含负值)必须原样到达注入后端" + ); + } + + /// 后端**没有**实现 mouse_move_rel 时不得 panic,且相对事件不得被误路由成 + /// 绝对移动(Linux/X11 后端的真实形态:默认实现 + 日志)。 + #[test] + fn rel_move_default_backend_does_not_panic() { + let inj = MockInjector::default(); + dispatch_input(&inj, InputEvent::MouseMoveRel { dx: 5, dy: 5 }); + // 默认实现只打日志,不应产生任何注入调用(尤其不能变成 move/绝对移动) + assert!( + inj.take().is_empty(), + "默认实现不得把相对移动降级成绝对移动" + ); + } + /// v3 关键回归:`Text` 必须被分发,且**后端没有实现 text() 时不能 panic**。 /// 这是 Linux/X11 后端的真实形态——用户对着 Linux 被控端打字会走到这里。 #[test]