Skip to content
Open
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
1 change: 0 additions & 1 deletion src/win/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
mod cursor;
mod drop_target;
mod hook;
mod keyboard;
Expand Down
272 changes: 89 additions & 183 deletions src/win/window.rs

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions src/wrappers/win32.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
pub mod cursor;
pub mod h_instance;
mod rect;
pub mod uuid;
pub mod window;

pub use rect::Rect;

use std::ptr::null_mut;
use windows_core::{Error, Result, HRESULT};
use windows_sys::Win32::Foundation::{S_FALSE, S_OK};
use windows_sys::Win32::System::Ole::OleInitialize;
use windows_sys::Win32::UI::HiDpi::{
SetProcessDpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{
DispatchMessageW, GetMessageW, TranslateMessage, MSG,
};

pub fn ole_initialize() -> Result<()> {
// SAFETY: this is always safe to call with NULL
match unsafe { OleInitialize(null_mut()) } {
S_OK | S_FALSE => Ok(()),
result => Err(Error::new(HRESULT(result), "OLE initialization failed")),
}
}

pub fn run_thread_message_loop_until(until: impl Fn() -> bool) -> Result<()> {
let mut msg = MSG::default();

loop {
// SAFETY: The msg pointer is valid as it comes from a reference. NULL/0 are valid arguments for this function.
let result = unsafe { GetMessageW(&mut msg, null_mut(), 0, 0) };

match result {
-1 => return Err(Error::from_win32()), // -1 means error
0 => return Ok(()), // 0 means WM_QUIT was received
_ => {} // Nonzero means a message was retrieved
}

// SAFETY: The msg pointer is valid since it comes from a reference.
// The contents of the msg struct itself are valid, since they come from GetMessage, and we
// checked the error cases above.
let _ = unsafe { TranslateMessage(&msg) }; // TODO: log warning if this failed

// SAFETY: same as above
unsafe { DispatchMessageW(&msg) };

if until() {
return Ok(());
}
}
}

// Only works on Windows 10 unfortunately.
pub fn set_process_per_monitor_dpi_aware() {
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) };
}
28 changes: 27 additions & 1 deletion src/win/cursor.rs → src/wrappers/win32/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::MouseCursor;
use std::ffi::c_void;
use std::ptr::{null_mut, NonNull};
use windows_core::{Error, Result};
use windows_sys::Win32::UI::WindowsAndMessaging::{LoadCursorW, SetCursor};
use windows_sys::{
core::PCWSTR,
Win32::UI::WindowsAndMessaging::{
Expand All @@ -7,7 +11,29 @@ use windows_sys::{
},
};

pub fn cursor_to_lpcwstr(cursor: MouseCursor) -> PCWSTR {
#[derive(Copy, Clone)]
pub struct SystemCursor(NonNull<c_void>);

impl SystemCursor {
pub fn load(cursor: MouseCursor) -> Result<Self> {
let cursor_ptr = cursor_to_lpcwstr(cursor);

// SAFETY: the PCWSTR returned by cursor_to_lpcwstr is always a valid shared cursor ID
let result = unsafe { LoadCursorW(null_mut(), cursor_ptr) };

match NonNull::new(result) {
Some(res) => Ok(Self(res)),
None => Err(Error::from_win32()),
}
}

pub fn set(&self) {
// SAFETY: This type guarantees the HCURSOR was returned by a successful call to LoadCursorW
unsafe { SetCursor(self.0.as_ptr()) };
}
}

fn cursor_to_lpcwstr(cursor: MouseCursor) -> PCWSTR {
match cursor {
MouseCursor::Default => IDC_ARROW,
MouseCursor::Hand => IDC_HAND,
Expand Down
36 changes: 36 additions & 0 deletions src/wrappers/win32/rect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::PhySize;
use windows_core::{Error, Result};
use windows_sys::Win32::Foundation::RECT;
use windows_sys::Win32::UI::WindowsAndMessaging::AdjustWindowRectEx;

#[derive(Copy, Clone)]
pub struct Rect(pub RECT);

impl Rect {
pub fn client_area_to_nc_area(mut self, style: u32) -> Result<Self> {
let result = unsafe { AdjustWindowRectEx(&mut self.0, style, 0, 0) };
if result == 0 {
return Err(Error::from_win32());
}

Ok(self)
}

pub fn size(&self) -> PhySize {
PhySize {
width: self.0.right.abs_diff(self.0.left),
height: self.0.top.abs_diff(self.0.bottom),
}
}
}

impl From<PhySize> for Rect {
fn from(size: PhySize) -> Self {
Self(RECT {
left: 0,
top: 0,
right: size.width.try_into().unwrap_or(i32::MAX),
bottom: size.height.try_into().unwrap_or(i32::MAX),
})
}
}
7 changes: 4 additions & 3 deletions src/wrappers/win32/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use window_class::RegisteredClass;
use windows_core::{Error, Result, HSTRING};

use crate::wrappers::win32::h_instance::HInstance;
use crate::PhySize;
use windows_sys::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
use windows_sys::Win32::UI::WindowsAndMessaging::{CreateWindowExW, WINDOW_STYLE};

Expand Down Expand Up @@ -48,7 +49,7 @@ pub trait WindowImpl: 'static {
/// For any non-trivial operations (e.g. window resizing, GL context creation, etc.), put them in
/// [`WindowImpl::after_create`] instead.
pub fn create_window<W: WindowImpl>(
title: &HSTRING, flags: WINDOW_STYLE, nc_width: i32, nc_height: i32, parent: HWND,
title: &HSTRING, flags: WINDOW_STYLE, nc_size: PhySize, parent: HWND,
initializer: impl FnOnce(HWnd) -> W + 'static,
) -> Result<HWND> {
let instance = HInstance::get();
Expand All @@ -64,8 +65,8 @@ pub fn create_window<W: WindowImpl>(
flags,
0,
0,
nc_width,
nc_height,
nc_size.width.try_into().unwrap_or(i32::MAX),
nc_size.height.try_into().unwrap_or(i32::MAX),
parent,
null_mut(),
instance.as_raw(),
Expand Down
170 changes: 165 additions & 5 deletions src/wrappers/win32/window/handle.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
use std::marker::PhantomData;
use std::ptr::NonNull;
use windows_core::{Error, Result, HRESULT};
use windows_sys::Win32::Foundation::{SetLastError, HWND};
use crate::wrappers::win32::Rect;
use crate::PhySize;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use std::ptr::{null_mut, NonNull};
use windows::Win32::System::Ole::IDropTarget;
use windows_core::{Error, Interface, InterfaceRef, Result, HRESULT};
use windows_sys::Win32::Foundation::{SetLastError, HWND, S_OK};
use windows_sys::Win32::System::Ole::{RegisterDragDrop, RevokeDragDrop};
use windows_sys::Win32::UI::HiDpi::GetDpiForWindow;
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
GetFocus, ReleaseCapture, SetCapture, SetFocus, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{
GetWindowLongPtrW, SetWindowLongPtrW, GWLP_USERDATA,
DestroyWindow, GetWindowLongPtrW, SetTimer, SetWindowLongPtrW, SetWindowPos, ShowWindow,
GWLP_USERDATA, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, SW_SHOW,
};

/// A simple wrapper around a HWND.
Expand Down Expand Up @@ -47,4 +57,154 @@ impl HWnd<'_> {

Err(error)
}

pub fn get_dpi(&self) -> Result<u32> {
// SAFETY: This type guarantees the HWND is safe to use.
match unsafe { GetDpiForWindow(self.0) } {
0 => Err(Error::from_win32()),
dpi => Ok(dpi),
}
}

pub fn register_drag_drop(&self, drop_target: InterfaceRef<IDropTarget>) -> Result<()> {
// SAFETY: This type guarantees the HWND is safe to use,
// and the interface pointer comes from a valid InterfaceRef.
let result = unsafe { RegisterDragDrop(self.0, drop_target.as_raw()) };

match result {
S_OK => Ok(()),
e => Err(Error::new(HRESULT(e), "RegisterDragDrop failed")),
}
}

pub fn revoke_drag_drop(&self) -> Result<()> {
let result = unsafe { RevokeDragDrop(self.0) };

match result {
S_OK => Ok(()),
e => Err(Error::new(HRESULT(e), "RegisterDragDrop failed")),
}
}

pub fn resize_nc_and_activate(&self, size: PhySize) -> Result<()> {
let result = unsafe {
SetWindowPos(
self.0,
null_mut(), // Ignored by SWP_NOZORDER
0, // Ignored by SWP_NOMOVE
0, // Ignored by SWP_NOMOVE
size.width.try_into().unwrap_or(i32::MAX),
size.height.try_into().unwrap_or(i32::MAX),
SWP_NOZORDER | SWP_NOMOVE,
)
};

if result == 0 {
return Err(Error::from_win32());
}

Ok(())
}

pub fn resize_and_activate(&self, client_size: PhySize, style: u32) -> Result<()> {
let rect = Rect::from(client_size).client_area_to_nc_area(style)?;

self.resize_nc_and_activate(rect.size())
}

/// Returns true if the window was previously visible, false otherwise
pub fn show_and_activate(&self) -> bool {
let result = unsafe { ShowWindow(self.0, SW_SHOW) };

result != 0
}

pub fn set_nc_rect(&self, nc_rect: Rect) -> Result<()> {
let size = nc_rect.size();

let result = unsafe {
SetWindowPos(
self.0,
null_mut(), // Ignored by SWP_NOZORDER
nc_rect.0.left,
nc_rect.0.top,
size.width.try_into().unwrap_or(i32::MAX),
size.height.try_into().unwrap_or(i32::MAX),
SWP_NOZORDER | SWP_NOACTIVATE,
)
};

if result == 0 {
return Err(Error::from_win32());
}

Ok(())
}

pub fn set_timer(&self, timer_id: NonZeroUsize, elapse: u32) -> Result<()> {
let result = unsafe { SetTimer(self.0, timer_id.get(), elapse, None) };

if result == 0 {
return Err(Error::from_win32());
}

Ok(())
}

pub fn set_focus(&self) -> Result<()> {
let previous = unsafe { SetFocus(self.0) };
if !previous.is_null() {
return Ok(());
}

// We can't know if a return value of 0 is indicative of an error, or if it's just because the
// previous value was 0. So we check GetLastError instead (called by Error::from_win32).
let error = Error::from_win32();
if error.code() == HRESULT(0) {
return Ok(());
}

Err(error)
}

pub fn destroy(&self) -> Result<()> {
let result = unsafe { DestroyWindow(self.0) };

if result == 0 {
return Err(Error::from_win32());
}

Ok(())
}

pub fn get_focused_window() -> HWND {
// SAFETY: this is always safe to call
unsafe { GetFocus() }
}

pub fn set_capture(&self) {
// SAFETY: This type guarantees the HWND is safe to use.
unsafe { SetCapture(self.0) };
}

pub fn release_capture() {
// SAFETY: this is always safe to call
unsafe { ReleaseCapture() };
}

pub fn start_cursor_leave_tracking(&self) -> Result<()> {
let mut track = TRACKMOUSEEVENT {
cbSize: size_of::<TRACKMOUSEEVENT>() as u32,
dwFlags: TME_LEAVE,
dwHoverTime: 0,
hwndTrack: self.0,
};

// SAFETY: eventtrack pointer comes from a reference, and the struct it points to is filled
// correctly
match unsafe { TrackMouseEvent(&mut track) } {
0 => Err(Error::from_win32()),
_ => Ok(()),
}
}
}
Loading