In this tip, we will see how we can use the Rust language to do GUI (Graphical User Interface) programming. As an example program, we will create a simple Window. Using Win32 API functions.

Please Sign up or sign in to vote.

Introduction

Rust is a general-purpose, multi paradigm, compiled programming language. It is designed to be a fast, secured and type-safe language. The language is Open-Source and developed by Mozilla Research.

According to https://en.wikipedia.org/wiki/Rust_(programming_language) -

Although its development is sponsored by Mozilla, it is an open community project.

Rust supports both Functional and Object-Oriented programming. It supports C/C++ like pointer manipulation. So what we do in C/C++ language, we can do in Rust language, but in a very different way.

Rust Language Featuring (Taken From https://www.rust-lang.org/):

zero-cost abstractions

move semantics

guaranteed memory safety

threads without data races

trait-based generics

pattern matching

type inference

minimal runtime

efficient C bindings

In this tip, we will see how we can use this language to do GUI (Graphical User Interface) programming. As an example program, we will create a simple Window. Using Win32 API functions.

Using the Code

As far as I know, libraries are called Crates in Rust language. We are going to use various crates libraries in this project. libc , winapi , etc. Those crates are available in Rust crate host website (https://crates.io/).

Crates are automatically downloaded when you build your project using Cargo tool.

The Cargo is a tool for Rust language that helps in managing Rust projects. It comes with Rust official installer. The Cargo tool downloads all the dependencies needed for the project, builds dependencies and builds the project. Cargo tool helps programmer to build Rust project nicely. So we better use Cargo to make things easier.

To use the Cargo tool in our project, first we have to follow some rule. We have to create a root folder. The folder name should be your project name. Inside the root folder, we need to create an another folder called ‘src’ for keeping project source files. Because the Cargo tool expects the project source files inside a src directory and then, inside the root folder, we need to create a configuration file called ‘Cargo.toml’. The file is in TOML format which is similar to INI file but has some extra advantage.

Create the ‘Cargo.toml’ file and write the following lines of code to it:

[package] name = "simple_window" version = "0.0.1" authors = [ "Your name <you@example.com>" ]

Then, add the following dependencies in the ‘Cargo.toml’ file:

[dependencies] libc = "0.1.10" winapi = "0.2.4" user32-sys = "0.1.2" kernel32-sys = "0.1.4"

First, we extern the following necessary crates in our main source file called ‘main.rs’. I assume that you are familiar with pure Win32 API functions:

extern crate kernel32; extern crate user32; extern crate winapi; extern crate libc;

Then we ‘ use ’ necessary types and functions from those libraries:

use winapi::windef::HWND; use winapi::windef::HMENU; use winapi::windef::HBRUSH; use winapi::minwindef::HINSTANCE; use winapi::minwindef::UINT; use winapi::minwindef::DWORD; use winapi::minwindef::WPARAM; use winapi::minwindef::LPARAM; use winapi::minwindef::LRESULT; use winapi::winnt::LPCWSTR; use winapi::winuser::WS_OVERLAPPEDWINDOW; use winapi::winuser::WS_VISIBLE; use winapi::winuser::WNDCLASSW; use std::os::windows::ffi::OsStrExt; use std::ffi::OsStr;

The following function is used to convert normal string to wide string :

fn to_wstring(str : &str) -> Vec<u16> { let v : Vec<u16> = OsStr:: new (str).encode_wide().chain(Some( 0 ).into_iter()).collect(); v }

This is our window message handler function. Currently, it only processes the WM_DESTROY message to exit our window properly on close event.

pub unsafe extern " system" fn window_proc(h_wnd :HWND, msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT { if msg == winapi::winuser::WM_DESTROY { user32::PostQuitMessage( 0 ); } return user32::DefWindowProcW(h_wnd, msg, w_param, l_param); }

You may have noticed that we have used an ‘ unsafe ’ keyword in the above codes. According to https://doc.rust-lang.org/book/unsafe.html:

Rust’s main draw is its powerful static guarantees about behavior. But safety checks are conservative by nature: there are some programs that are actually safe, but the compiler is not able to verify this is true. To write these kinds of programs, we need to tell the compiler to relax its restrictions a bit. For this, Rust has a keyword, unsafe. Code using unsafe has less restrictions than normal code does.

So that’s why we have to use the unsafe keyword.

The following function hides the Console Window since we are building a GUI application. (Actually, I don’t know how to use ‘ subsytem ’ with Cargo tool! If someone knows, then please comment to improve this tip. :-> )

fn hide_console_window() { let window = unsafe { kernel32::GetConsoleWindow() }; if window != std::ptr::null_mut() { unsafe { user32::ShowWindow (window, winapi::SW_HIDE) }; } }

The following is the entry point function of our program. We create our window inside this function by using the typical Win32 GUI programming style:

fn main() { unsafe { hide_console_window(); let class_name = to_wstring( " my_window" ); let wnd = WNDCLASSW { style: 0 , lpfnWndProc: Some(window_proc), cbClsExtra: 0 , cbWndExtra: 0 , hInstance: 0 as HINSTANCE, hIcon: user32::LoadIconW( 0 as HINSTANCE, winapi::winuser::IDI_APPLICATION), hCursor: user32::LoadCursorW( 0 as HINSTANCE, winapi::winuser::IDI_APPLICATION), hbrBackground: 16 as HBRUSH, lpszMenuName: 0 as LPCWSTR, lpszClassName: class_name.as_ptr(), }; user32::RegisterClassW(&wnd); let h_wnd_window = user32::CreateWindowExW( 0 , class_name.as_ptr(), to_wstring( " Simple Window" ).as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0 , 0 , 400 , 400 , 0 as HWND, 0 as HMENU, 0 as HINSTANCE, std::ptr::null_mut()); let mut msg = winapi::winuser::MSG { hwnd : 0 as HWND, message : 0 as UINT, wParam : 0 as WPARAM, lParam : 0 as LPARAM, time : 0 as DWORD, pt : winapi::windef::POINT { x: 0 , y: 0 , }, }; user32::ShowWindow(h_wnd_window, winapi::SW_SHOW); loop { let pm = user32::GetMessageW(&mut msg, 0 as HWND, 0 , 0 ); if pm == 0 { break ; } if msg.message == winapi::winuser::WM_QUIT { break ; } user32::TranslateMessage(&mut msg); user32::DispatchMessageW(&mut msg); } } }

Conclusion

Rust is a nice programming language. Programmers can learn this language to enhance their skill on programming.