2 个版本
0.1.1 | 2022 年 8 月 10 日 |
---|---|
0.1.0 | 2022 年 8 月 10 日 |
#838 in GUI
56KB
724 行
winit-modular: 代理 winit
事件循环,可以在单独的线程中同时运行
提供了一个与 winit
非常相似的 API,除了 EventLoop
类型可以在多个线程上创建,可以同时存在多个事件循环,你可以轮询事件或异步接收事件,等等。
注意: 此库仍在早期开发阶段,某些功能尚未完全测试。如果您遇到任何问题,请提交 issue 或 PR 到 github。
问题
winit
是在 Rust 中创建窗口和监听系统事件的事实上的方式。然而,允许您创建窗口和监听事件的核结构体 EventLoop
有一些令人烦恼的限制
- 它必须在主线程上创建
- 它只能创建一次
- 当
EventLoop::run
结束时,您的应用程序退出。
这导致更多的限制
- 您不能同时运行多个事件循环
- 您不能停止一个事件循环,然后运行另一个事件循环
- 您不能有多个创建事件循环的依赖项。
还有 控制反转 的问题,winit-main 对此进行了说明并尝试解决。
解决方案
此 crate 解决了 winit
的模块化不足问题,某种程度上。它提供了与 winit::event_loop::EventLoop
相似 API 的 EventLoop
,但
- 可以在不同的线程或甚至在同一线程上存在并运行(参见
run
) - 可以异步运行(见
run_async
) - 可以被轮询(见
run_immediate
),解决“控制反转”问题 - 您可以停止对这些的调用,并丢弃事件循环而无需退出整个应用程序。
这是因为这些 EventLoop
实际上是代理,它们使用异步通道和原子操作将调用和事件从主事件循环中转发。
缺点
这并不能完全解决 winit
的问题,并不总是可以直接替换。
最重要的是,您必须在您的应用程序中,在主线程上,使用此crate中的事件循环之前,恰好调用一次 winit_modular::run
。如果您不这样做,将会收到一条解释这一点的panic消息。因为 winit_modular::run
占用了主线程,它提供了一个回调来在后台线程上运行您的其余代码。
另外,使用多个线程和通过通道发送消息的性能损失。代理事件循环必须在线程边界内与实际 winit 事件循环通信,对于每个操作或拦截的事件。这意味着,通常在每一帧刷新时就会进行一次。幸运的是,现代硬件通常足够快,并且线程足够好,即使在这种情况下,它也只是一种轻微的性能损失。但在嵌入式系统中,或者如果您同时生成很多代理事件循环,可能会成为问题。
示例(原始来自 winit-main)
没有 winit_modular
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if matches!(
event,
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id()
) {
*control_flow = ControlFlow::Exit;
}
});
}
有 winit-modular
use winit_modular::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop}
};
use pollster::block_on;
fn main() {
winit_modular::run(|| block_on(async {
let event_loop = EventLoop::new().await;
let window = event_loop
.create_window(|builder| builder)
.await
.unwrap();
event_loop.run_async(|event, control_flow| {
if matches!(
event,
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window_id == window.id()
) {
*control_flow = ControlFlow::ExitApp;
}
}).await;
}));
}
依赖项
~3–6MB
~121K SLoC