使用旧的Rust 2015

0.32.2 2019年4月24日

#42 in #sdl

MIT 许可证

680KB
12K SLoC

Rust-SDL2 构建状态 crates.io徽章

SDL2在Rust中的绑定

0.32版本变更日志

概述

Rust-SDL2是一个用于与Rust中的新SDL2.0库进行通信的库。使用Rust代码封装底层C组件,使其更加符合惯例,并抽象出不适用的手动内存管理。

Rust-SDL2使用MIT许可证。

如果您需要一个与SDL早期版本兼容的库,请参阅此处

文档

需求

Rust

我们目前针对Rust的最新稳定版。

SDL2.0开发库

建议使用SDL2 >= 2.0.8绑定这些库,但请注意,也支持SDL2 >= 2.0.5。如果低于2.0.5,您可能会遇到链接时错误,因为这里使用了某些函数,但它们在SDL2中未定义。如果您因为使用LTS机器(例如,Ubuntu 12.04或Ubuntu 14.04)而遇到此问题,我们强烈建议您使用“bundled”功能,该功能将为您的项目编译最新的稳定版SDL2。

“Bundled”功能

从0.31版开始,此crate支持一个名为“bundled”的功能,该功能从源代码下载SDL2,自动编译并链接。虽然这应该适用于任何架构,但您必须使用C编译器(如gccclang或MS的编译器)才能正确使用此功能。

Linux

通过您喜欢的包管理工具安装这些程序,或者通过http://www.libsdl.org/

Ubuntu 示例

sudo apt-get install libsdl2-dev

Fedora 示例

sudo dnf install SDL2-devel

Arch 示例
(Arch 没有分开的常规和开发包,所有内容都是一起的。)

sudo pacman -S sdl2

您可能还需要一个C编译器(gcc)。

Mac OS X

如果您使用homebrew

在OSX上,通过homebrew安装这些是个好主意。

brew install sdl2

然后,如果您还没有这样做,请将以下内容添加到您的~/.bash_profile文件中。

export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/lib"

否则,如果您使用macports

您也可以通过macports获取sdl2。

sudo port install libsdl2

然后,如果您还没有这样做,请将以下内容添加到您的~/.bash_profile文件中。

export LIBRARY_PATH="$LIBRARY_PATH:/opt/local/lib/"

如果您在homebrew或macports上遇到问题,请查看这里

如果您使用SDL2框架

您可以从https://www.libsdl.org/download-2.0.php下载并安装SDL2 Mac OS X框架。

要使sdl2 crate链接到SDL2框架,您需要启用use_mac_framework功能。要使用此功能构建和测试sdl2 crate,请使用

cargo test --features use_mac_framework

要启用此功能并依赖sdl2 crate,请将以下内容放入您的项目的Cargo.toml文件中

[dependencies.sdl2]
features = ["use_mac_framework"]
version = ...  # Whichever version you are using

或者,您可以通过在Cargo.toml文件中放置以下内容来在您的包中重新导出此功能

[features]
default = []
use_sdl2_mac_framework = ["sdl2/use_mac_framework"]

带有构建脚本的Windows

  1. http://www.libsdl.org/下载mingw和msvc开发库(SDL2-devel-2.0.x-mingw.tar.gz & SDL2-devel-2.0.x-VC.zip)。
  2. 解压到您选择的文件夹(之后可以删除)。
  3. 在Cargo.toml相同的文件夹中创建以下文件夹结构
gnu-mingw\dll\32
gnu-mingw\dll\64
gnu-mingw\lib\32
gnu-mingw\lib\64
msvc\dll\32
msvc\dll\64
msvc\lib\32
msvc\lib\64
  1. 将源存档中的lib和dll文件复制到我们在步骤3中创建的目录中,如下所示
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\bin 		-> 	gnu-mingw\dll\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\bin 	-> 	gnu-mingw\dll\64
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\lib 		-> 	gnu-mingw\lib\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\lib 	-> 	gnu-mingw\lib\64
SDL2-devel-2.0.8-VC.zip\SDL2-2.0.x\lib\x86\*.dll	 		-> 	msvc\dll\32
SDL2-devel-2.0.8-VC.zip\SDL2-2.0.x\lib\x64\*.dll 			-> 	msvc\dll\64
SDL2-devel-2.0.8-VC.zip\SDL2-2.0.x\lib\x86\*.lib	 		-> 	msvc\lib\32
SDL2-devel-2.0.8-VC.zip\SDL2-2.0.x\lib\x64\*.lib	 		-> 	msvc\lib\64
  1. 如果您还没有构建脚本,请在Cargo.toml中的以下位置创建一个build脚本

