如何获得rust-sdl2窗口表面并同时使用事件迭代器?

问题描述

sdl2::video::Window::surface需要对事件泵的可变引用,但是当我遍历sdl2::EventPump::wait_iter()给出的事件时,事件泵被阻止了。

复制步骤:

创建一个新的货运项目:

$ cargo new foo
$ cd foo
$ echo 'sdl2 = "0.34.3"' >> Cargo.toml

用以下命令替换src / main.rs:

extern crate sdl2; 

use sdl2::event::Event;
use sdl2::event::EventType;
 
pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let window = video_subsystem.window("foo",600,600).build().unwrap();
    let mut event_pump = sdl_context.event_pump().unwrap();
    for event in event_pump.wait_iter() {
        let mut wsuf = window.surface(&event_pump).unwrap();
    }
}

不使用迭代器是一种解决方法。所以这个main.rs会做同样的事情,但是这个会编译:

use sdl2::event::Event;
use sdl2::event::EventType;
 
pub fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let window = video_subsystem.window("foo",600).build().unwrap();
    let mut event_pump = sdl_context.event_pump().unwrap();
    loop {
        let event = event_pump.wait_event();
        let mut wsuf = window.surface(&event_pump).unwrap();
    }
}

这感觉不太习惯。

我看不到sdl2::video::Window::surface借用事件泵的原因,尤其是在函数忽略该参数的情况下。 Take a look at the sdl2::video::Window::surface source code

解决方法

此函数的文档未提及此神秘论点,因此我将做一个有根据的猜测

首先,查看函数Window::surface declaration

pub fn surface<'a>(&'a self,_e: &'a EventPump) -> Result<WindowSurfaceRef<'a>,String>

,请注意,生存期'a在两个参数和返回的WindowSurfaceRef<'a>中都使用。因此,只要返回的值存在,selfEventPump都将被借用。

查看代码,它是本机SDL2-sys函数SDL_GetWindowSurface()的简单包装,因此我们可以看一下其documentation

如有必要,将以窗口的最佳格式创建一个新曲面。销毁窗户后,该表面将被释放。不要释放该表面。

如果调整窗口大小,则该表面将无效。调整窗口大小后,必须再次调用此函数以返回有效表面。

您不得在此窗口中将此与3D或渲染API结合使用。

关键字为粗体字。如果调整了窗口的大小,则该窗口将无效,这可能应解释为“如果调整窗口的大小然后使用此表面,则会导致未定义的行为”。

要使此包装器生锈安全,必须避免在返回的WindowSurfaceRef的有效期内调整大小。这是通过借用EventPump来实现的:借用EventPump时,无法处理任何消息,并且不会调整窗口的大小。

请注意,要借用EventPump,您实际上不需要保留对其的引用,只需保留其生命周期即可。通过返回WindowSurfaceRef<'a>,可以借用寿命为'a的任何东西。

文档中关于不使用此窗口上的呈现API 的最后一段,是通过类似的方式来实现的,方法是也保持Window的借用,并且寿命相同{ {1}}。