diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f76ff72..32726d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On MacOS and Windows, add `Window::set_content_protected`. - On MacOS, add `EventLoopBuilderExtMacOS::with_activate_ignoring_other_apps`. - On Windows, fix icons specified on `WindowBuilder` not taking effect for windows created after the first one. - On Windows and macOS, add `Window::title` to query the current window title. diff --git a/src/platform_impl/macos/appkit/mod.rs b/src/platform_impl/macos/appkit/mod.rs index 8c9dc171..57273ea5 100644 --- a/src/platform_impl/macos/appkit/mod.rs +++ b/src/platform_impl/macos/appkit/mod.rs @@ -54,7 +54,7 @@ pub(crate) use self::version::NSAppKitVersion; pub(crate) use self::view::{NSTrackingRectTag, NSView}; pub(crate) use self::window::{ NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState, - NSWindowStyleMask, NSWindowTitleVisibility, + NSWindowSharingType, NSWindowStyleMask, NSWindowTitleVisibility, }; #[link(name = "AppKit", kind = "framework")] diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs index 8c044dbe..e717695b 100644 --- a/src/platform_impl/macos/appkit/window.rs +++ b/src/platform_impl/macos/appkit/window.rs @@ -84,6 +84,9 @@ extern_methods!( #[sel(setMovable:)] pub fn setMovable(&self, movable: bool); + #[sel(setSharingType:)] + pub fn setSharingType(&self, sharingType: NSWindowSharingType); + #[sel(setOpaque:)] pub fn setOpaque(&self, opaque: bool); @@ -349,3 +352,16 @@ pub enum NSBackingStoreType { unsafe impl Encode for NSBackingStoreType { const ENCODING: Encoding = NSUInteger::ENCODING; } + +#[allow(dead_code)] +#[repr(usize)] // NSUInteger +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum NSWindowSharingType { + NSWindowSharingNone = 0, + NSWindowSharingReadOnly = 1, + NSWindowSharingReadWrite = 2, +} + +unsafe impl Encode for NSWindowSharingType { + const ENCODING: Encoding = NSUInteger::ENCODING; +} diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 75ddb313..c06eb438 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -45,7 +45,8 @@ use objc2::{declare_class, msg_send, msg_send_id, sel, ClassType}; use super::appkit::{ NSApp, NSAppKitVersion, NSAppearance, NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSCursor, NSFilenamesPboardType, NSRequestUserAttentionType, NSResponder, NSScreen, - NSWindow, NSWindowButton, NSWindowLevel, NSWindowStyleMask, NSWindowTitleVisibility, + NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask, + NSWindowTitleVisibility, }; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -305,6 +306,10 @@ impl WinitWindow { this.setTitle(&NSString::from_str(&attrs.title)); this.setAcceptsMouseMovedEvents(true); + if attrs.content_protected { + this.setSharingType(NSWindowSharingType::NSWindowSharingNone); + } + if pl_attrs.titlebar_transparent { this.setTitlebarAppearsTransparent(true); } @@ -1123,6 +1128,14 @@ impl WinitWindow { } #[inline] + pub fn set_content_protected(&self, protected: bool) { + self.setSharingType(if protected { + NSWindowSharingType::NSWindowSharingNone + } else { + NSWindowSharingType::NSWindowSharingReadOnly + }) + } + pub fn title(&self) -> String { self.title_().to_string() } diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 424a885f..88dd1a38 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -42,11 +42,12 @@ use windows_sys::Win32::{ CreateWindowExW, FlashWindowEx, GetClientRect, GetCursorPos, GetForegroundWindow, GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW, GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW, SetCursor, - SetCursorPos, SetForegroundWindow, SetWindowPlacement, SetWindowPos, SetWindowTextW, - CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, - FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTCAPTION, MAPVK_VK_TO_VSC, NID_READY, - PM_NOREMOVE, SM_DIGITIZER, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, - SWP_NOZORDER, WM_NCLBUTTONDOWN, WNDCLASSEXW, + SetCursorPos, SetForegroundWindow, SetWindowDisplayAffinity, SetWindowPlacement, + SetWindowPos, SetWindowTextW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, FLASHWINFO, + FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY, GWLP_HINSTANCE, HTCAPTION, + MAPVK_VK_TO_VSC, NID_READY, PM_NOREMOVE, SM_DIGITIZER, SWP_ASYNCWINDOWPOS, + SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, WDA_EXCLUDEFROMCAPTURE, WDA_NONE, + WM_NCLBUTTONDOWN, WNDCLASSEXW, }, }, }; @@ -738,6 +739,20 @@ impl Window { unsafe { force_window_active(window.0) }; } } + + #[inline] + pub fn set_content_protected(&self, protected: bool) { + unsafe { + SetWindowDisplayAffinity( + self.hwnd(), + if protected { + WDA_EXCLUDEFROMCAPTURE + } else { + WDA_NONE + }, + ) + }; + } } impl Drop for Window { @@ -908,6 +923,10 @@ impl<'a, T: 'static> InitData<'a, T> { let attributes = self.attributes.clone(); + if attributes.content_protected { + win.set_content_protected(true); + } + // Set visible before setting the size to ensure the // attribute is correctly applied. win.set_visible(attributes.visible); diff --git a/src/window.rs b/src/window.rs index c11be5da..d5f505c8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -136,6 +136,7 @@ pub(crate) struct WindowAttributes { pub window_icon: Option, pub preferred_theme: Option, pub resize_increments: Option, + pub content_protected: bool, } impl Default for WindowAttributes { @@ -157,6 +158,7 @@ impl Default for WindowAttributes { window_icon: None, preferred_theme: None, resize_increments: None, + content_protected: false, } } } @@ -365,6 +367,23 @@ impl WindowBuilder { self } + /// Prevents the window contents from being captured by other apps. + /// + /// The default is `false`. + /// + /// ## 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 / Web / x11:** Ignored. + /// + /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone + #[inline] + pub fn with_content_protected(mut self, protected: bool) -> Self { + self.window.content_protected = protected; + self + } + /// Builds the window. /// /// Possible causes of error include denied permission, incompatible system, and lack of memory. @@ -953,6 +972,20 @@ impl Window { self.window.theme() } + /// 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:** Unsupported. + /// + /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone + pub fn set_content_protected(&self, _protected: bool) { + #[cfg(any(target_os = "macos", target_os = "windows"))] + self.window.set_content_protected(_protected); + } + /// Gets the current title of the window. /// /// ## Platform-specific