Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ windows = { version = "=0.61.3", features = [
windows-sys = { version = "0.61.2", features = [
"Win32_Graphics_Gdi",
"Win32_Graphics_OpenGL",
"Win32_System_Com",
"Win32_System_Rpc",
"Win32_System_LibraryLoader",
"Win32_System_Ole",
"Win32_System_SystemServices",
Expand Down
88 changes: 19 additions & 69 deletions src/win/window.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use windows_core::{ComObject, Interface};
use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
System::{
Com::CoCreateGuid,
Ole::{OleInitialize, RevokeDragDrop},
},
System::Ole::{OleInitialize, RevokeDragDrop},
UI::{
Controls::{HOVER_DEFAULT, WM_MOUSELEAVE},
HiDpi::{
Expand All @@ -16,16 +13,15 @@ use windows_sys::Win32::{
},
WindowsAndMessaging::{
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, RegisterClassW, SetCursor,
SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, UnregisterClassW,
CS_OWNDC, GWLP_USERDATA, HTCLIENT, IDC_ARROW, MSG, SWP_NOACTIVATE, SWP_NOMOVE,
SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED,
WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN,
WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY,
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR,
WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSW,
WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW,
WS_SIZEBOX, WS_VISIBLE,
GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, SetCursor, SetTimer,
SetWindowLongPtrW, SetWindowPos, TranslateMessage, GWLP_USERDATA, HTCLIENT, MSG,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE,
WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL,
WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE,
WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN,
WM_XBUTTONUP, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX,
WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
},
},
};
Expand All @@ -42,7 +38,6 @@ use raw_window_handle::{
WindowsDisplayHandle,
};
use windows::Win32::System::Ole::IDropTarget;
use windows_sys::core::GUID;
use windows_sys::Win32::Foundation::FALSE;
use windows_sys::Win32::System::Ole::RegisterDragDrop;

Expand All @@ -60,25 +55,8 @@ use super::keyboard::KeyboardState;

#[cfg(feature = "opengl")]
use crate::gl::GlContext;

unsafe fn generate_guid() -> String {
let mut guid: GUID = std::mem::zeroed();
CoCreateGuid(&mut guid);
format!(
"{:0X}-{:0X}-{:0X}-{:0X}{:0X}-{:0X}{:0X}{:0X}{:0X}{:0X}{:0X}\0",
guid.data1,
guid.data2,
guid.data3,
guid.data4[0],
guid.data4[1],
guid.data4[2],
guid.data4[3],
guid.data4[4],
guid.data4[5],
guid.data4[6],
guid.data4[7]
)
}
use crate::wrappers::win32::h_instance::HInstance;
use crate::wrappers::win32::window_class::RegisteredClass;

#[allow(non_snake_case)]
fn HIWORD(wparam: WPARAM) -> u16 {
Expand Down Expand Up @@ -174,7 +152,7 @@ pub(crate) unsafe extern "system" fn wnd_proc(
// NOTE: This is not handled in `wnd_proc_inner` because of the deferred task loop above
if msg == WM_NCDESTROY {
RevokeDragDrop(hwnd);
unregister_wnd_class((*window_state_ptr).window_class);
let _ = (*window_state_ptr).window_class.take();
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
drop(Rc::from_raw(window_state_ptr));
}
Expand Down Expand Up @@ -497,35 +475,6 @@ unsafe fn wnd_proc_inner(
}
}

#[allow(clippy::upper_case_acronyms)]
type ATOM = u16;

unsafe fn register_wnd_class() -> ATOM {
// We generate a unique name for the new window class to prevent name collisions
let class_name_str = format!("Baseview-{}", generate_guid());
let mut class_name: Vec<u16> = OsStr::new(&class_name_str).encode_wide().collect();
class_name.push(0);

let wnd_class = WNDCLASSW {
style: CS_OWNDC,
lpfnWndProc: Some(wnd_proc),
hInstance: null_mut(),
lpszClassName: class_name.as_ptr(),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(),
hCursor: LoadCursorW(null_mut(), IDC_ARROW),
hbrBackground: null_mut(),
lpszMenuName: null_mut(),
};

RegisterClassW(&wnd_class)
}

unsafe fn unregister_wnd_class(wnd_class: ATOM) {
UnregisterClassW(wnd_class as _, null_mut());
}

/// All data associated with the window. This uses internal mutability so the outer struct doesn't
/// need to be mutably borrowed. Mutably borrowing the entire `WindowState` can be problematic
/// because of the Windows message loops' reentrant nature. Care still needs to be taken to prevent
Expand All @@ -536,7 +485,7 @@ pub(super) struct WindowState {
/// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd,
/// GWLP_USERDATA) } as *const WindowState`.
pub hwnd: HWND,
window_class: ATOM,
window_class: Cell<Option<RegisteredClass>>,
current_size: Cell<PhySize>,
current_scale_factor: Cell<f64>,
_parent_handle: Option<ParentHandle>,
Expand Down Expand Up @@ -680,12 +629,13 @@ impl Window<'_> {
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static,
{
let instance = HInstance::get();

unsafe {
let mut title: Vec<u16> = OsStr::new(&options.title[..]).encode_wide().collect();
title.push(0);

let window_class = register_wnd_class();
// todo: manage error ^
let window_class = RegisteredClass::register_new(instance, Some(wnd_proc)).unwrap();

let scaling = match options.scale {
WindowScalePolicy::SystemScaleFactor => 1.0,
Expand Down Expand Up @@ -720,7 +670,7 @@ impl Window<'_> {

let hwnd = CreateWindowExW(
0,
window_class as _,
window_class.as_atom_ptr(),
title.as_ptr(),
flags,
0,
Expand Down Expand Up @@ -750,7 +700,7 @@ impl Window<'_> {

let window_state = Rc::new(WindowState {
hwnd,
window_class,
window_class: Some(window_class).into(),
current_scale_factor: scaling.into(),
current_size: current_size.into(),
_parent_handle: parent_handle,
Expand Down
3 changes: 3 additions & 0 deletions src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ pub mod xlib;
/// Wrappers and utilities around GLX
#[cfg(all(target_os = "linux", feature = "opengl"))]
pub mod glx;

#[cfg(target_os = "windows")]
pub mod win32;
3 changes: 3 additions & 0 deletions src/wrappers/win32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod h_instance;
pub mod uuid;
pub mod window_class;
34 changes: 34 additions & 0 deletions src/wrappers/win32/h_instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::ptr::null_mut;
use windows_core::Error;
use windows_sys::Win32::Foundation::HINSTANCE;
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;

#[derive(Copy, Clone, PartialEq)]
pub struct HInstance(HINSTANCE);

// SAFETY: This is actually a pointer to the memory image of the executable file. It is guaranteed
// to be valid for the process's lifetime.
// This getting invalidated would imply our own executable has been unloaded already. At that point,
// pointer invalidation would the least of our concerns anyway.
unsafe impl Send for HInstance {}
// SAFETY: same as above
unsafe impl Sync for HInstance {}

impl HInstance {
pub fn get() -> Self {
let result = unsafe { GetModuleHandleW(null_mut()) };
if result.is_null() {
panic!(
"Failed to get HInstance pointer: GetModuleHandleW failed: {}",
Error::from_win32()
);
}

Self(result)
}

#[inline]
pub fn as_raw(&self) -> HINSTANCE {
self.0
}
}
36 changes: 36 additions & 0 deletions src/wrappers/win32/uuid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::fmt::{Display, Formatter};
use windows_sys::core::GUID;
use windows_sys::Win32::System::Rpc::UuidCreate;

pub struct Uuid(GUID);

impl Uuid {
pub fn new() -> Self {
let mut guid = GUID::default();

// SAFETY: the passed pointer is valid, it comes from a mut reference
unsafe { UuidCreate(&mut guid) };

Self(guid)
}
}

impl Display for Uuid {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:0X}-{:0X}-{:0X}-{:0X}{:0X}-{:0X}{:0X}{:0X}{:0X}{:0X}{:0X}\0",
self.0.data1,
self.0.data2,
self.0.data3,
self.0.data4[0],
self.0.data4[1],
self.0.data4[2],
self.0.data4[3],
self.0.data4[4],
self.0.data4[5],
self.0.data4[6],
self.0.data4[7]
)
}
}
65 changes: 65 additions & 0 deletions src/wrappers/win32/window_class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::wrappers::win32::h_instance::HInstance;
use crate::wrappers::win32::uuid::Uuid;
use std::ptr::null_mut;
use std::sync::Arc;
use windows_core::{Error, Result, HSTRING};
use windows_sys::core::PCWSTR;
use windows_sys::Win32::UI::WindowsAndMessaging::{
LoadCursorW, RegisterClassW, UnregisterClassW, CS_OWNDC, IDC_ARROW, WNDCLASSW, WNDPROC,
};

#[derive(Clone)]
pub struct RegisteredClass(Arc<RegisteredClassInner>);

impl RegisteredClass {
pub fn register_new(instance: HInstance, wnd_proc: WNDPROC) -> Result<Self> {
let class_name = format!("Baseview-{}", Uuid::new());
let class_name = HSTRING::from(&class_name);

let class_info = WNDCLASSW {
lpfnWndProc: wnd_proc,
hInstance: instance.as_raw(),
lpszClassName: class_name.as_ptr(),

style: CS_OWNDC, // TODO: this is very suspicious
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(), // Default icon
hCursor: unsafe { LoadCursorW(null_mut(), IDC_ARROW) }, // Arrow cursor
hbrBackground: null_mut(), // No default background
lpszMenuName: null_mut(), // No default menu
};

let class_atom = unsafe { RegisterClassW(&class_info) };
if class_atom == 0 {
return Err(Error::from_win32());
}

Ok(Self(Arc::new(RegisteredClassInner(class_atom, instance))))
}

#[inline]
pub fn as_atom_ptr(&self) -> PCWSTR {
self.0.as_atom_ptr()
}
}

struct RegisteredClassInner(u16, HInstance);

impl RegisteredClassInner {
// See:
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw
// https://networkdls.com/Win32Ref/MAKEINTATOM.html
// https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
#[inline]
pub fn as_atom_ptr(&self) -> PCWSTR {
self.0 as u32 as PCWSTR
}
}

impl Drop for RegisteredClassInner {
fn drop(&mut self) {
// Ignore errors from this, at worst this is a small memory leak.
let _ = unsafe { UnregisterClassW(self.as_atom_ptr(), self.1.as_raw()) };
}
}
Loading