4 个版本

0.2.1 2023年10月15日
0.2.0 2023年8月30日
0.1.1 2023年4月16日
0.1.0 2023年4月16日

GUI 中排名 471

每月下载量 34

LGPL-3.0 或更高 OR MPL-2.0

220KB
4K SLoC

async-winit

像您一直想要的异步运行时一样使用 winit

winit 实际上是异步的,这与普遍看法相反;它只是不是 async。它使用事件循环来处理事件,这对于某些情况来说是很好的,但对于其他情况则不然。 winit 的维护者将这种类型的事件循环称为“穷人版的 async”;一个不是 async 但仍然是异步的系统。

这个 crate 在这个事件循环之上构建了一个 async 接口。

示例

考虑以下 winit 程序,它创建一个窗口并在窗口大小改变时打印窗口大小

use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::window::Window;

fn main2(evl: EventLoop<()>) {
    let mut window = None;

    evl.run(move |event, elwt, flow| {
        match event {
            Event::Resumed => {
                // Application is active; create a window.
                window = Some(Window::new(elwt).unwrap());
            },

            Event::Suspended => {
                // Application is inactive; destroy the window.
                window = None;
            },

            Event::WindowEvent { event, .. } => match event {
                WindowEvent::CloseRequested => {
                    // Window is closed; exit the application.
                    flow.set_exit();
                },

                WindowEvent::Resized(size) => {
                    println!("{:?}", size);
                }

                _ => {},
            },

            _ => {},
        }
    });
}

fn main() {
#   return;
    let evl = EventLoop::new();
    main2(evl);
}

这种策略有点冗长。现在,比较一下等效的 async-winit 程序

use async_winit::event_loop::EventLoop;
use async_winit::window::Window;
use async_winit::ThreadUnsafe;
use futures_lite::prelude::*;

fn main2(evl: EventLoop<ThreadUnsafe>) {
    let window_target = evl.window_target().clone();

    evl.block_on(async move {
        loop {
            // Wait for the application to be active.
            window_target.resumed().await;

            // Create a window.
            let window = Window::<ThreadUnsafe>::new().await.unwrap();

            // Print the size of the window when it is resized.
            let print_size = async {
                window
                    .resized()
                    .wait()
                    .for_each(|size| {
                        println!("{:?}", size);
                    })
                    .await;

                true
            };

            // Wait until the window is closed.
            let close = async {
                window.close_requested().wait().await;
                println!("Close");
                true
            };

            // Wait until the application is suspended.
            let suspend = async {
                window_target.suspended().wait().await;
                false
            };

            // Run all of these at once.
            let needs_exit = print_size.or(close).or(suspend).await;

            // If we need to exit, exit. Otherwise, loop again, destroying the window.
            if needs_exit {
                window_target.exit().await;
            } else {
                drop(window);
            }
        }
    });
}

fn main() {
#   return;
    let evl = EventLoop::new();
    main2(evl);
}

在我看来,更简洁的 async 风格更容易阅读和理解。您的体验可能不同。

优点

  • 在许多情况下,将程序视为异步任务可能比将其视为事件循环更有意义。
  • 您不需要将所有内容都绑定到 EventLoopWindowTarget;例如,Window::new() 和其他函数不需要参数,并且只要某个地方有一个 EventLoop 在运行,就可以在任何地方调用它们。
  • 您可以使用 async 生态系统充分发挥其潜力。

缺点

  • 使用 async-winit 会涉及相当数量的开销。这是因为 async-winit 是基于 winit 构建的,而 winit 又是基于其事件循环构建的。这意味着 async-winit 必须在 asyncwinit 的事件循环之间进行转换,这不是免费的。
  • async-winit 不像 winit 那样底层。这意味着您不能做所有可以用 winit 做的事情。
    • 例如,数据不能在各个任务之间进行可变共享。在简单情况下,这可以通过 RefCell 容易地解决,但仍需要额外的思考来处理共享状态。

致谢

async-winit 由 John Nunley (@notgull) 创建。

此项目在很大程度上基于 Stjepan Glavina 等人编写的 async-io,以及 Pierre Kreiger 等人编写的 winit

许可证

async-winit 是自由软件:您可以在以下许可证的条款下重新分发它并/或修改它:

  • 由自由软件基金会发布的 GNU Lesser General Public License 的第 3 版或更高版本。
  • 由 Mozilla 基金会发布的 Mozilla Public License 第 2 版。

async-winit 的分发是为了希望它将是有用的,但没有任何保证;甚至没有关于适销性和针对特定目的的适合性的暗示保证。有关更多详细信息,请参阅 GNU Lesser General Public License 或 Mozilla Public License。

您应已收到与 async-winit 一起提供的 GNU Lesser General Public License 和 Mozilla Public License 的副本。如果没有,请参阅 https://www.gnu.org/licenses/

依赖项

~3–17MB
~179K SLoC