From 3414f535ac0a605f99d9295fe88dded040ce1f91 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 23 Mar 2024 11:52:04 -0700 Subject: [PATCH] feat: Add AnyThread wrapper This type can be used to ignore thread affinity for types that already take `HasWindowHandle`. Signed-off-by: John Nunley --- src/platform/windows.rs | 92 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 0558534a10..67a1f92ae9 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -2,7 +2,7 @@ //! //! The supported OS version is Windows 7 or higher, though Windows 10 is //! tested regularly. -use std::{ffi::c_void, path::Path}; +use std::{borrow::Borrow, ffi::c_void, path::Path}; use crate::{ dpi::PhysicalSize, @@ -105,6 +105,62 @@ pub enum CornerPreference { RoundSmall = 3, } +/// A wrapper around a [`Window`] that ignores thread-specific window handle limitations. +/// +/// See [`WindowBorrowExtWindows::any_thread`] for more information. +#[derive(Debug)] +pub struct AnyThread(W); + +impl> AnyThread { + /// Get a reference to the inner window. + #[inline] + pub fn get_ref(&self) -> &Window { + self.0.borrow() + } + + /// Get a reference to the inner object. + #[inline] + pub fn inner(&self) -> &W { + &self.0 + } + + /// Unwrap and get the inner window. + #[inline] + pub fn into_inner(self) -> W { + self.0 + } +} + +impl> AsRef for AnyThread { + fn as_ref(&self) -> &Window { + self.get_ref() + } +} + +impl> Borrow for AnyThread { + fn borrow(&self) -> &Window { + self.get_ref() + } +} + +impl> std::ops::Deref for AnyThread { + type Target = Window; + + fn deref(&self) -> &Self::Target { + self.get_ref() + } +} + +#[cfg(feature = "rwh_06")] +impl> rwh_06::HasWindowHandle for AnyThread { + fn window_handle(&self) -> Result, rwh_06::HandleError> { + // SAFETY: The top level user has asserted this is only used safely. + unsafe { + self.get_ref().window_handle_any_thread() + } + } +} + /// Additional methods on `EventLoop` that are specific to Windows. pub trait EventLoopBuilderExtWindows { /// Whether to allow the event loop to be created off of the main thread. @@ -265,6 +321,11 @@ pub trait WindowExtWindows { /// However in some cases you may already know that you are using the window handle for /// operations that are guaranteed to be thread-safe. In which case this function aims /// to provide an escape hatch so these functions are still accessible from other threads. + /// + /// # Safety + /// + /// It is the responsibility of the user to only pass the window handle into thread-safe + /// Win32 APIs. /// /// [`SetWindowSubclass`]: https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass /// [`GetDC`]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc @@ -363,6 +424,35 @@ impl WindowExtWindows for Window { } } +/// Additional methods for anything that dereference to [`Window`]. +/// +/// [`Window`]: crate::window::Window +pub trait WindowBorrowExtWindows : Borrow + Sized { + /// Create an object that allows accessing the inner window handle in a thread-unsafe way. + /// + /// It is possible to call [`window_handle_any_thread`] to get around Windows's thread + /// affinity limitations. However, it may be desired to pass the [`Window`] into something + /// that requires the [`HasWindowHandle`] trait, while ignoring thread affinity limitations. + /// + /// This function wraps anything that implements `Borrow` into a structure that + /// uses the inner window handle as a mean of implementing [`HasWindowHandle`]. It wraps + /// `Window`, `&Window`, `Arc`, and other reference types. + /// + /// # Safety + /// + /// It is the responsibility of the user to only pass the window handle into thread-safe + /// Win32 APIs. + /// + /// [`window_handle_any_thread`]: WindowExtWindows::window_handle_any_thread + /// [`Window`]: crate::window::Window + /// [`HasWindowHandle`]: rwh_06::HasWindowHandle + unsafe fn any_thread(self) -> AnyThread { + AnyThread(self) + } +} + +impl + Sized> WindowBorrowExtWindows for W {} + /// Additional methods on `WindowAttributes` that are specific to Windows. #[allow(rustdoc::broken_intra_doc_links)] pub trait WindowAttributesExtWindows {