From b23e38efcb707464b7af6bac0edfc8420b213d2c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 Sep 2025 18:47:51 +0200 Subject: [PATCH] Add some missing Window getters Implemented properly when it was easy for me, but a lot of places are stubbed out for now. --- winit-android/src/event_loop.rs | 32 +++++++++++ winit-appkit/src/window.rs | 34 +++++++++++- winit-appkit/src/window_delegate.rs | 62 +++++++++++++++++++++ winit-core/src/window.rs | 86 ++++++++++++++++++++++++++++- winit-orbital/src/window.rs | 43 +++++++++++++++ winit-uikit/src/window.rs | 76 ++++++++++++++++++++++++- winit-wayland/src/window/mod.rs | 35 ++++++++++++ winit-wayland/src/window/state.rs | 21 +++++++ winit-web/src/window.rs | 36 ++++++++++++ winit-win32/src/window.rs | 78 ++++++++++++++++++++++---- winit-x11/src/window.rs | 67 ++++++++++++++++++++++ winit/src/changelog/unreleased.md | 8 +++ 12 files changed, 562 insertions(+), 16 deletions(-) diff --git a/winit-android/src/event_loop.rs b/winit-android/src/event_loop.rs index 1396574cb..7c2481015 100644 --- a/winit-android/src/event_loop.rs +++ b/winit-android/src/event_loop.rs @@ -876,8 +876,16 @@ impl CoreWindow for Window { PhysicalInsets::new(0, 0, 0, 0) } + fn min_surface_size(&self) -> Option> { + None + } + fn set_min_surface_size(&self, _: Option) {} + fn max_surface_size(&self) -> Option> { + None + } + fn set_max_surface_size(&self, _: Option) {} fn surface_resize_increments(&self) -> Option> { @@ -888,8 +896,16 @@ impl CoreWindow for Window { fn set_title(&self, _title: &str) {} + fn is_transparent(&self) -> bool { + false + } + fn set_transparent(&self, _transparent: bool) {} + fn is_blurred(&self) -> bool { + false + } + fn set_blur(&self, _blur: bool) {} fn set_visible(&self, _visibility: bool) {} @@ -936,8 +952,16 @@ impl CoreWindow for Window { true } + fn window_level(&self) -> WindowLevel { + WindowLevel::default() + } + fn set_window_level(&self, _level: WindowLevel) {} + fn window_icon(&self) -> Option { + None + } + fn set_window_icon(&self, _window_icon: Option) {} fn set_ime_cursor_area(&self, _position: Position, _size: Size) {} @@ -977,6 +1001,10 @@ impl CoreWindow for Window { fn request_user_attention(&self, _request_type: Option) {} + fn cursor(&self) -> Cursor { + Cursor::default() + } + fn set_cursor(&self, _: Cursor) {} fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> { @@ -1010,6 +1038,10 @@ impl CoreWindow for Window { None } + fn content_protected(&self) -> bool { + false + } + fn set_content_protected(&self, _protected: bool) {} fn has_focus(&self) -> bool { diff --git a/winit-appkit/src/window.rs b/winit-appkit/src/window.rs index 515ac55f7..3d3eb50a5 100644 --- a/winit-appkit/src/window.rs +++ b/winit-appkit/src/window.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use dispatch2::MainThreadBound; -use dpi::{Position, Size}; +use dpi::{PhysicalSize, Position, Size}; use objc2::rc::{autoreleasepool, Retained}; use objc2::{define_class, MainThreadMarker, Message}; use objc2_app_kit::{NSPanel, NSResponder, NSWindow}; @@ -141,10 +141,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.safe_area()) } + fn min_surface_size(&self) -> Option> { + self.maybe_wait_on_main(|delegate| delegate.min_surface_size()) + } + fn set_min_surface_size(&self, min_size: Option) { self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size)) } + fn max_surface_size(&self) -> Option> { + self.maybe_wait_on_main(|delegate| delegate.max_surface_size()) + } + fn set_max_surface_size(&self, max_size: Option) { self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size)); } @@ -161,10 +169,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.set_title(title)); } + fn is_transparent(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.is_transparent()) + } + fn set_transparent(&self, transparent: bool) { self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent)); } + fn is_blurred(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.is_blurred()) + } + fn set_blur(&self, blur: bool) { self.maybe_wait_on_main(|delegate| delegate.set_blur(blur)); } @@ -225,10 +241,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.is_decorated()) } + fn window_level(&self) -> WindowLevel { + self.maybe_wait_on_main(|delegate| delegate.window_level()) + } + fn set_window_level(&self, level: WindowLevel) { self.maybe_wait_on_main(|delegate| delegate.set_window_level(level)); } + fn window_icon(&self) -> Option { + self.maybe_wait_on_main(|delegate| delegate.window_icon()) + } + fn set_window_icon(&self, window_icon: Option) { self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon)); } @@ -261,6 +285,10 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.theme()) } + fn content_protected(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.content_protected()) + } + fn set_content_protected(&self, protected: bool) { self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected)); } @@ -269,6 +297,10 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.title()) } + fn cursor(&self) -> Cursor { + self.maybe_wait_on_main(|delegate| delegate.cursor()) + } + fn set_cursor(&self, cursor: Cursor) { self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor)); } diff --git a/winit-appkit/src/window_delegate.rs b/winit-appkit/src/window_delegate.rs index 9d9825f40..051a11e8f 100644 --- a/winit-appkit/src/window_delegate.rs +++ b/winit-appkit/src/window_delegate.rs @@ -934,6 +934,10 @@ impl WindowDelegate { self.window().setTitle(&NSString::from_str(title)) } + pub fn is_transparent(&self) -> bool { + unsafe { !self.window().isOpaque() } + } + pub fn set_transparent(&self, transparent: bool) { // This is just a hint for Quartz, it doesn't actually speculate with window alpha. // Providing a wrong value here could result in visual artifacts, when the window is @@ -954,6 +958,12 @@ impl WindowDelegate { self.window().setBackgroundColor(Some(&color)); } + pub fn is_blurred(&self) -> bool { + // What API would we use to get this? `CGSGetWindowBackgroundBlurRadius` doesn't exist. + warn!("getting the background blur of the window is not supported on macOS"); + false + } + pub fn set_blur(&self, blur: bool) { // NOTE: in general we want to specify the blur radius, but the choice of 80 // should be a reasonable default. @@ -1073,6 +1083,14 @@ impl WindowDelegate { None } + pub fn min_surface_size(&self) -> Option> { + let size = unsafe { self.window().contentMinSize() }; + if size == NSSize::ZERO { + return None; + } + Some(LogicalSize::new(size.width, size.height).to_physical(self.scale_factor())) + } + pub fn set_min_surface_size(&self, dimensions: Option) { let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize { width: 0.0, height: 0.0 })); @@ -1092,6 +1110,15 @@ impl WindowDelegate { self.window().setContentSize(current_size); } + pub fn max_surface_size(&self) -> Option> { + let size = unsafe { self.window().contentMaxSize() }; + // AppKit sets the max size to f32::MAX by default. + if size == NSSize::new(f32::MAX as _, f32::MAX as _) { + return None; + } + Some(LogicalSize::new(size.width, size.height).to_physical(self.scale_factor())) + } + pub fn set_max_surface_size(&self, dimensions: Option) { let dimensions = dimensions.unwrap_or(Size::Logical(LogicalSize { width: f32::MAX as f64, @@ -1213,6 +1240,11 @@ impl WindowDelegate { buttons } + pub fn cursor(&self) -> Cursor { + warn!("getting the cursor is unimplemented on macOS"); + Cursor::default() + } + pub fn set_cursor(&self, cursor: Cursor) { let view = self.view(); @@ -1643,6 +1675,20 @@ impl WindowDelegate { self.ivars().decorations.get() } + pub fn window_level(&self) -> WindowLevel { + let level = unsafe { self.window().level() }; + if level == kCGFloatingWindowLevel as NSWindowLevel { + WindowLevel::AlwaysOnTop + } else if level == (kCGNormalWindowLevel - 1) as NSWindowLevel { + WindowLevel::AlwaysOnBottom + } else if level == kCGNormalWindowLevel as NSWindowLevel { + WindowLevel::Normal + } else { + warn!(?level, "cannot determine window level, it must've been set outside of Winit"); + WindowLevel::default() + } + } + #[inline] pub fn set_window_level(&self, level: WindowLevel) { // Note: There are two different things at play here: @@ -1662,6 +1708,11 @@ impl WindowDelegate { self.window().setLevel(level); } + #[inline] + pub fn window_icon(&self) -> Option { + None + } + #[inline] pub fn set_window_icon(&self, _icon: Option) { // macOS doesn't have window icons. Though, there is @@ -1815,6 +1866,17 @@ impl WindowDelegate { unsafe { self.window().setAppearance(theme_to_appearance(theme).as_deref()) }; } + pub fn content_protected(&self) -> bool { + match unsafe { self.window().sharingType() } { + NSWindowSharingType::None => true, + NSWindowSharingType::ReadOnly => false, + sharing_type => { + warn!(?sharing_type, "unknown sharing type"); + false + }, + } + } + #[inline] pub fn set_content_protected(&self, protected: bool) { self.window().setSharingType(if protected { diff --git a/winit-core/src/window.rs b/winit-core/src/window.rs index 055935045..8246d1bf4 100644 --- a/winit-core/src/window.rs +++ b/winit-core/src/window.rs @@ -792,6 +792,16 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// ``` fn safe_area(&self) -> PhysicalInsets; + /// The minimum dimensions of the window's surface if it was set. + /// + /// Getter for [`set_min_surface_size`][Window::set_min_surface_size], see that for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Orbital:** Unsupported. + /// - **Web:** Unimplemented, returns `None`. + fn min_surface_size(&self) -> Option>; + /// Sets a minimum dimensions of the window's surface. /// /// ```no_run @@ -811,6 +821,16 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// - **iOS / Android / Orbital:** Unsupported. fn set_min_surface_size(&self, min_size: Option); + /// The maximum dimensions of the window's surface if it was set. + /// + /// Getter for [`set_max_surface_size`][Window::set_max_surface_size], see that for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Orbital:** Unsupported. + /// - **Web:** Unimplemented, returns `None`. + fn max_surface_size(&self) -> Option>; + /// Sets a maximum dimensions of the window's surface. /// /// ```no_run @@ -850,6 +870,16 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// - **iOS / Android / Web / Orbital:** Unsupported. fn set_surface_resize_increments(&self, increments: Option); + /// The window transparency state. + /// + /// Getter for [`set_transparent`][Window::set_transparent], see that for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web:** Unsupported. + /// - **X11:** Unimplemented, returns `false`. + fn is_transparent(&self) -> bool; + /// Change the window transparency state. /// /// This is just a hint that may not change anything about @@ -867,6 +897,15 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// [`WindowAttributes::with_transparent`]. fn set_transparent(&self, transparent: bool); + /// The window blur state. + /// + /// Getter for [`set_blur`][Window::set_blur], see that for details. + /// + /// ## Platform-specific + /// + /// - **Android / iOS / X11 / Web / Windows / macOS:** Unsupported. + fn is_blurred(&self) -> bool; + /// Change the window blur state. /// /// If `true`, this will make the transparent window background blurry. @@ -1029,13 +1068,39 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// - **iOS / Android / Web:** No effect. fn set_decorations(&self, decorations: bool); + /// The window level. + /// + /// Getter for [`set_window_level`][Window::set_window_level], see that and [`WindowLevel`] for + /// details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web / Wayland:** Unsupported. + /// - **X11:** Unimplemented, returns the default window level. + fn window_level(&self) -> WindowLevel; + /// Change the window level. /// /// This is just a hint to the OS, and the system could ignore it. /// /// See [`WindowLevel`] for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web / Wayland:** Unsupported. fn set_window_level(&self, level: WindowLevel); + /// The window icon, if any was set. + /// + /// Getter for [`set_window_icon`][Window::set_window_icon], see that for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Web / macOS / Orbital:** Unsupported. + /// - **Windows:** The icon may be different from the one set with `set_window_icon`. + /// - **X11 / Wayland:** Unimplemented, returns `None`. + fn window_icon(&self) -> Option; + /// Sets the window icon. /// /// On Windows, Wayland and X11, this is typically the small icon in the top-left @@ -1043,7 +1108,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// /// ## Platform-specific /// - /// - **iOS / Android / Web / / macOS / Orbital:** Unsupported. + /// - **iOS / Android / Web / macOS / Orbital:** Unsupported. /// /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. @@ -1273,13 +1338,20 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// - **iOS / Android / Web / Orbital:** Unsupported. fn set_theme(&self, theme: Option); + /// Whether the window's contents are prevented from being captured by other apps. + /// + /// Getter for [`set_content_protected`][Window::set_content_protected], see that for details. + /// + /// - **iOS / Android / X11 / Wayland / Web / Orbital:** Unsupported. + fn content_protected(&self) -> bool; + /// Prevents the window contents from being captured by other apps. /// /// ## Platform-specific /// /// - **macOS**: if `false`, [`NSWindowSharingNone`] is used but doesn't completely prevent all /// apps from reading the window content, for instance, QuickTime. - /// - **iOS / Android / x11 / Wayland / Web / Orbital:** Unsupported. + /// - **iOS / Android / X11 / Wayland / Web / Orbital:** Unsupported. /// /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone fn set_content_protected(&self, protected: bool); @@ -1298,6 +1370,16 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// - **iOS / Android:** Unsupported. fn set_title(&self, title: &str); + /// The cursor icon of the window. + /// + /// Getter for [`set_cursor`][Window::set_cursor], see that for details. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Orbital:** Unsupported. + /// - **macOS / X11 / Wayland / Web / Windows:** Unimplemented, returns the default cursor. + fn cursor(&self) -> Cursor; + /// Modifies the cursor icon of the window. /// /// ## Platform-specific diff --git a/winit-orbital/src/window.rs b/winit-orbital/src/window.rs index 4858b29b3..12565da6f 100644 --- a/winit-orbital/src/window.rs +++ b/winit-orbital/src/window.rs @@ -3,6 +3,7 @@ use std::iter; use std::sync::{Arc, Mutex}; use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; +use tracing::warn; use winit_core::cursor::Cursor; use winit_core::error::{NotSupportedError, RequestError}; use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; @@ -252,9 +253,17 @@ impl CoreWindow for Window { PhysicalInsets::new(0, 0, 0, 0) } + fn min_surface_size(&self) -> Option> { + None + } + #[inline] fn set_min_surface_size(&self, _: Option) {} + fn max_surface_size(&self) -> Option> { + None + } + #[inline] fn set_max_surface_size(&self, _: Option) {} @@ -271,11 +280,19 @@ impl CoreWindow for Window { self.window_socket.write(format!("T,{title}").as_bytes()).expect("failed to set title"); } + fn is_transparent(&self) -> bool { + self.get_flag(ORBITAL_FLAG_TRANSPARENT).unwrap_or(false) + } + #[inline] fn set_transparent(&self, transparent: bool) { let _ = self.set_flag(ORBITAL_FLAG_TRANSPARENT, transparent); } + fn is_blurred(&self) -> bool { + false + } + #[inline] fn set_blur(&self, _blur: bool) {} @@ -359,6 +376,20 @@ impl CoreWindow for Window { !self.get_flag(ORBITAL_FLAG_BORDERLESS).unwrap_or(false) } + fn window_level(&self) -> window::WindowLevel { + let back = self.get_flag(ORBITAL_FLAG_BACK).unwrap_or(false); + let front = self.get_flag(ORBITAL_FLAG_FRONT).unwrap_or(false); + match (back, front) { + (true, false) => window::WindowLevel::AlwaysOnBottom, + (false, false) => window::WindowLevel::Normal, + (false, true) => window::WindowLevel::AlwaysOnTop, + (true, true) => { + warn!("unclear back/front window levelling"); + window::WindowLevel::default() + }, + } + } + #[inline] fn set_window_level(&self, level: window::WindowLevel) { match level { @@ -375,6 +406,10 @@ impl CoreWindow for Window { } } + fn window_icon(&self) -> Option { + None + } + #[inline] fn set_window_icon(&self, _window_icon: Option) {} @@ -388,6 +423,10 @@ impl CoreWindow for Window { #[inline] fn request_user_attention(&self, _request_type: Option) {} + fn cursor(&self) -> Cursor { + Cursor::default() + } + #[inline] fn set_cursor(&self, _: Cursor) {} @@ -470,6 +509,10 @@ impl CoreWindow for Window { #[inline] fn set_theme(&self, _theme: Option) {} + fn content_protected(&self) -> bool { + false + } + fn set_content_protected(&self, _protected: bool) {} fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle { diff --git a/winit-uikit/src/window.rs b/winit-uikit/src/window.rs index 52acb038f..a26206c1e 100644 --- a/winit-uikit/src/window.rs +++ b/winit-uikit/src/window.rs @@ -122,10 +122,20 @@ impl Inner { debug!("`Window::set_title` is ignored on iOS") } + pub fn is_transparent(&self) -> bool { + debug!("`Window::is_transparent` is ignored on iOS"); + false + } + pub fn set_transparent(&self, _transparent: bool) { debug!("`Window::set_transparent` is ignored on iOS") } + pub fn is_blurred(&self) -> bool { + debug!("`Window::is_blurred` is ignored on iOS"); + false + } + pub fn set_blur(&self, _blur: bool) { debug!("`Window::set_blur` is ignored on iOS") } @@ -216,10 +226,20 @@ impl Inner { insets.to_physical(self.scale_factor()) } + pub fn min_surface_size(&self) -> Option> { + debug!("`Window::min_surface_size` is ignored on iOS"); + None + } + pub fn set_min_surface_size(&self, _dimensions: Option) { warn!("`Window::set_min_surface_size` is ignored on iOS") } + pub fn max_surface_size(&self) -> Option> { + debug!("`Window::max_surface_size` is ignored on iOS"); + None + } + pub fn set_max_surface_size(&self, _dimensions: Option) { warn!("`Window::set_max_surface_size` is ignored on iOS") } @@ -257,6 +277,11 @@ impl Inner { self.view.contentScaleFactor() as _ } + pub fn cursor(&self) -> Cursor { + warn!("`Window::cursor` is ignored on iOS"); + Cursor::default() + } + pub fn set_cursor(&self, _cursor: Cursor) { debug!("`Window::set_cursor` ignored on iOS") } @@ -371,10 +396,20 @@ impl Inner { true } + pub fn window_level(&self) -> WindowLevel { + debug!("`Window::window_level` is ignored on iOS"); + WindowLevel::default() + } + pub fn set_window_level(&self, _level: WindowLevel) { warn!("`Window::set_window_level` is ignored on iOS") } + pub fn window_icon(&self) -> Option { + debug!("`Window::window_icon` is ignored on iOS"); + None + } + pub fn set_window_icon(&self, _icon: Option) { warn!("`Window::set_window_icon` is ignored on iOS") } @@ -462,7 +497,14 @@ impl Inner { None } - pub fn set_content_protected(&self, _protected: bool) {} + pub fn content_protected(&self) -> bool { + warn!("`Window::content_protected` is ignored on iOS"); + false + } + + pub fn set_content_protected(&self, _protected: bool) { + warn!("`Window::set_content_protected` is ignored on iOS"); + } pub fn has_focus(&self) -> bool { self.window.isKeyWindow() @@ -641,10 +683,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.safe_area()) } + fn min_surface_size(&self) -> Option> { + self.maybe_wait_on_main(|delegate| delegate.min_surface_size()) + } + fn set_min_surface_size(&self, min_size: Option) { self.maybe_wait_on_main(|delegate| delegate.set_min_surface_size(min_size)) } + fn max_surface_size(&self) -> Option> { + self.maybe_wait_on_main(|delegate| delegate.max_surface_size()) + } + fn set_max_surface_size(&self, max_size: Option) { self.maybe_wait_on_main(|delegate| delegate.set_max_surface_size(max_size)); } @@ -661,10 +711,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.set_title(title)); } + fn is_transparent(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.is_transparent()) + } + fn set_transparent(&self, transparent: bool) { self.maybe_wait_on_main(|delegate| delegate.set_transparent(transparent)); } + fn is_blurred(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.is_blurred()) + } + fn set_blur(&self, blur: bool) { self.maybe_wait_on_main(|delegate| delegate.set_blur(blur)); } @@ -725,10 +783,18 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.is_decorated()) } + fn window_level(&self) -> WindowLevel { + self.maybe_wait_on_main(|delegate| delegate.window_level()) + } + fn set_window_level(&self, level: WindowLevel) { self.maybe_wait_on_main(|delegate| delegate.set_window_level(level)); } + fn window_icon(&self) -> Option { + self.maybe_wait_on_main(|delegate| delegate.window_icon()) + } + fn set_window_icon(&self, window_icon: Option) { self.maybe_wait_on_main(|delegate| delegate.set_window_icon(window_icon)); } @@ -761,6 +827,10 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.theme()) } + fn content_protected(&self) -> bool { + self.maybe_wait_on_main(|delegate| delegate.content_protected()) + } + fn set_content_protected(&self, protected: bool) { self.maybe_wait_on_main(|delegate| delegate.set_content_protected(protected)); } @@ -769,6 +839,10 @@ impl CoreWindow for Window { self.maybe_wait_on_main(|delegate| delegate.title()) } + fn cursor(&self) -> Cursor { + self.maybe_wait_on_main(|delegate| delegate.cursor()) + } + fn set_cursor(&self, cursor: Cursor) { self.maybe_wait_on_main(|delegate| delegate.set_cursor(cursor)); } diff --git a/winit-wayland/src/window/mod.rs b/winit-wayland/src/window/mod.rs index fb5f22fea..e4d252bd9 100644 --- a/winit-wayland/src/window/mod.rs +++ b/winit-wayland/src/window/mod.rs @@ -351,6 +351,11 @@ impl CoreWindow for Window { PhysicalInsets::new(0, 0, 0, 0) } + fn min_surface_size(&self) -> Option> { + let size = self.window_state.lock().unwrap().min_surface_size(); + size.map(|size| size.to_physical(self.scale_factor())) + } + fn set_min_surface_size(&self, min_size: Option) { let scale_factor = self.scale_factor(); let min_size = min_size.map(|size| size.to_logical(scale_factor)); @@ -359,6 +364,11 @@ impl CoreWindow for Window { self.request_redraw(); } + fn max_surface_size(&self) -> Option> { + let size = self.window_state.lock().unwrap().max_surface_size(); + size.map(|size| size.to_physical(self.scale_factor())) + } + /// Set the maximum surface size for the window. #[inline] fn set_max_surface_size(&self, max_size: Option) { @@ -382,6 +392,10 @@ impl CoreWindow for Window { self.window_state.lock().unwrap().set_title(new_title); } + fn is_transparent(&self) -> bool { + self.window_state.lock().unwrap().is_transparent() + } + #[inline] fn set_transparent(&self, transparent: bool) { self.window_state.lock().unwrap().set_transparent(transparent); @@ -487,6 +501,10 @@ impl CoreWindow for Window { self.window_state.lock().unwrap().scale_factor() } + fn is_blurred(&self) -> bool { + self.window_state.lock().unwrap().is_blurred() + } + #[inline] fn set_blur(&self, blur: bool) { self.window_state.lock().unwrap().set_blur(blur); @@ -502,8 +520,16 @@ impl CoreWindow for Window { self.window_state.lock().unwrap().is_decorated() } + fn window_level(&self) -> WindowLevel { + WindowLevel::default() + } + fn set_window_level(&self, _level: WindowLevel) {} + fn window_icon(&self) -> Option { + self.window_state.lock().unwrap().window_icon() + } + fn set_window_icon(&self, window_icon: Option) { self.window_state.lock().unwrap().set_window_icon(window_icon) } @@ -566,8 +592,17 @@ impl CoreWindow for Window { self.window_state.lock().unwrap().theme() } + fn content_protected(&self) -> bool { + false + } + fn set_content_protected(&self, _protected: bool) {} + fn cursor(&self) -> Cursor { + warn!("getting the cursor is unimplemented on Wayland"); + Cursor::default() + } + fn set_cursor(&self, cursor: Cursor) { let window_state = &mut self.window_state.lock().unwrap(); diff --git a/winit-wayland/src/window/state.rs b/winit-wayland/src/window/state.rs index 899b0e017..2c8865945 100644 --- a/winit-wayland/src/window/state.rs +++ b/winit-wayland/src/window/state.rs @@ -780,6 +780,10 @@ impl WindowState { }); } + pub fn min_surface_size(&self) -> Option> { + self.min_surface_size + } + /// Set maximum inner window size. pub fn set_min_surface_size(&mut self, size: Option>) { // Ensure that the window has the right minimum size. @@ -798,6 +802,10 @@ impl WindowState { self.window.set_min_size(Some(size.into())); } + pub fn max_surface_size(&self) -> Option> { + self.max_surface_size + } + /// Set maximum inner window size. pub fn set_max_surface_size(&mut self, size: Option>) { let size = size.map(|size| { @@ -1059,6 +1067,10 @@ impl WindowState { } } + pub fn is_blurred(&self) -> bool { + self.blur.is_some() + } + /// Make window background blurred #[inline] pub fn set_blur(&mut self, blurred: bool) { @@ -1099,6 +1111,11 @@ impl WindowState { self.title = title; } + pub fn window_icon(&self) -> Option { + warn!("getting the window icon is unimplemented on Wayland"); + None + } + /// Set the window's icon pub fn set_window_icon(&mut self, window_icon: Option) { let xdg_toplevel_icon_manager = match self.xdg_toplevel_icon_manager.as_ref() { @@ -1138,6 +1155,10 @@ impl WindowState { } } + pub fn is_transparent(&self) -> bool { + self.transparent + } + /// Mark the window as transparent. #[inline] pub fn set_transparent(&mut self, transparent: bool) { diff --git a/winit-web/src/window.rs b/winit-web/src/window.rs index a3579a33b..91aa4dc78 100644 --- a/winit-web/src/window.rs +++ b/winit-web/src/window.rs @@ -6,6 +6,7 @@ use dpi::{ LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size, }; +use tracing::warn; use web_sys::HtmlCanvasElement; use winit_core::cursor::Cursor; use winit_core::error::{NotSupportedError, RequestError}; @@ -192,6 +193,11 @@ impl RootWindow for Window { }) } + fn min_surface_size(&self) -> Option> { + warn!("min_surface_size is not implemented on Web"); + None + } + fn set_min_surface_size(&self, min_size: Option) { self.inner.dispatch(move |inner| { let dimensions = min_size.map(|min_size| min_size.to_logical(inner.scale_factor())); @@ -204,6 +210,11 @@ impl RootWindow for Window { }) } + fn max_surface_size(&self) -> Option> { + warn!("max_surface_size is not implemented on Web"); + None + } + fn set_max_surface_size(&self, max_size: Option) { self.inner.dispatch(move |inner| { let dimensions = max_size.map(|dimensions| dimensions.to_logical(inner.scale_factor())); @@ -228,8 +239,16 @@ impl RootWindow for Window { self.inner.queue(|inner| inner.canvas.set_attribute("alt", title)) } + fn is_transparent(&self) -> bool { + false + } + fn set_transparent(&self, _: bool) {} + fn is_blurred(&self) -> bool { + false + } + fn set_blur(&self, _: bool) {} fn set_visible(&self, _: bool) { @@ -300,10 +319,18 @@ impl RootWindow for Window { true } + fn window_level(&self) -> WindowLevel { + WindowLevel::default() + } + fn set_window_level(&self, _: WindowLevel) { // Intentionally a no-op, no window ordering } + fn window_icon(&self) -> Option { + None + } + fn set_window_icon(&self, _: Option) { // Currently an intentional no-op } @@ -344,12 +371,21 @@ impl RootWindow for Window { }) } + fn content_protected(&self) -> bool { + false + } + fn set_content_protected(&self, _: bool) {} fn title(&self) -> String { String::new() } + fn cursor(&self) -> Cursor { + warn!("getting the cursor is not implemented on Web"); + Cursor::default() + } + fn set_cursor(&self, cursor: Cursor) { self.inner.dispatch(move |inner| inner.canvas.cursor.set_cursor(cursor)) } diff --git a/winit-win32/src/window.rs b/winit-win32/src/window.rs index 61a9d2ed9..ef41ce9e5 100644 --- a/winit-win32/src/window.rs +++ b/winit-win32/src/window.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::{io, panic, ptr}; use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; -use tracing::warn; +use tracing::{error, warn}; use windows_sys::Win32::Foundation::{ HWND, LPARAM, OLE_E_WRONGCOMPOBJ, POINT, POINTS, RECT, RPC_E_CHANGED_MODE, S_OK, WPARAM, }; @@ -34,17 +34,17 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ use windows_sys::Win32::UI::Input::Touch::{RegisterTouchWindow, TWF_WANTPALM}; use windows_sys::Win32::UI::WindowsAndMessaging::{ CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos, - GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW, - GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, - SendMessageW, SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem, - SetWindowDisplayAffinity, SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu, - CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, - FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, - HTTOP, HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, - NID_READY, PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, - SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN, - TPM_RETURNCMD, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SETICON, WM_SYSCOMMAND, - WNDCLASSEXW, + GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowDisplayAffinity, + GetWindowPlacement, GetWindowTextLengthW, GetWindowTextW, IsWindowVisible, LoadCursorW, + PeekMessageW, PostMessageW, RegisterClassExW, SendMessageW, SetCursor, SetCursorPos, + SetForegroundWindow, SetMenuDefaultItem, SetWindowDisplayAffinity, SetWindowPlacement, + SetWindowPos, SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, + FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTBOTTOM, + HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, + MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY, PM_NOREMOVE, SC_CLOSE, + SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, SM_DIGITIZER, SWP_ASYNCWINDOWPOS, + SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN, TPM_RETURNCMD, WDA_EXCLUDEFROMCAPTURE, + WDA_NONE, WM_NCLBUTTONDOWN, WM_SETICON, WM_SYSCOMMAND, WNDCLASSEXW, }; use winit_core::cursor::Cursor; use winit_core::error::RequestError; @@ -426,6 +426,11 @@ impl CoreWindow for Window { } } + fn is_transparent(&self) -> bool { + let window_state = self.window_state.lock().unwrap(); + window_state.window_flags().contains(WindowFlags::TRANSPARENT) + } + fn set_transparent(&self, transparent: bool) { let window = self.window; let window_state = Arc::clone(&self.window_state); @@ -437,6 +442,10 @@ impl CoreWindow for Window { }); } + fn is_blurred(&self) -> bool { + false + } + fn set_blur(&self, _blur: bool) {} fn set_visible(&self, visible: bool) { @@ -556,6 +565,11 @@ impl CoreWindow for Window { PhysicalInsets::new(0, 0, 0, 0) } + fn min_surface_size(&self) -> Option> { + let size = self.window_state_lock().min_size; + size.map(|size| size.to_physical(self.scale_factor())) + } + fn set_min_surface_size(&self, size: Option) { self.window_state_lock().min_size = size; // Make windows re-check the window size bounds. @@ -563,6 +577,11 @@ impl CoreWindow for Window { let _ = self.request_surface_size(size.into()); } + fn max_surface_size(&self) -> Option> { + let size = self.window_state_lock().max_size; + size.map(|size| size.to_physical(self.scale_factor())) + } + fn set_max_surface_size(&self, size: Option) { self.window_state_lock().max_size = size; // Make windows re-check the window size bounds. @@ -626,6 +645,11 @@ impl CoreWindow for Window { buttons } + fn cursor(&self) -> Cursor { + warn!("getting the cursor is unimplemented on Windows"); + Cursor::default() + } + fn set_cursor(&self, cursor: Cursor) { match cursor { Cursor::Icon(icon) => { @@ -977,6 +1001,21 @@ impl CoreWindow for Window { window_state.window_flags.contains(WindowFlags::MARKER_DECORATIONS) } + fn window_level(&self) -> WindowLevel { + let flags = self.window_state_lock().window_flags(); + let top = flags.contains(WindowFlags::ALWAYS_ON_TOP); + let bottom = flags.contains(WindowFlags::ALWAYS_ON_BOTTOM); + match (top, bottom) { + (true, false) => WindowLevel::AlwaysOnTop, + (false, false) => WindowLevel::Normal, + (false, true) => WindowLevel::AlwaysOnBottom, + (true, true) => { + warn!("unclear top/bottom window levelling"); + WindowLevel::default() + }, + } + } + fn set_window_level(&self, level: WindowLevel) { let window = self.window; let window_state = Arc::clone(&self.window_state); @@ -1006,6 +1045,10 @@ impl CoreWindow for Window { Some(CoreMonitorHandle(Arc::new(monitor::primary_monitor()))) } + fn window_icon(&self) -> Option { + self.window_state_lock().window_icon.clone() + } + fn set_window_icon(&self, window_icon: Option) { if let Some(window_icon) = window_icon { self.set_icon(window_icon, IconType::Small); @@ -1131,6 +1174,17 @@ impl CoreWindow for Window { } } + fn content_protected(&self) -> bool { + let mut affinity = WDA_NONE; + let res = unsafe { GetWindowDisplayAffinity(self.hwnd(), &mut affinity) }; + if res == 0 { + let error = std::io::Error::last_os_error(); + error!(?error, "failed getting content protected state"); + return false; + } + affinity == WDA_EXCLUDEFROMCAPTURE + } + #[inline] fn set_content_protected(&self, protected: bool) { unsafe { diff --git a/winit-x11/src/window.rs b/winit-x11/src/window.rs index 7dacfcd66..7806008dc 100644 --- a/winit-x11/src/window.rs +++ b/winit-x11/src/window.rs @@ -115,10 +115,18 @@ impl CoreWindow for Window { self.0.safe_area() } + fn min_surface_size(&self) -> Option> { + self.0.min_surface_size() + } + fn set_min_surface_size(&self, min_size: Option) { self.0.set_min_surface_size(min_size) } + fn max_surface_size(&self) -> Option> { + self.0.max_surface_size() + } + fn set_max_surface_size(&self, max_size: Option) { self.0.set_max_surface_size(max_size) } @@ -135,10 +143,18 @@ impl CoreWindow for Window { self.0.set_title(title); } + fn is_transparent(&self) -> bool { + self.0.is_transparent() + } + fn set_transparent(&self, transparent: bool) { self.0.set_transparent(transparent); } + fn is_blurred(&self) -> bool { + self.0.is_blurred() + } + fn set_blur(&self, blur: bool) { self.0.set_blur(blur); } @@ -199,10 +215,19 @@ impl CoreWindow for Window { self.0.is_decorated() } + fn window_level(&self) -> WindowLevel { + self.0.window_level() + } + fn set_window_level(&self, level: WindowLevel) { self.0.set_window_level(level); } + fn window_icon(&self) -> Option { + warn!("getting the window icon is unimplemented on X11"); + None + } + fn set_window_icon(&self, window_icon: Option) { let icon = match window_icon.as_ref() { Some(icon) => icon.cast_ref::(), @@ -239,6 +264,10 @@ impl CoreWindow for Window { self.0.theme() } + fn content_protected(&self) -> bool { + self.0.content_protected() + } + fn set_content_protected(&self, protected: bool) { self.0.set_content_protected(protected); } @@ -247,6 +276,10 @@ impl CoreWindow for Window { self.0.title() } + fn cursor(&self) -> Cursor { + self.0.cursor() + } + fn set_cursor(&self, cursor: Cursor) { self.0.set_cursor(cursor); } @@ -1357,9 +1390,18 @@ impl UnownedWindow { self.xconn.flush_requests().expect("Failed to set window title"); } + fn is_transparent(&self) -> bool { + warn!("getting transparency state is unimplemented on X11"); + false + } + #[inline] pub fn set_transparent(&self, _transparent: bool) {} + fn is_blurred(&self) -> bool { + false + } + #[inline] pub fn set_blur(&self, _blur: bool) {} @@ -1404,6 +1446,11 @@ impl UnownedWindow { self.toggle_atom(_NET_WM_STATE_BELOW, level == WindowLevel::AlwaysOnBottom) } + fn window_level(&self) -> WindowLevel { + warn!("getting the window level is unimplemented on X11"); + WindowLevel::default() + } + #[inline] pub fn set_window_level(&self, level: WindowLevel) { self.set_window_level_inner(level) @@ -1665,6 +1712,11 @@ impl UnownedWindow { .expect("Failed to call `XSetWMNormalHints`"); } + fn min_surface_size(&self) -> Option> { + let size = self.shared_state_lock().min_surface_size; + size.map(|size| size.to_physical(self.scale_factor())) + } + #[inline] pub fn set_min_surface_size(&self, dimensions: Option) { self.shared_state_lock().min_surface_size = dimensions; @@ -1681,6 +1733,11 @@ impl UnownedWindow { .expect("Failed to call `XSetWMNormalHints`"); } + fn max_surface_size(&self) -> Option> { + let size = self.shared_state_lock().max_surface_size; + size.map(|size| size.to_physical(self.scale_factor())) + } + #[inline] pub fn set_max_surface_size(&self, dimensions: Option) { self.shared_state_lock().max_surface_size = dimensions; @@ -1799,6 +1856,12 @@ impl UnownedWindow { self.xwindow as ffi::Window } + #[inline] + pub fn cursor(&self) -> Cursor { + warn!("getting the cursor is unimplemented on X11"); + Cursor::default() + } + #[inline] pub fn set_cursor(&self, cursor: Cursor) { match cursor { @@ -2255,6 +2318,10 @@ impl UnownedWindow { None } + pub fn content_protected(&self) -> bool { + false + } + pub fn set_content_protected(&self, _protected: bool) {} #[inline] diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 89ca902ba..bfb05dca5 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -69,6 +69,14 @@ changelog entry. - Add `DeviceId::into_raw()` and `from_raw()`. - Added `Window::surface_position`, which is the position of the surface inside the window. - Added `Window::safe_area`, which describes the area of the surface that is unobstructed. +- Added `Window::min_surface_size`. +- Added `Window::max_surface_size`. +- Added `Window::is_transparent`. +- Added `Window::is_blurred`. +- Added `Window::window_level`. +- Added `Window::window_icon`. +- Added `Window::content_protected`. +- Added `Window::cursor`. - On X11, Wayland, Windows and macOS, improved scancode conversions for more obscure key codes. - Add ability to make non-activating window on macOS using `NSPanel` with `NSWindowStyleMask::NonactivatingPanel`. - Implement `MonitorHandleProvider` for `MonitorHandle` to access common monitor API.