build = "build.rs"

  1. 在Cargo.toml相同的目录中创建一个名为build.rs的文件(如果您还没有构建脚本)并将以下内容粘贴到其中
use std::env;
use std::path::PathBuf;

fn main() {
    let target = env::var("TARGET").unwrap();
    if target.contains("pc-windows") {
        let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let mut lib_dir = manifest_dir.clone();
        let mut dll_dir = manifest_dir.clone();
        if target.contains("msvc") {
            lib_dir.push("msvc");
            dll_dir.push("msvc");
        }
        else {
            lib_dir.push("gnu-mingw");
            dll_dir.push("gnu-mingw");
        }
        lib_dir.push("lib");
        dll_dir.push("dll");
        if target.contains("x86_64") {
            lib_dir.push("64");
            dll_dir.push("64");
        }
        else {
            lib_dir.push("32");
            dll_dir.push("32");
        }
        println!("cargo:rustc-link-search=all={}", lib_dir.display());
        for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir")  {
            let entry_path = entry.expect("Invalid fs entry").path();
            let file_name_result = entry_path.file_name();
            let mut new_file_path = manifest_dir.clone();
            if let Some(file_name) = file_name_result {
                let file_name = file_name.to_str().unwrap();
                if file_name.ends_with(".dll") {
                    new_file_path.push(file_name);
                    std::fs::copy(&entry_path, new_file_path.as_path()).expect("Can't copy from DLL dir");
                }
            }
        }
    }
}
  1. 在构建时,构建脚本将所需DLL复制到与Cargo.toml相同的目录,但您可能不希望将这些文件提交到任何Git仓库,因此请向您的.gitignore文件添加以下行

