我已经有了创建透明窗口并使用 winit、winapi 和 pixels 在其上绘制正方形的代码,但我不能让它点击,也就是说,让用户与它交互在覆盖窗口后面,同时仍然让应用程序捕获输入。这是我的代码的一个最小示例:
use pixels::{wgpu::Color, Pixels, SurfaceTexture};
use winapi::{
shared::windef::HWND__,
um::winuser::{SetWindowLongPtrW, GWL_EXSTYLE, WS_EX_LAYERED, WS_EX_TRANSPARENT},
};
use winit::{
event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
event_loop::EventLoop,
platform::windows::{WindowExtWindows, HWND},
window::{WindowBuilder, WindowLevel},
};
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
.with_transparent(true)
.build(&event_loop)
.unwrap();
window.set_window_level(WindowLevel::AlwaysOnTop);
// Set the window as transparent and layered
let hwnd = window.hwnd() as *mut HWND__;
unsafe {
SetWindowLongPtrW(
hwnd,
GWL_EXSTYLE,
(WS_EX_TRANSPARENT | WS_EX_LAYERED) as HWND,
);
}
let window_size = window.inner_size();
let surface = SurfaceTexture::new(window_size.width, window_size.height, &window);
let mut pixels = Pixels::new(window_size.width, window_size.height, surface).unwrap();
pixels.set_clear_color(Color::TRANSPARENT);
event_loop.run(move |event, _, control_flow| {
control_flow.set_wait();
match event {
Event::WindowEvent {
event: window_event,
..
} => match window_event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Space),
state: ElementState::Pressed,
..
},
..
} => {
println!("Input from window event");
}
WindowEvent::CloseRequested => control_flow.set_exit(),
_ => (),
},
Event::DeviceEvent {
event:
DeviceEvent::Key(KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Space),
state: ElementState::Pressed,
..
}),
..
} => {
println!("Input from device event");
}
Event::RedrawRequested(_) => {
pixels.render().unwrap();
}
_ => (),
}
});
}
我认为
Event::DeviceEvent
会起作用,因为它似乎不受特定窗口的限制,但确实如此。在我尝试过的每一种情况下,都调用了 println!()
或都没有调用。我需要另一个板条箱吗?
crate device_query 可以解决这个问题。它甚至不需要窗口来运行,因为通过分别在键盘和鼠标的 DeviceState 实例上调用
DeviceState::get_keys()
和 DeviceState::mouse()
来按需查询输入。
use device_query::{DeviceState, DeviceQuery};
// Cheaply creates an empty DeviceState
let device_state = DeviceState::new();
// Those methods query the input. They are individualy lazily queried.
let keys = device_state.get_keys();
let mouse = device_state.get_mouse();
let is_alt_pressed = keys.contains(&Keycode::LAlt);
let is_m1_pressed = mouse.button_pressed[1]; // It starts at [1] for M1. [0] has no meaning.
上面的代码可以在没有窗口焦点甚至根本没有窗口的情况下捕获输入。在问题提供的代码中,它应该放在
MainEventsCleared
事件中。还需要将 control_flow.set_wait()
闭包内第一行的 event_loop.run()
替换为 control_flow.set_poll()
,这样 MainEventsCleared
将始终运行,即使没有新事件。