英文:
How can I make a click-through overlay in Rust that still captures input?
问题
以下是您提供的代码的翻译部分,代码本身不需要翻译:
我已经有创建透明窗口并在其上绘制正方形的代码,使用[winit](https://crates.io/crates/winit/0.26.1/dependencies)和[pixels](https://crates.io/crates/pixels)。但我无法使其透明,也就是说,让用户与叠加窗口后面的内容进行交互,同时仍然允许应用程序捕获输入。以下是我的代码的最小示例:
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);
window.set_cursor_hittest(false).unwrap();
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();
}
_ => (),
}
});
}
希望这对您有所帮助。如果您有其他问题或需要进一步帮助,请告诉我。
英文:
I already have code that creates a transparent window and draws a square on it using winit and pixels, but I can't make it click-through, that is, let the user interact with what is behind the overlay window, while still letting the app capture input. Here's a minimal example of my code:
use pixels::{wgpu::Color, Pixels, SurfaceTexture};
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);
window.set_cursor_hittest(false).unwrap();
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();
}
_ => (),
}
});
}
I thought that Event::DeviceEvent
would work because it seemed like it wasn't restricted to a specific window, but it is. In every scenario I've tried, both or none of the println!()
s were called. Do I need another crate for that?
答案1
得分: 0
"device_query" crate可以解决这个问题。它甚至不需要一个窗口来运行,因为输入是通过在键盘和鼠标的DeviceState实例上调用DeviceState::get_keys()
和DeviceState::mouse()
来按需查询的。
use device_query::{DeviceState, DeviceQuery};
// 廉价创建一个空的DeviceState
let device_state = DeviceState::new();
// 这些方法查询输入。它们是分别惰性查询的。
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]; // 它从[1]开始表示M1。[0]没有意义。
上述代码可以在没有窗口焦点甚至没有窗口的情况下捕获输入。在问题中提供的代码中,它应该放在MainEventsCleared
事件内。还需要将event_loop.run()
闭包内的第一行的control_flow.set_wait()
替换为control_flow.set_poll()
,以便MainEventsCleared
将始终运行,即使没有新的事件。
英文:
The crate device_query can solve the problem. It doesn't even need a window to function, as the input is queried on demand by calling DeviceState::get_keys()
and DeviceState::mouse()
on a DeviceState instance for keyboard and mouse respectively.
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.
The above code can capture input without window focus and even without a window at all. In the code provided in the question, it should be put inside the MainEventsCleared
event. It is also required to replace control_flow.set_wait()
in the first line inside the event_loop.run()
closure with control_flow.set_poll()
, so that MainEventsCleared
will always run, even without new events.
答案2
得分: 0
window.set_cursor_hittest(false)
可以工作,尽管这仅在某些平台上受支持。
英文:
It looks like window.set_cursor_hittest(false)
will work although this is only supported on certain platforms.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论