/*.dll

  1. 当您发布游戏时,请确保将相应的SDL2.dll复制到与编译后的exe相同的目录,否则游戏无法启动。

现在,您的项目应该可以在任何Windows计算机上构建和运行了!

Windows(MinGW)

  1. http://www.libsdl.org/下载mingw开发库(SDL2-devel-2.0.x-mingw.tar.gz)。

  2. 解压到您选择的文件夹(之后可以删除)。

  3. 将以下目录中的所有lib文件复制到

    SDL2-devel-2.0.x-mingw\SDL2-2.0.x\x86_64-w64-mingw32\lib

    到(对于Rust 1.6及以上版本)

    C:\Program Files\Rust\lib\rustlib\x86_64-pc-windows-gnu\lib

    或者(对于Rust版本1.5及以下)

    C:\Program Files\Rust\bin\rustlib\x86_64-pc-windows-gnu\lib

    或者到您选择的库文件夹,并确保您有一个系统环境变量

    LIBRARY_PATH = C:\your\rust\library\folder

    对于Rustup用户,此文件夹位于

    C:\Users\{Your Username}\.rustup\toolchains\{current toolchain}\lib\rustlib\{current toolchain}\lib

其中current toolchain可能是stable-x86_64-pc-windows-gnu

  1. SDL2-devel-2.0.x-mingw\SDL2-2.0.x\x86_64-w64-mingw32\bin

    将其添加到您的Cargo项目中,紧挨着您的Cargo.toml文件。

  2. 当您发布游戏时,请确保将SDL2.dll复制到与编译后的exe文件相同的目录中,否则游戏无法启动。

使用MinGW进行静态链接

如果您想使用windows-gnu工具链的static-link功能,那么您还需要以下库

libimm32.a
libversion.a
libdinput8.a
libdxguid.a

这些文件目前不包含在windows-gnu工具链中,但可以从这里下载。对于x86_64工具链,您需要的是x86_64-win32-seh包,对于i686,您需要的是i686-win32-dwarf包。

您可以在mingw64/x86_64-w64-mingw32/lib/(对于x86_64)或mingw32/i686-w64-mingw32/lib/(对于i686)下找到上述库。将它们复制到您的工具链的lib目录中(与您复制SDL .a文件相同的目录)。

Windows (MSVC)

  1. http://www.libsdl.org/下载MSVC开发库(SDL2-devel-2.0.x-VC.zip)。

  2. 将SDL2-devel-2.0.x-VC.zip解压缩到您选择的文件夹中(之后您可以删除它)。

  3. 将以下目录中的所有lib文件复制到

    SDL2-devel-2.0.x-VC\SDL2-2.0.x\lib\x64\

    到(对于Rust 1.6及以上版本)

    C:\Program Files\Rust\lib\rustlib\x86_64-pc-windows-msvc\lib

    或者(对于Rust版本1.5及以下)

    C:\Program Files\Rust\bin\rustlib\x86_64-pc-windows-msvc\lib

    或者到您选择的库文件夹,并确保您有一个系统环境变量

    LIB = C:\your\rust\library\folder

    对于Rustup用户,此文件夹位于

    C:\Users\{Your Username}\.rustup\toolchains\{current toolchain}\lib\rustlib\{current toolchain}\lib

当前工具链可能是stable-x86_64-pc-windows-msvc

  1. SDL2-devel-2.0.x-VC\SDL2-2.0.x\lib\x64\

    将其添加到您的Cargo项目中,紧挨着您的Cargo.toml文件。

  2. 当您发布游戏时,请确保将SDL2.dll复制到与编译后的exe文件相同的目录中,否则游戏无法启动。

使用MSVC进行静态链接

http://libsdl.org/提供的MSVC开发库不包含静态库。这意味着如果您想使用windows-msvc工具链的static-link功能,您必须

  • 自己构建SDL2静态库并将其复制到工具链的lib目录中;或者
  • 同时启用bundled功能,该功能将为您构建静态库。

安装

如果您使用cargo管理项目,您可以通过Crates.io下载

    [dependencies]
    sdl2 = "0.32"

或者,从GitHub拉取以获取master的最新版本

    [dependencies.sdl2]
    git = "https://github.com/AngryLawyer/rust-sdl2"

否则,克隆此仓库并运行cargo

cargo build

您可以通过添加以下内容来启用功能,例如ttf、image、gfx和mixer

    [dependencies.sdl2]
    version = "0.32"
    default-features = false
    features = ["ttf","image","gfx","mixer"]

这些功能需要相应的库,可以在以下位置找到:(安装过程与SDL2相同)

关于sdl2_net呢?

到目前为止,与如serdebincode等其他crate相比,sdl2_net没有意义。我们强烈建议使用它们来开发任何UDP或TCP相关的内容(同时使用futures或标准库中的TCP/UDP)。

如果您仍然想添加sdl2_net的实现,您可以通过Pull Request将它们添加到这个仓库中作为功能。这个绑定的一个相对较旧的版本可以在这里找到

演示

我们包含了一些简单的示例项目

cargo run --example demo

您可以在examples/文件夹中查看完整的列表。一些示例需要某些功能,您可以通过以下方式启用它们

cargo run --example gfx-demo --features "gfx"

将"gfx"替换为您想要的示例所需的功能。

关于unsafe_textures功能

sdl2::render 模块中,Texture 默认具有生命周期,以防止其生命周期超过其父 TextureCreator。这些生命周期在 Rust 中有时很难处理,因此您可以选择启用 unsafe_textures 功能。

这会移除 Texture 上的生命周期,但代价是可选的手动内存管理。如果您想手动销毁您使用的 Texture,可以调用您的 Texturedestroy 方法,但请注意,不应该 在父项(CanvasTextureCreator)都存活的情况下调用该方法。如果您不调用此方法,内存将在最后一个 Canvas 或最后一个 TextureCreator 被释放时自动释放。

此功能没有在线文档,但是您可以通过在 Cargo.toml 中启用功能、运行 cargo doc 并通过浏览器访问 target/doc/sdl2/index.html 来在自己的项目中构建它。

使用 bindgen 生成 sdl2-sys

为此 crate 生成的是非常通用的 sdl2-sys,可以用于许多平台,限制很少。然而,您有时在使用 SDL2 的特定平台功能时可能会遇到麻烦,例如窗口管理器类别。

"use-bindgen" 功能允许您通过根据您的目标生成适当的绑定来避免这种限制。它将根据 pkg-config 输出的内容(如果您启用了 "use-pkg-config" 功能)获取头文件,并基于这些头文件生成绑定。如果您没有 pkg-config 或禁用了该功能,它将尝试从此 crate 的 SDL-2.0.8/include 中获取头文件。

如果您有自己的头文件想使用(使用 beta 版本、旧版本等),可以设置环境变量 "SDL2_INCLUDE_PATH",然后 bindgen 将使用这些头文件。

OpenGL

如果您想使用 OpenGL,还需要 gl-rs 包。如果您使用 cargo,只需将以下行添加到您的 Cargo.toml 中

    [dependencies.gl]
    git = "https://github.com/bjz/gl-rs"

使用 OpenGL 的两种方式

  • 使用 Canvas 和 sdl2::render 一起使用 OpenGL
  • 直接在 "shell" 窗口上使用 OpenGL 并使用手动 OpenGL 调用来渲染内容

使用 sdl2::render

首先,从 SDL 中找到 OpenGL 驱动程序

fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if item.name == "opengl" {
            return Some(index as u32);
        }
    }
    None
}

这在非 Linux 系统上尤其相关,因为在这些系统中,默认的渲染引擎可能是其他引擎(例如 Windows 上的 DirectX)。

接下来,初始化 SDL2 子系统,并使用 OpenGL 画布创建您的窗口


let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem.window("Window", 800, 600)
    .opengl()
    .build()
    .unwrap();
let canvas = window.into_canvas()
    .index(find_sdl_gl_driver().unwrap())
    .build()
    .unwrap();

gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
canvas.window().gl_set_context_to_current();

unsafe {
    gl::ClearColor(0.6, 0.0, 0.8, 1.0);
    gl::Clear(gl::COLOR_BUFFER_BIT);
}

canvas.present();

// opengl code here

但是请注意,sdl2 有自己的内部状态,您应该避免干预。避免在 SDL2 调用中间使用手动 OpenGL,或在此之间更改状态。

您无法通过这种方法覆盖 OpenGL 版本,除非通过更改 gl 状态

手动使用 OpenGL 调用

extern crate sdl2;
extern crate gl;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    
    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_context_profile(GLProfile::Core);
    gl_attr.set_context_version(3, 3);

    let window = video_subsystem.window("Window", 800, 600)
        .opengl()
        .build()
        .unwrap();

    let ctx = window.gl_create_context().unwrap();
    gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    
    debug_assert_eq!(gl_attr.context_profile(), GLProfile::Core);
    debug_assert_eq!(gl_attr.context_version(), (3, 3));

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        unsafe {
            gl::ClearColor(0.6, 0.0, 0.8, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }

        window.gl_swap_window();
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                _ => {}
            }
        }
        ::std::thread::sleep(::std::time::Duration::new(0, 1_000_000_000u32 / 60));
    }
}

当您不关心 sdl2 的渲染功能,但关心其音频、控制器和其他一些 cool 功能时,这种方法很有用。

Vulkan

要使用 Vulkan,您需要一个 Rust 的 Vulkan 库。此示例使用 Vulkano 库。其他库可能使用不同的原始 Vulkan 对象句柄数据类型。与这些库接口 SDL2 的 Vulkan 函数的步骤将因每个库而异。

extern crate sdl2;
extern crate vulkano;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use std::ffi::CString;
use vulkano::VulkanObject;
use vulkano::instance::{Instance, RawInstanceExtensions};
use vulkano::swapchain::Surface;

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Window", 800, 600)
        .vulkan()
        .build()
        .unwrap();

    let instance_extensions = window.vulkan_instance_extensions().unwrap();
    let raw_instance_extensions = RawInstanceExtensions::new(instance_extensions.iter().map(
        |&v| CString::new(v).unwrap()
        ));
    let instance = Instance::new(None, raw_instance_extensions, None).unwrap();
    let surface_handle = window.vulkan_create_surface(instance.internal_object()).unwrap();
    let surface = unsafe { Surface::from_raw_surface(instance, surface_handle, window.context()) };

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                _ => {}
            }
        }
        ::std::thread::sleep(::std::time::Duration::new(0, 1_000_000_000u32 / 60));
    }
}

当事情出错时

Rust 和 Rust-SDL2 都还在积极开发中,使用时可能会遇到一些成长问题。在恐慌之前,请检查您是否正在使用 Rust 和 Cargo 的最新版本,并确保您已将 Rust-SDL2 更新到最新版本。然后运行 cargo clean。如果这失败了,请通过问题跟踪器告诉我们。

贡献

欢迎任何大小的 Pull Request!然而,贡献也有一些条件。

  • 新功能必须有适当的文档,无论是通过示例还是内联文档(通过 cargo doc)。文档必须面向最终用户以及您的下一位贡献者。
  • 重大更改必须附有适当的论证。虽然这个 crate 的 1.0 版本之前的版本允许我们保持一定的不稳定,但 无用的重大更改将被拒绝
  • 通过 Pull Request 添加的次要更改、重大更改和新功能必须添加到 变更日志文件。现在,在变更日志中记录您的更改是 强制性的。一个简短的描述以及指向您的 GitHub 提交/拉取请求的链接即可。内部、文档或元更改(travis 构建更改、README 指令更新等)无需添加到变更日志中。

依赖项

~17MB
~351K SLoC