From ed2816331e6f58a18f85ae70afeae266af683212 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Mar 2024 14:25:05 +0100 Subject: [PATCH] Add support for raw-window-handle (#4918) Add a `raw-window-handle-06` feature to the Rust API crates, which adds support for version 0.6 of rwh to slint::Window, by delegation adding a `window_handle()` function that returns a struct that implements the corresponding traits from rwh. HasDisplayHandle could also be provided on the backend, but that can be done separately if needed. This is only implemented for the winit backend right now. cc #877 --- .github/workflows/build_docs.yaml | 2 +- Cargo.toml | 2 ++ api/rs/slint/Cargo.toml | 10 +++++- internal/backends/selector/Cargo.toml | 2 ++ internal/backends/winit/Cargo.toml | 5 ++- internal/backends/winit/lib.rs | 6 +++- internal/backends/winit/winitwindowadapter.rs | 14 ++++++++ internal/core/Cargo.toml | 8 +++-- internal/core/api.rs | 35 +++++++++++++++++++ internal/core/window.rs | 16 +++++++++ internal/interpreter/Cargo.toml | 9 ++++- 11 files changed, 102 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_docs.yaml b/.github/workflows/build_docs.yaml index 870ced6f666..ebeaea52630 100644 --- a/.github/workflows/build_docs.yaml +++ b/.github/workflows/build_docs.yaml @@ -22,7 +22,7 @@ jobs: - name: Set up rgb crate rustdoc link run: | rgb_version=`grep 'rgb = ' internal/core/Cargo.toml | sed 's/^.*"\(.*\)"/\1/'` - echo "RUSTDOCFLAGS=$RUSTDOCFLAGS --extern-html-root-url rgb=https://docs.rs/rgb/$rgb_version/ --extern-html-root-url android_activity=https://docs.rs/android-activity/0.5/ -Z unstable-options" >> $GITHUB_ENV + echo "RUSTDOCFLAGS=$RUSTDOCFLAGS --extern-html-root-url rgb=https://docs.rs/rgb/$rgb_version/ --extern-html-root-url android_activity=https://docs.rs/android-activity/0.5/ --extern-html-root-url raw_window_handle=https://docs.rs/raw_window_handle/0.6 -Z unstable-options" >> $GITHUB_ENV - uses: ./.github/actions/install-nodejs - name: Cache mdbook and mdbook-linkcheck uses: actions/cache@v4 diff --git a/Cargo.toml b/Cargo.toml index 3b1da5e706b..b2a3d9c3301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,6 +140,8 @@ strum = { version = "0.26.1", default-features = false, features = ["derive"] } toml_edit = { version = "0.22.7" } cfg_aliases = { version = "0.2.0" } +raw-window-handle-06 = { package = "raw-window-handle", version = "0.6", features = ["alloc"] } + [profile.release] lto = true panic = "abort" diff --git a/api/rs/slint/Cargo.toml b/api/rs/slint/Cargo.toml index 18507ce9bf7..d1553ad4309 100644 --- a/api/rs/slint/Cargo.toml +++ b/api/rs/slint/Cargo.toml @@ -78,6 +78,12 @@ unsafe-single-threaded = ["i-slint-core/unsafe-single-threaded"] ## APIs to support screen readers and other assistive technologies. accessibility = ["i-slint-backend-selector/accessibility"] +## Enable integration with [raw-window-handle](raw_window_handle_06) version 0.6. This provides a +## [`Window::window_handle()`] function that returns a struct that implements +## [HasWindowHandle](raw_window_handle_06::HasWindowHandle) and +## [HasDisplayHandle](raw_window_handle_06::HasDisplayHandle) implementation. +raw-window-handle-06 = ["dep:raw-window-handle-06", "i-slint-backend-selector/raw-window-handle-06"] + #! ### Backends #! Slint needs a backend that will act as liaison between Slint and the OS. @@ -175,6 +181,8 @@ num-traits = { version = "0.2", default-features = false } log = { version = "0.4.17", optional = true } +raw-window-handle-06 = { workspace = true, optional = true } + [target.'cfg(not(target_os = "android"))'.dependencies] # FemtoVG is disabled on android because it doesn't compile without setting RUST_FONTCONFIG_DLOPEN=on # end even then wouldn't work because it can't load fonts @@ -200,4 +208,4 @@ rustdoc-args = [ "--html-in-header", "docs/resources/slint-docs-highlight.html", ] -features = ["document-features", "log", "gettext", "renderer-software", "renderer-femtovg"] +features = ["document-features", "log", "gettext", "renderer-software", "renderer-femtovg", "raw-window-handle-06"] diff --git a/internal/backends/selector/Cargo.toml b/internal/backends/selector/Cargo.toml index c0ed65bca98..e067be6ef99 100644 --- a/internal/backends/selector/Cargo.toml +++ b/internal/backends/selector/Cargo.toml @@ -32,6 +32,8 @@ renderer-software = ["i-slint-backend-winit?/renderer-software", "i-slint-core/s rtti = ["i-slint-core/rtti", "i-slint-backend-qt?/rtti"] accessibility = ["i-slint-backend-winit?/accessibility"] +raw-window-handle-06 = ["i-slint-core/raw-window-handle-06", "i-slint-backend-winit?/raw-window-handle-06"] + # note that default enable the i-slint-backend-qt, but not its enable feature default = ["i-slint-backend-qt", "backend-winit"] diff --git a/internal/backends/winit/Cargo.toml b/internal/backends/winit/Cargo.toml index 0507198f35c..08ee04ec519 100644 --- a/internal/backends/winit/Cargo.toml +++ b/internal/backends/winit/Cargo.toml @@ -27,6 +27,7 @@ renderer-skia-opengl = ["renderer-skia", "i-slint-renderer-skia/opengl"] renderer-skia-vulkan = ["renderer-skia", "i-slint-renderer-skia/vulkan"] renderer-software = ["dep:softbuffer", "dep:imgref", "dep:rgb", "i-slint-core/software-renderer-systemfonts", "dep:bytemuck", "winit/rwh_05"] accessibility = ["dep:accesskit", "dep:accesskit_winit"] +raw-window-handle-06 = ["dep:raw-window-handle-06", "winit/rwh_06"] default = [] [dependencies] @@ -58,6 +59,8 @@ imgref = { version = "1.6.1", optional = true } rgb = { version = "0.8.27", optional = true } bytemuck = { workspace = true, optional = true, features = ["derive"] } +raw-window-handle-06 = { workspace = true, optional = true } + [target.'cfg(target_arch = "wasm32")'.dependencies] web-sys = { version = "0.3", features=["HtmlInputElement", "HtmlCanvasElement", "Window", "Document", "Event", "KeyboardEvent", "InputEvent", "CompositionEvent", "DomStringMap", "ClipboardEvent", "DataTransfer"] } wasm-bindgen = { version = "0.2" } @@ -77,7 +80,7 @@ cocoa = { version = "0.25.0" } cfg_aliases = { workspace = true } [dev-dependencies] -slint = { path = "../../../api/rs/slint", default-features = false, features = ["std", "compat-1-2", "backend-winit", "renderer-software"] } +slint = { path = "../../../api/rs/slint", default-features = false, features = ["std", "compat-1-2", "backend-winit", "renderer-software", "raw-window-handle-06"] } [package.metadata.docs.rs] features = ["wayland", "renderer-software"] diff --git a/internal/backends/winit/lib.rs b/internal/backends/winit/lib.rs index cacfa5c5c69..b8a9173f8a5 100644 --- a/internal/backends/winit/lib.rs +++ b/internal/backends/winit/lib.rs @@ -408,11 +408,15 @@ mod testui { // Sorry, can't test with rust test harness and multiple threads. #[cfg(not(any(target_arch = "wasm32", target_os = "macos", target_os = "ios")))] #[test] -fn test_window_accessor() { +fn test_window_accessor_and_rwh() { slint::platform::set_platform(Box::new(crate::Backend::new().unwrap())).unwrap(); use testui::*; let app = App::new().unwrap(); let slint_window = app.window(); assert!(slint_window.has_winit_window()); + let handle = slint_window.window_handle(); + use raw_window_handle_06::{HasDisplayHandle, HasWindowHandle}; + assert!(handle.window_handle().is_ok()); + assert!(handle.display_handle().is_ok()); } diff --git a/internal/backends/winit/winitwindowadapter.rs b/internal/backends/winit/winitwindowadapter.rs index 819b337e562..86e8915655f 100644 --- a/internal/backends/winit/winitwindowadapter.rs +++ b/internal/backends/winit/winitwindowadapter.rs @@ -617,6 +617,20 @@ impl WindowAdapter for WinitWindowAdapter { fn internal(&self, _: corelib::InternalToken) -> Option<&dyn WindowAdapterInternal> { Some(self) } + + #[cfg(feature = "raw-window-handle-06")] + fn window_handle_06( + &self, + ) -> Result, raw_window_handle_06::HandleError> { + raw_window_handle_06::HasWindowHandle::window_handle(&self.winit_window) + } + + #[cfg(feature = "raw-window-handle-06")] + fn display_handle_06( + &self, + ) -> Result, raw_window_handle_06::HandleError> { + raw_window_handle_06::HasDisplayHandle::display_handle(&self.winit_window) + } } impl WindowAdapterInternal for WinitWindowAdapter { diff --git a/internal/core/Cargo.toml b/internal/core/Cargo.toml index 9b97560f3c9..4068ddcafc1 100644 --- a/internal/core/Cargo.toml +++ b/internal/core/Cargo.toml @@ -24,7 +24,7 @@ libm = ["num-traits/libm", "euclid/libm"] # Allow the viewer to query at runtime information about item types rtti = [] # Use the standard library -std = ["euclid/std", "once_cell/std", "scoped-tls-hkt", "lyon_path", "lyon_algorithms", "lyon_geom", "lyon_extra", "dep:web-time", "image-decoders", "svg"] +std = ["euclid/std", "once_cell/std", "scoped-tls-hkt", "lyon_path", "lyon_algorithms", "lyon_geom", "lyon_extra", "dep:web-time", "image-decoders", "svg", "raw-window-handle-06?/std"] # Unsafe feature meaning that there is only one core running and all thread_local are static. # You can only enable this feature if you are sure that any API of this crate is only called # from a single core, and not in a interrupt or signal handler. @@ -44,6 +44,8 @@ box-shadow-cache = [] shared-fontdb = ["i-slint-common/shared-fontdb"] +raw-window-handle-06 = ["dep:raw-window-handle-06"] + default = ["std", "unicode"] [dependencies] @@ -86,6 +88,8 @@ resvg = { workspace = true, optional = true } fontdb = { workspace = true, optional = true } serde = { version = "1.0.163", features = ["derive"], optional = true } +raw-window-handle-06 = { workspace = true, optional = true } + [target.'cfg(target_family = "unix")'.dependencies] gettext-rs = { version = "0.7", optional = true, features = ["gettext-system"] } @@ -107,4 +111,4 @@ ttf-parser = "0.20.0" fontdb = { workspace = true, default-features = true } serde_json = "1.0.96" tiny-skia = "0.11.0" -tokio = { version = "1.35", features = ["rt-multi-thread"] } \ No newline at end of file +tokio = { version = "1.35", features = ["rt-multi-thread"] } diff --git a/internal/core/api.rs b/internal/core/api.rs index 4aef11de405..5127c089e35 100644 --- a/internal/core/api.rs +++ b/internal/core/api.rs @@ -324,6 +324,34 @@ pub enum SetRenderingNotifierError { AlreadySet, } +/// This struct represents a persistent handle to a window and implements the +/// [`raw_window_handle_06::HasWindowHandle`] and [`raw_window_handle_06::HasDisplayHandle`] +/// traits for accessing exposing raw window and display handles. +/// Obtain an instance of this by calling [`Window::window_handle()`]. +#[cfg(feature = "raw-window-handle-06")] +#[derive(Clone)] +pub struct WindowHandle { + adapter: alloc::rc::Rc, +} + +#[cfg(feature = "raw-window-handle-06")] +impl raw_window_handle_06::HasWindowHandle for WindowHandle { + fn window_handle<'a>( + &'a self, + ) -> Result, raw_window_handle_06::HandleError> { + self.adapter.window_handle_06() + } +} + +#[cfg(feature = "raw-window-handle-06")] +impl raw_window_handle_06::HasDisplayHandle for WindowHandle { + fn display_handle<'a>( + &'a self, + ) -> Result, raw_window_handle_06::HandleError> { + self.adapter.display_handle_06() + } +} + /// This type represents a window towards the windowing system, that's used to render the /// scene of a component. It provides API to control windowing system specific aspects such /// as the position on the screen. @@ -571,6 +599,13 @@ impl Window { pub fn is_visible(&self) -> bool { self.0.is_visible() } + + /// Returns a struct that implements the raw window handle traits to access the windowing system specific window + /// and display handles. This function is only accessible if you enable the `raw-window-handle-06` crate feature. + #[cfg(feature = "raw-window-handle-06")] + pub fn window_handle(&self) -> WindowHandle { + WindowHandle { adapter: self.0.window_adapter() } + } } pub use crate::SharedString; diff --git a/internal/core/window.rs b/internal/core/window.rs index 3de16656ca0..2e360e57e16 100644 --- a/internal/core/window.rs +++ b/internal/core/window.rs @@ -137,6 +137,22 @@ pub trait WindowAdapter { fn internal(&self, _: crate::InternalToken) -> Option<&dyn WindowAdapterInternal> { None } + + /// Re-implement this to support exposing raw window handles (version 0.6). + #[cfg(feature = "raw-window-handle-06")] + fn window_handle_06( + &self, + ) -> Result, raw_window_handle_06::HandleError> { + Err(raw_window_handle_06::HandleError::NotSupported) + } + + /// Re-implement this to support exposing raw display handles (version 0.6). + #[cfg(feature = "raw-window-handle-06")] + fn display_handle_06( + &self, + ) -> Result, raw_window_handle_06::HandleError> { + Err(raw_window_handle_06::HandleError::NotSupported) + } } /// Implementation details behind [`WindowAdapter`], but since this diff --git a/internal/interpreter/Cargo.toml b/internal/interpreter/Cargo.toml index 9ecdf227ee9..dcecc0287f7 100644 --- a/internal/interpreter/Cargo.toml +++ b/internal/interpreter/Cargo.toml @@ -107,6 +107,12 @@ renderer-winit-software = ["renderer-software"] ## APIs to support screen readers and other assistive technologies. accessibility = ["i-slint-backend-selector/accessibility"] +## Enable integration with [raw-window-handle](raw_window_handle_06) version 0.6. This provides a +## [`Window::window_handle()`] function that returns a struct that implements +## [HasWindowHandle](raw_window_handle_06::HasWindowHandle) and +## [HasDisplayHandle](raw_window_handle_06::HasDisplayHandle) implementation. +raw-window-handle-06 = ["dep:raw-window-handle-06", "i-slint-backend-selector/raw-window-handle-06"] + ## Features used internally by Slint tooling that are not stable and come without ## any stability guarantees whatsoever. internal = [] @@ -126,6 +132,7 @@ once_cell = "1.5" thiserror = "1" document-features = { version = "0.2.0", optional = true } spin_on = { version = "0.1", optional = true } +raw-window-handle-06 = { workspace = true, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] i-slint-backend-winit = { workspace = true } @@ -140,4 +147,4 @@ i-slint-backend-testing = { path = "../../internal/backends/testing" } spin_on = "0.1" [package.metadata.docs.rs] -features = ["display-diagnostics", "document-features"] +features = ["display-diagnostics", "document-features", "raw-window-handle-06"]