4 个版本

0.1.3 2021年11月29日
0.1.2 2021年11月29日
0.1.1 2021年1月11日
0.1.0 2021年1月11日

#345 in 过程宏

每月 25 次下载

自定义许可证

20KB
276

Winit-main

这是一个 winit 工具,用于抽象化Winit的事件循环控制反转。

理由

由于平台限制,Winit必然需要劫持主线程,造成“你不调用我,我叫你”的情况。控制反转有一些不理想的特性,包括

  • 在事后向程序添加控制反转很困难,因为它往往会影响程序的根本架构。
  • 由于上述原因,很难编写在不同控制反转之间通用的程序,或者将程序从使用一个控制反转框架修改为使用不同的框架。
  • 同时使用多个控制反转很复杂。例如,在不创建额外抽象的情况下,很难将tokio与winit结合使用。

解决方案

此库在第二个线程(一个“模拟主线程”)上运行你的代码,使用winit的事件循环劫持真实主线程,并提供你的代码处理程序以与主事件循环通信。这使得你可以像编写任何其他程序一样编写你的程序,将winit的事件循环视为事件迭代器和创建窗口以及询问系统时使用的处理程序。当模拟主线程退出时,它将触发事件循环退出,关闭进程,就像它是真实主线程一样。

控制流程处理

阻塞器

模拟主线程通过一个EventReceiver接收winit的Event。在这些事件中,用户事件类型是Blocker。这是主线程发出的一种并发结构,它阻止事件循环处理进一步的winit事件,直到Blocker被丢弃。这是在某种程度上同步事件循环与模拟主线程的一种方式,例如同步图像显示。

每当事件循环遇到一个RedrawRequested事件时,它会立即发出一个Blocker,因此不会继续进行,直到模拟主线程接收并丢弃那个Blocker

控制流程

这个库保持winit事件循环在 ControlFlow::Wait 状态。因此,如果您想在一个循环中重新绘制窗口,您应该在每次绘制后调用 Window::request_redraw

示例

没有 winit-main

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-main(没有proc宏)

use winit_main::reexports::{
    event::{Event, WindowEvent},
    window::WindowAttributes,
};

fn main() {
    winit_main::run(|event_loop, events| {
        let window = event_loop
            .create_window(WindowAttributes::default())
            .unwrap();

        for event in events.iter() {
            if matches!(
                event,
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    window_id,
                } if window_id == window.id()
            ) {
                break;
            }
        }
    });
}

winit-main(带有proc宏)

use winit_main::{
    reexports::{
        event::{Event, WindowEvent},
        window::WindowAttributes,
    },
    EventLoopHandle,
    EventReceiver,
};


#[winit_main::main]
fn main(event_loop: EventLoopHandle, events: EventReceiver) {
    let window = event_loop
        .create_window(WindowAttributes::default())
        .unwrap();

    for event in events.iter() {
        if matches!(
            event,
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                window_id,
            } if window_id == window.id()
        ) {
            break;
        }
    }
}

项目成熟度

这个项目很年轻,测试很少,可能优化得也不好。主要的测试形式是本仓库中的 wgpu-example 包,它是 wgpu 项目的三角形示例,经过修改以利用这个抽象。

依赖

~2.3–3.5MB
~77K SLoC