diff --git a/winit-appkit/Cargo.toml b/winit-appkit/Cargo.toml index 1094510621..5231cada6a 100644 --- a/winit-appkit/Cargo.toml +++ b/winit-appkit/Cargo.toml @@ -54,6 +54,7 @@ objc2-app-kit = { workspace = true, features = [ "NSTrackingArea", "NSToolbar", "NSView", + "NSVisualEffectView", "NSWindow", "NSWindowScripting", "NSWindowTabGroup", diff --git a/winit-appkit/src/ffi.rs b/winit-appkit/src/ffi.rs index 7f7cec0bfb..1fd11c5425 100644 --- a/winit-appkit/src/ffi.rs +++ b/winit-appkit/src/ffi.rs @@ -4,8 +4,6 @@ use std::ffi::c_void; -use objc2::ffi::NSInteger; -use objc2::runtime::AnyObject; use objc2_core_foundation::{CFString, CFUUID, cf_type}; use objc2_core_graphics::CGDirectDisplayID; @@ -28,17 +26,6 @@ unsafe extern "C" { pub fn CGDisplayGetDisplayIDFromUUID(uuid: &CFUUID) -> CGDirectDisplayID; } -#[link(name = "CoreGraphics", kind = "framework")] -unsafe extern "C" { - // Wildly used private APIs; Apple uses them for their Terminal.app. - pub fn CGSMainConnectionID() -> *mut AnyObject; - pub fn CGSSetWindowBackgroundBlurRadius( - connection_id: *mut AnyObject, - window_id: NSInteger, - radius: i64, - ) -> i32; -} - #[repr(transparent)] pub struct TISInputSource(std::ffi::c_void); diff --git a/winit-appkit/src/view.rs b/winit-appkit/src/view.rs index 69b87a49d7..6ec4d449c6 100644 --- a/winit-appkit/src/view.rs +++ b/winit-appkit/src/view.rs @@ -141,6 +141,7 @@ define_class!( #[unsafe(super(NSView, NSResponder, NSObject))] #[ivars = ViewState] #[name = "WinitView"] + #[derive(Debug)] pub(super) struct WinitView; /// This documentation attribute makes rustfmt work for some reason? diff --git a/winit-appkit/src/window_delegate.rs b/winit-appkit/src/window_delegate.rs index 4697cf820f..54716d810d 100644 --- a/winit-appkit/src/window_delegate.rs +++ b/winit-appkit/src/window_delegate.rs @@ -18,12 +18,13 @@ use objc2::{ }; use objc2_app_kit::{ NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSAppearanceCustomization, - NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, NSBackingStoreType, - NSColor, NSDraggingDestination, NSDraggingInfo, NSRequestUserAttentionType, NSScreen, - NSToolbar, NSView, NSViewFrameDidChangeNotification, NSWindow, NSWindowButton, - NSWindowDelegate, NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode, - NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, - NSWindowToolbarStyle, + NSAppearanceNameAqua, NSApplication, NSApplicationPresentationOptions, + NSAutoresizingMaskOptions, NSBackingStoreType, NSColor, NSDraggingDestination, NSDraggingInfo, + NSRequestUserAttentionType, NSScreen, NSToolbar, NSView, NSViewFrameDidChangeNotification, + NSVisualEffectBlendingMode, NSVisualEffectMaterial, NSVisualEffectState, NSVisualEffectView, + NSWindow, NSWindowButton, NSWindowDelegate, NSWindowLevel, NSWindowOcclusionState, + NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, + NSWindowTitleVisibility, NSWindowToolbarStyle, }; #[allow(deprecated)] use objc2_app_kit::{NSFilenamesPboardType, NSWindowFullScreenButton}; @@ -55,7 +56,6 @@ use winit_core::window::{ use super::app_state::AppState; use super::cursor::{CustomCursor, cursor_from_icon}; -use super::ffi; use super::monitor::{self, MonitorHandle, flip_window_screen_coordinates, get_display_id}; use super::util::cgerr; use super::view::WinitView; @@ -69,6 +69,8 @@ pub(crate) struct State { window: Retained, + view: Retained, + // During `windowDidResize`, we use this to only send Moved if the position changed. // // This is expressed in desktop coordinates, and flipped to match Winit's coordinate system. @@ -779,6 +781,12 @@ impl WindowDelegate { let window = new_window(app_state, &attrs, &macos_attrs, mtm) .ok_or_else(|| os_error!("couldn't create `NSWindow`"))?; + let view: Retained = window + .contentView() + .expect("window should have a content view") + .downcast() + .expect("content view should be a `WinitView`"); + match attrs.parent_window() { Some(rwh_06::RawWindowHandle::AppKit(handle)) => { // SAFETY: Caller ensures the pointer is valid or NULL @@ -817,6 +825,7 @@ impl WindowDelegate { let delegate = mtm.alloc().set_ivars(State { app_state: Rc::clone(app_state), window: window.retain(), + view, previous_position: Cell::new(flip_window_screen_coordinates(window.frame())), previous_scale_factor: Cell::new(scale_factor), surface_resize_increments: Cell::new(surface_resize_increments), @@ -888,8 +897,7 @@ impl WindowDelegate { #[track_caller] pub(super) fn view(&self) -> Retained { - // The view inside WinitWindow should always be set and be `WinitView`. - self.window().contentView().unwrap().downcast().unwrap() + self.ivars().view.clone() } #[track_caller] @@ -970,16 +978,31 @@ impl WindowDelegate { } 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. - let radius = if blur { 80 } else { 0 }; - let window_number = self.window().windowNumber(); - unsafe { - ffi::CGSSetWindowBackgroundBlurRadius( - ffi::CGSMainConnectionID(), - window_number, - radius, + let window = self.window(); + let view = self.view(); + let currently_blurred = window + .contentView() + .is_some_and(|content| content.downcast::().is_ok()); + if blur == currently_blurred { + return; + } + + if blur { + let mtm = MainThreadMarker::from(self); + let effect_view = NSVisualEffectView::new(mtm); + effect_view.setBlendingMode(NSVisualEffectBlendingMode::BehindWindow); + effect_view.setState(NSVisualEffectState::Active); + effect_view.setMaterial(NSVisualEffectMaterial::WindowBackground); + + window.setContentView(Some(&effect_view)); + view.setFrame(effect_view.bounds()); + view.setAutoresizingMask( + NSAutoresizingMaskOptions::ViewWidthSizable + | NSAutoresizingMaskOptions::ViewHeightSizable, ); + effect_view.addSubview(&view); + } else { + window.setContentView(Some(&view)); } } diff --git a/winit/src/changelog/unreleased.md b/winit/src/changelog/unreleased.md index 1c58b725c0..d3809ab453 100644 --- a/winit/src/changelog/unreleased.md +++ b/winit/src/changelog/unreleased.md @@ -67,3 +67,5 @@ changelog entry. - On macOS, fix borderless game presentation options not sticking after switching spaces. - On macOS, fix IME being locked on (regardless of requests to disable) after being enabled once. - On macOS, fix a panic and incorrect cursor position in Ime::Preedit when the preedit string contains special characters (ie. emojis) caused by incorrect UTF-16 to UTF-8 offset conversion. +- On macOS fix private `CGSSetWindowBackgroundBlurRadius` call causing App Store rejection, `NSVisualEffectView` replacement. +