Rust has been gaining popularity in the development community due to its speed, safety, and ability to handle complex tasks effectively. As Rust garners more recognition, developers want to create GUIs and manage windows in their Rust applications.
GUI frameworks and libraries are essential for building modern desktop applications and games. GUI tools allow you to create visually appealing UIs. Rust is home to many GUI libraries and frameworks, from Tauri to Druid, Slint, gtk-rs, and Winit.
Window management is an essential aspect of GUI programming and game development. Window managers are responsible for creating and managing the windows that make up a GUI. They also handle tasks such as drawing graphics, responding to user input, and managing the window’s position, size, and shape. In this article, we will explore how to create and manage windows in your Rust app with Winit.
Jump ahead:
Winit is a lightweight, cross-platform window management library written in Rust that provides functionality for creating, managing, and controlling windows in Rust applications. Winit is designed to be agnostic and highly customizable, and it works seamlessly with other Rust libraries like OpenGL, making it an ideal choice for GUI and game development.
Several features built into Winit make the tool famous for creating GUIs for desktop and mobile devices.
Here are some of the features:
Winit allows you to create windows easily, regardless of the platform. With Winit, you can create windows and set properties from size to position, title, and other properties. You can also listen to events like resizing, closing, and focus changes, resulting in a better UX.
Input handling is an essential aspect of GUI development. Winit has inbuilt functionality for handling user input. Winit supports a mouse, keyboard, and touch input that you can use to create a seamless UX for interactivity.
Winit is cross-platform; you can use the library to create GUIs for various devices, including desktop and mobile devices. Winit supports platforms from Windows to Linux, macOS, iOS, and Android. You can use Winit to create applications for multiple platforms without learning a different tool for each platform.
Winit is a powerful tool for developers creating GUIs for desktop and mobile devices. With features such as windowing, input handling, cross-platform support, and extensive device support, Winit offers a versatile and reliable way to create graphical UIs. It is also free to use and modify as an open source tool, making it a popular choice for developers. Without Winit, creating interactive and engaging applications for various devices has always been challenging.
Winit simplifies the process of window management. To get started with Winit, you’ll need to create a new Rust project. Run this command in the terminal of your working directory to create a Rust project with Cargo:
cargo new my_project
Next, you’ll need to add Winit as a dependency to your project. You can add Winit as a dependency by adding the following line to your Cargo.toml
file:
[dependencies] winit = "0.28.0"
This will download and install the specified version of Winit in your project. Now that you have Winit installed in your project, you can create and manage windows with Winit.
To create windows with Winit, refer to the following code:
use winit::{ event_loop::EventLoop, window::{Window, WindowBuilder}, }; fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = winit::event_loop::ControlFlow::Wait; match event { winit::event::Event::WindowEvent { event: winit::event::WindowEvent::CloseRequested, .. } => *control_flow = winit::event_loop::ControlFlow::Exit, _ => (), } }); }
The program creates a new event loop and window with the new
method of the EventLoop
and WindowBuilder
structs. The run
method of the EventLoop
instance handles events with a closure.
The closure sets the control_flow
variable to ControlFlow::Wait
to keep the event loop running and then uses a match
statement to check if the event is a WindowEvent
with the CloseRequested
variant. If it is, the closure sets the control_flow
variable to ControlFlow::Exit
, which causes the event loop to exit and terminate the program. Otherwise, the window stays on. Here’s what that looks like:
One of the main use cases for Winit is for game and GUI development. Unfortunately, Winit doesn’t provide a direct method for drawing on windows. However, you can retrieve the raw handle of a window and display with the platform
module, the raw_window_handle
and raw_display_handle
methods that allow you to create graphics with OpenGL, Vulkan, and Metal libraries that provide the functionality for drawing graphics. You’ll need to know these libraries to interoperate them with Winit.
Winit provides functionality for user input from the cursor and keyboard to text input. To accept keyboard input, you’ll use the KeyboardInput
struct of the WindowEvent
enum type. Here’s how you can accept keyboard input in your Winit app:
use winit::{event_loop::EventLoop, event::WindowEvent}; use winit::window::WindowBuilder; fn main() { let event_loop = EventLoop::new(); let window_builder = WindowBuilder::new() .with_title("My Window") .with_inner_size(winit::dpi::LogicalSize::new(800.0, 600.0)); let window = window_builder.build(&event_loop).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = winit::event_loop::ControlFlow::Poll; match event { winit::event::Event::WindowEvent { event, .. } => match event { WindowEvent::KeyboardInput { input, .. } => { match input.virtual_keycode { Some(winit::event::VirtualKeyCode::Escape) => *control_flow = winit::event_loop::ControlFlow::Exit, _ => (), } }, WindowEvent::CloseRequested => *control_flow = winit::event_loop::ControlFlow::Exit, _ => (), }, _ => (), } }); }
The program creates a window with a title
and size
, and the event loop matches a key for the escape
key on the keyboard that exits the window. On running the program and pressing the escape
button, the program should exit the event loop and close the window.
Managing windows is one of the popular tasks you’ll perform with a windowing library. You can further customize Windows with Winit for your application, like setting the theme and window size. Winit provides methods with the window builder instance for customizing windows. You can use the with_theme
method to set a theme and the with_inner_size
method to set a window size. Take a look at the following code:
use winit::{event_loop::EventLoop, window::WindowBuilder}; use winit::window::Theme; fn main() { let event_loop = EventLoop::new(); let window_builder = WindowBuilder::new() .with_theme(Some(winit::window::Theme::Light)) .with_title("My Window") .with_inner_size(winit::dpi::LogicalSize::new(800.0, 600.0)); let window = window_builder.build(&event_loop).unwrap(); event_loop.run(move |event, _, control_flow| { *control_flow = winit::event_loop::ControlFlow::Poll; match event { winit::event::Event::WindowEvent { event, .. } => match event { winit::event::WindowEvent::CloseRequested => *control_flow = winit::event_loop::ControlFlow::Exit, _ => (), }, _ => (), } }); }
The program creates a window of 800
by 600
, and the theme is set to Light
instead of the default dark. Here’s what that looks like:
Additionally, you can use the set_outer_position
method to set the position for a window on the screen, like so:
use winit::dpi::LogicalPosition; let window = window_builder.build(&event_loop).unwrap(); window.set_outer_position(LogicalPosition::new(100, 100));
The LogicalPosition
struct instance takes in the width and height for positioning. You can resize windows with the with_resizable
method of the window builder instance that takes in a Boolean value, like so:
let window = WindowBuilder::new() .with_title("Resizable Window") .with_resizable(true) .build(&event_loop) .unwrap();
Additionally, you can set a window to full-screen mode with the with_fullscreen
method that takes in a windowed context:
let window = WindowBuilder::new() .with_title("Fullscreen Window") .with_fullscreen(Some(windowed_context.get_primary_monitor())) .build(&event_loop) .unwrap();
You can also configure your windows for minimizing and maximizing with the set_minimized
and set_maximized
methods of the window builder instance:
window.set_minimized(true); window.set_maximized(true);
As seen above, the two methods take in a Boolean value.
You’ve learned about the Winit library and its features and functionalities. You also learned how to create and manage windows for your Rust app with Winit. Winit is versatile and cross-platform, making the library a must-have in your platform development arsenal.
Debugging Rust applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking the performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Rust application. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Modernize how you debug your Rust apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]