Opening a window using SDL2 in Rust

on 2023-08-16

Introduction


Simple DirectMedia Layer(SDL) is a library written in C which provides low-level access to audio, keyboard, mouse, joystick, and graphics hardware. We will use the rust-sdl2 library, which has SDL2 Rust bindings for C. In this tutorial, we will see how to open a window using SDL2 in Rust.

Setup


Install sdl2 on your system. You can follow the installation steps mentioned in the rust-sdl2 documentation. If you use Windows or macOS, ensure the LIBRARY_PATH environment variable is set.

Now create a new cargo project sdl2_tutorial using the following command.

cargo new sdl2_tutorial
cd sdl2_tutorial

Add the sdl2 dependency to Cargo.toml file.

sdl2 = "0.35.2"

Sdl Initialization


The first step to using SDL functionalities is to initialize the SDL Library. So let's start by initializing the SDL library using init() function in main.rs. The sdl init function must be called before using any other SDL function.

pub fn main() -> Result<(), String> {
    let sdl_context = sdl2::init()?;
}

Sdl can be initialized only once at a time. The init function calls SDL_Init and throws an error if Sdl has already been initialized. You can initialize another Sdl only after the first instance of Sdl is dropped.

Video Subsystem


To open a window, we need a connection to Window Manager of our system, which is provided by the SDL Video Subsystem. Let's initialize the video subsystem by calling video() function on sdl_context which we just created.

let video_subsystem = sdl_context.video()?;

The video function calls SDL_VideoInit and throws an error on failure.

Opening Window


Now that we have a connection to the window manager through video_subsystem, let's define a window.

let window = video_subsystem
        .window("first window", 640, 480)
        .position_centered()
        .build()
        .map_err(|e| format!("{e}"))?;

The window() function takes the window's title, width and height as params. position_centered() positions our window at the center of the screen.

SDL provides options to make the window resizable, fullscreen, borderless, etc. You can play around with the options mentioned in docs.

build() function calls SDL_CreateWindow and throws WindowBuildError error type on failure. Since we defined our main function to return errors as String, we mapped WindowBuildError error type to String.

Event Pump


When we perform any action on our application like mouse movement, mouse clicks, pressing keyboard keys, etc. our Operating System generates events for all our actions.
SDL Event Pump gathers and places all these events in the event queue. We poll for these events and define what action should be performed for each event.

Now that we know we have to handle all input events, let's handle closing of our window. On clicking the close button, Quit event will be generated. We will start the Event Pump, poll for events and handle Quit event.

'app: loop {
    for event in sdl_context.event_pump()?.poll_iter() {
        if let Event::Quit { .. } = event { break 'app; }
    }
}

event_pump() initializes Event Pump and places all events in the event queue. poll_iter() returns a polling iterator that calls poll_event() which polls for pending events in the event queue. The iterator will terminate once there are no more pending events.

To keep the window open, we used an infinite loop. Since we are using nested loops, we annotated our outer loop with 'app so that we can easily break from the outer loop by passing the loop annotation to break.

The poll iterator keeps polling for events and when we receive Quit event, we break from the loop. Once the Window instance goes out of scope, our window will be destroyed and closed.

Run Application


Here is the complete code for opening a window using SDL2 in Rust.

use sdl2::event::Event;

pub fn main() -> Result<(), String> {
    let sdl_context = sdl2::init()?;
    let video_subsystem = sdl_context.video()?;
    let window = video_subsystem
        .window("first window", 640, 480)
        .position_centered()
        .build()
        .map_err(|e| format!("{e}"))?;
    
    'app: loop {
        for event in sdl_context.event_pump()?.poll_iter() {
            if let Event::Quit { .. } = event { break 'app; } 
        }
    }
    Ok(())
}

Build and run the project.

cargo run

You will see a blank window with first window as title.

blank_window

Stay tuned for more posts on SDL2 in Rust. Happy coding! 😀