如何在应用未聚焦时通过winit接收DeviceEvent?
Windows平台下让Rust winit后台接收DeviceEvent的方法
winit默认仅在窗口聚焦时接收设备事件,要实现后台接收,需要结合Windows原生API修改输入监听逻辑,步骤如下:
1. 获取窗口的HWND句柄
先从winit窗口中提取Windows平台的窗口句柄(HWND),后续API调用依赖该句柄:
use winit::window::Window; use raw_window_handle::RawWindowHandle; fn get_hwnd(window: &Window) -> Option<isize> { match window.raw_window_handle() { RawWindowHandle::Win32(win32_handle) => Some(win32_handle.hwnd as isize), _ => None, } }
2. 注册原始输入设备
通过Windows的RegisterRawInputDevicesAPI注册鼠标输入,指定RIDEV_INPUTSINK标记,允许窗口在未聚焦时接收输入:
use winapi::{ shared::minwindef::{BOOL, DWORD, UINT}, um::winuser::{ RegisterRawInputDevices, RAWINPUTDEVICE, RIDEV_INPUTSINK, RIM_TYPEMOUSE, }, }; use std::ptr; fn register_raw_input(hwnd: isize) -> bool { let rid = RAWINPUTDEVICE { usUsagePage: 0x01, // 通用桌面控件页面 usUsage: 0x02, // 鼠标设备 dwFlags: RIDEV_INPUTSINK, // 允许后台接收输入 hwndTarget: hwnd as _, }; unsafe { RegisterRawInputDevices( &rid, 1, std::mem::size_of::<RAWINPUTDEVICE>() as UINT, ) != 0 } }
3. 在事件循环中处理原始输入
winit的DeviceEvent不会自动包含后台输入,需通过WindowEvent::RawWindowEvent拦截Windows原生消息,处理WM_INPUT事件:
use winit::event::{Event, WindowEvent}; use winapi::um::winuser::WM_INPUT; // 事件循环示例 for event in event_loop.run() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::RawWindowEvent { raw_window_event, .. } => { if let raw_window_handle::RawWindowEvent::Win32(msg) = raw_window_event { if msg.message == WM_INPUT { // 此处添加原始鼠标输入的解析逻辑 // 可参考Windows API文档处理RAWINPUT结构体 println!("后台收到鼠标输入"); } } } _ => {} }, _ => {} } }
依赖与注意事项
- 在
Cargo.toml中添加必要依赖:[dependencies] winit = "0.29" raw-window-handle = "0.5" winapi = { version = "0.3", features = ["winuser", "minwindef"] } - 相比全局钩子,原始输入注册的方式性能更优,且无需额外DLL
- 若使用全局钩子(
SetWindowsHookExW),需注意钩子函数的线程安全与资源释放
内容的提问来源于stack exchange,提问作者Rhysol
相关产品推荐
相关产品推荐

