首页 > 解决方案 > 如何获取 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, 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借用事件泵的原因,尤其是在函数忽略该参数的情况下。看一下 sdl2::video::Window::surface 源代码

标签: rustsdl-2

解决方案


这个函数的文档没有说明这个神秘的论点,所以我要做一个有根据的猜测

首先,看函数Window::surface声明

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

并注意生命周期'a在参数和返回的WindowSurfaceRef<'a>. 所以只要返回值存在,self和都会被借用。EventPump

查看代码,它是原生 SDL2-sys 函数的简单包装器SDL_GetWindowSurface(),因此我们可以查看它的文档

如有必要,将使用窗口的最佳格式创建一个新表面。当窗口被破坏时,这个表面将被释放。不要释放这个表面。

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

您不能将此与 3D 或此窗口上的渲染 API 结合使用。

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

为了使这个包装器生锈安全,你必须避免在返回的生命周期内调整大小WindowSurfaceRef。这是通过借用EventPump: 来实现的,在EventPump借用 时,不能处理任何消息,并且窗口不会调整大小。

请注意,为了借用EventPump您不需要实际保留对它的引用,只需保留它的生命周期。通过归还WindowSurfaceRef<'a>任何具有生命周期的东西'a来保持借用。

文档中的最后一段,关于不在此窗口上使用呈现 API,以类似的方式实现,同时保持Window借来的,具有相同的生命周期'a


推荐阅读