diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 3fb678e306..1d07fe5868 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -17,6 +17,8 @@ default-target = "x86_64-pc-windows-msvc" default = ["gtk"] gtk = ["gio", "gdk", "gdk-sys", "glib", "glib-sys", "gtk-sys", "gtk-rs", "gdk-pixbuf"] x11 = ["x11rb", "nix", "cairo-sys-rs"] +# Implement HasRawWindowHandle for WindowHandle +raw-win-handle = ["raw-window-handle"] # passing on all the image features. AVIF is not supported because it does not # support decoding, and that's all we use `Image` for. @@ -51,6 +53,7 @@ keyboard-types = { version = "0.5.0", default_features = false } # Optional dependencies image = { version = "0.23.12", optional = true, default_features = false } +raw-window-handle = { version = "0.3.3", optional = true, default_features = false } [target.'cfg(target_os="windows")'.dependencies] scopeguard = "1.1.0" diff --git a/druid-shell/src/platform/gtk/window.rs b/druid-shell/src/platform/gtk/window.rs index 0a235ca2f9..907e719b6f 100644 --- a/druid-shell/src/platform/gtk/window.rs +++ b/druid-shell/src/platform/gtk/window.rs @@ -32,6 +32,9 @@ use gio::ApplicationExt; use gtk::prelude::*; use gtk::{AccelGroup, ApplicationWindow, DrawingArea, SettingsExt}; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; use crate::piet::{Piet, PietText, RenderContext}; @@ -94,6 +97,15 @@ pub struct WindowHandle { marker: std::marker::PhantomData<*const ()>, } +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + error!("HasRawWindowHandle trait not implemented for gtk."); + // GTK is not a platform, and there's no empty generic handle. Pick XCB randomly as fallback. + RawWindowHandle::Xcb(XcbHandle::empty()) + } +} + /// Operations that we defer in order to avoid re-entrancy. See the documentation in the windows /// backend for more details. enum DeferredOp { diff --git a/druid-shell/src/platform/mac/window.rs b/druid-shell/src/platform/mac/window.rs index ed74eeff5f..e23e2b63b3 100644 --- a/druid-shell/src/platform/mac/window.rs +++ b/druid-shell/src/platform/mac/window.rs @@ -40,6 +40,9 @@ use objc::rc::WeakPtr; use objc::runtime::{Class, Object, Sel}; use objc::{class, msg_send, sel, sel_impl}; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; + use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; use crate::piet::{Piet, PietText, RenderContext}; @@ -1214,6 +1217,18 @@ impl WindowHandle { } } +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + let nsv = self.nsview.load(); + let handle = MacOSHandle { + ns_view: *nsv as *mut _, + ..MacOSHandle::empty() + }; + RawWindowHandle::MacOS(handle) + } +} + unsafe impl Send for IdleHandle {} impl IdleHandle { diff --git a/druid-shell/src/platform/web/window.rs b/druid-shell/src/platform/web/window.rs index 3e16852106..99a226bced 100644 --- a/druid-shell/src/platform/web/window.rs +++ b/druid-shell/src/platform/web/window.rs @@ -25,6 +25,9 @@ use instant::Instant; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; use crate::piet::{PietText, RenderContext}; @@ -75,6 +78,14 @@ pub(crate) struct WindowBuilder { #[derive(Clone, Default)] pub struct WindowHandle(Weak); +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + error!("HasRawWindowHandle trait not implemented for wasm."); + RawWindowHandle::Web(WebHandle::empty()) + } +} + /// A handle that can get used to schedule an idle handler. Note that /// this handle is thread safe. #[derive(Clone)] diff --git a/druid-shell/src/platform/windows/window.rs b/druid-shell/src/platform/windows/window.rs index 1976493cff..32d8f1d91a 100644 --- a/druid-shell/src/platform/windows/window.rs +++ b/druid-shell/src/platform/windows/window.rs @@ -44,6 +44,9 @@ use winapi::um::wingdi::*; use winapi::um::winnt::*; use winapi::um::winuser::*; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle}; + use piet_common::d2d::{D2DFactory, DeviceContext}; use piet_common::dwrite::DwriteFactory; @@ -159,6 +162,26 @@ pub struct WindowHandle { state: Weak, } +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + if let Some(hwnd) = self.get_hwnd() { + let handle = WindowsHandle { + hwnd: hwnd as *mut core::ffi::c_void, + hinstance: unsafe { + winapi::um::libloaderapi::GetModuleHandleW(0 as winapi::um::winnt::LPCWSTR) + as *mut core::ffi::c_void + }, + ..WindowsHandle::empty() + }; + RawWindowHandle::Windows(handle) + } else { + error!("Cannot retrieved HWND for window."); + RawWindowHandle::Windows(WindowsHandle::empty()) + } + } +} + /// A handle that can get used to schedule an idle handler. Note that /// this handle is thread safe. If the handle is used after the hwnd /// has been destroyed, probably not much will go wrong (the DS_RUN_IDLE diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 87f7a1e0a0..14f6343f74 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -37,6 +37,9 @@ use x11rb::protocol::xproto::{ use x11rb::wrapper::ConnectionExt as _; use x11rb::xcb_ffi::XCBConnection; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; + use crate::common_util::IdleCallback; use crate::dialog::FileDialogOptions; use crate::error::Error as ShellError; @@ -1547,3 +1550,11 @@ impl WindowHandle { } } } + +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + error!("HasRawWindowHandle trait not implemented for x11."); + RawWindowHandle::Xcb(XcbHandle::empty()) + } +} diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index eb779d6e2a..16f4ad1725 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -29,6 +29,8 @@ use crate::platform::window as platform; use crate::region::Region; use crate::scale::Scale; use piet_common::PietText; +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; /// A token that uniquely identifies a running timer. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)] @@ -338,6 +340,13 @@ impl WindowHandle { } } +#[cfg(feature = "raw-win-handle")] +unsafe impl HasRawWindowHandle for WindowHandle { + fn raw_window_handle(&self) -> RawWindowHandle { + self.0.raw_window_handle() + } +} + /// A builder type for creating new windows. pub struct WindowBuilder(platform::WindowBuilder);