diff --git a/.changes/use-objc2.md b/.changes/use-objc2.md new file mode 100644 index 0000000..85dff02 --- /dev/null +++ b/.changes/use-objc2.md @@ -0,0 +1,5 @@ +--- +"window-vibrancy": patch +--- + +Use `objc2` internally, leading to better memory safety. diff --git a/Cargo.toml b/Cargo.toml index 33d11e8..632d4b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "window-vibrancy" description = "Make your windows vibrant." -authors = [ "Tauri Programme within The Commons Conservancy" ] +authors = ["Tauri Programme within The Commons Conservancy"] version = "0.5.1" edition = "2021" rust-version = "1.56" @@ -9,12 +9,12 @@ license = "Apache-2.0 OR MIT" readme = "README.md" repository = "https://github.com/tauri-apps/tauri-plugin-vibrancy" documentation = "https://docs.rs/tauri-plugin-vibrancy" -keywords = [ "vibrancy", "acrylic", "mica", "blur", "windowing" ] -categories = [ "gui" ] +keywords = ["vibrancy", "acrylic", "mica", "blur", "windowing"] +categories = ["gui"] [package.metadata.docs.rs] default-target = "x86_64-pc-windows-msvc" -targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ] +targets = ["x86_64-apple-darwin", "x86_64-pc-windows-msvc"] [dependencies] raw-window-handle = "0.6" @@ -23,20 +23,27 @@ raw-window-handle = "0.6" tao = "0.30" winit = "0.29" -[target."cfg(target_os = \"windows\")".dependencies] +[target.'cfg(target_os = "windows")'.dependencies] windows-version = "0.1" - [target."cfg(target_os = \"windows\")".dependencies.windows-sys] - version = "0.59.0" - features = [ +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = "0.59.0" +features = [ "Win32_Foundation", "Win32_System_LibraryLoader", "Win32_System_SystemInformation", "Win32_Graphics_Gdi", "Win32_Graphics_Dwm", - "Win32_UI_WindowsAndMessaging" + "Win32_UI_WindowsAndMessaging", ] -[target."cfg(target_os = \"macos\")".dependencies] -cocoa = "0.26" -objc = "0.2" +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.5.2" +objc2-app-kit = { version = "0.2.2", features = [ + "NSApplication", + "NSGraphics", + "NSResponder", + "NSView", + "NSVisualEffectView", +] } +objc2-foundation = { version = "0.2.2", features = ["NSThread", "NSGeometry"] } diff --git a/src/lib.rs b/src/lib.rs index d2bc3c6..fde3cbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,9 +220,9 @@ pub fn apply_vibrancy( ) -> Result<(), Error> { match window.window_handle()?.as_raw() { #[cfg(target_os = "macos")] - raw_window_handle::RawWindowHandle::AppKit(handle) => { - macos::apply_vibrancy(handle.ns_view.as_ptr() as _, effect, state, radius) - } + raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe { + macos::apply_vibrancy(handle.ns_view, effect, state, radius) + }, _ => Err(Error::UnsupportedPlatform( "\"apply_vibrancy()\" is only supported on macOS.", )), diff --git a/src/macos.rs b/src/macos.rs index 733a932..a59c3a9 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -74,172 +74,71 @@ pub use internal::apply_vibrancy; #[cfg(target_os = "macos")] mod internal { - use super::{NSVisualEffectMaterial, NSVisualEffectState}; - - use cocoa::{ - appkit::{ - NSAppKitVersionNumber, NSAppKitVersionNumber10_10, NSAppKitVersionNumber10_11, - NSAutoresizingMaskOptions, NSView, NSViewHeightSizable, NSViewWidthSizable, - NSWindowOrderingMode, - }, - base::{id, nil, BOOL}, - foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize}, + use std::{ffi::c_void, ptr::NonNull}; + + use objc2_app_kit::{ + NSAppKitVersionNumber, NSAppKitVersionNumber10_10, NSAppKitVersionNumber10_11, + NSAppKitVersionNumber10_14, NSAutoresizingMaskOptions, NSView, NSVisualEffectBlendingMode, + NSVisualEffectMaterial, NSVisualEffectState, NSVisualEffectView, NSWindowOrderingMode, }; - use objc::{class, msg_send, sel, sel_impl}; + use objc2_foundation::{CGFloat, MainThreadMarker}; use crate::Error; #[allow(deprecated)] - pub fn apply_vibrancy( - ns_view: id, - appearance: NSVisualEffectMaterial, - state: Option, + pub unsafe fn apply_vibrancy( + ns_view: NonNull, + appearance: super::NSVisualEffectMaterial, + state: Option, radius: Option, ) -> Result<(), Error> { + let mtm = MainThreadMarker::new().ok_or(Error::NotMainThread( + "\"apply_vibrancy()\" can only be used on the main thread.", + ))?; + unsafe { + let view: &NSView = ns_view.cast().as_ref(); + if NSAppKitVersionNumber < NSAppKitVersionNumber10_10 { - eprintln!("\"NSVisualEffectView\" is only available on macOS 10.10 or newer"); return Err(Error::UnsupportedPlatformVersion( "\"apply_vibrancy()\" is only available on macOS 10.0 or newer.", )); } - if !msg_send![class!(NSThread), isMainThread] { - return Err(Error::NotMainThread( - "\"apply_vibrancy()\" can only be used on the main thread.", - )); - } - - let mut m = appearance; + let mut m = NSVisualEffectMaterial(appearance as isize); if (appearance as u32 > 9 && NSAppKitVersionNumber < NSAppKitVersionNumber10_14) || (appearance as u32 > 4 && NSAppKitVersionNumber < NSAppKitVersionNumber10_11) { m = NSVisualEffectMaterial::AppearanceBased; } - let bounds = NSView::bounds(ns_view); - let blurred_view = - NSVisualEffectView::initWithFrame_(NSVisualEffectView::alloc(nil), bounds); - blurred_view.autorelease(); - - blurred_view.setMaterial_(m); - blurred_view.setCornerRadius_(radius.unwrap_or(0.0)); - blurred_view.setBlendingMode_(NSVisualEffectBlendingMode::BehindWindow); - blurred_view.setState_(state.unwrap_or(NSVisualEffectState::FollowsWindowActiveState)); - NSVisualEffectView::setAutoresizingMask_( - blurred_view, - NSViewWidthSizable | NSViewHeightSizable, + let bounds = view.bounds(); + let blurred_view = NSVisualEffectView::initWithFrame(mtm.alloc(), bounds); + + blurred_view.setMaterial(m); + set_corner_radius(&blurred_view, radius.unwrap_or(0.0)); + blurred_view.setBlendingMode(NSVisualEffectBlendingMode::BehindWindow); + blurred_view.setState( + state + .map(|state| NSVisualEffectState(state as isize)) + .unwrap_or(NSVisualEffectState::FollowsWindowActiveState), + ); + blurred_view.setAutoresizingMask( + NSAutoresizingMaskOptions::NSViewWidthSizable + | NSAutoresizingMaskOptions::NSViewHeightSizable, ); - let _: () = msg_send![ns_view, addSubview: blurred_view positioned: NSWindowOrderingMode::NSWindowBelow relativeTo: 0]; + view.addSubview_positioned_relativeTo( + &blurred_view, + NSWindowOrderingMode::NSWindowBelow, + None, + ); } Ok(()) } - #[allow(non_upper_case_globals)] - const NSAppKitVersionNumber10_14: f64 = 1671.0; - - // https://developer.apple.com/documentation/appkit/nsvisualeffectview/blendingmode - #[allow(dead_code)] - #[repr(u64)] - #[derive(Clone, Copy, Debug, PartialEq)] - enum NSVisualEffectBlendingMode { - BehindWindow = 0, - WithinWindow = 1, - } - - // macos 10.10+ - // https://developer.apple.com/documentation/appkit/nsvisualeffectview - #[allow(non_snake_case, dead_code)] - trait NSVisualEffectView: Sized { - unsafe fn alloc(_: Self) -> id { - msg_send![class!(NSVisualEffectView), alloc] - } - - unsafe fn init(self) -> id; - unsafe fn initWithFrame_(self, frameRect: NSRect) -> id; - unsafe fn bounds(self) -> NSRect; - unsafe fn frame(self) -> NSRect; - unsafe fn setFrameSize(self, frameSize: NSSize); - unsafe fn setFrameOrigin(self, frameOrigin: NSPoint); - - unsafe fn superview(self) -> id; - unsafe fn removeFromSuperview(self); - unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions); - - // API_AVAILABLE(macos(10.12)); - unsafe fn isEmphasized(self) -> BOOL; - // API_AVAILABLE(macos(10.12)); - unsafe fn setEmphasized_(self, emphasized: BOOL); - - unsafe fn setMaterial_(self, material: NSVisualEffectMaterial); - unsafe fn setCornerRadius_(self, radius: f64); - unsafe fn setState_(self, state: NSVisualEffectState); - unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode); - } - - #[allow(non_snake_case)] - impl NSVisualEffectView for id { - unsafe fn init(self) -> id { - msg_send![self, init] - } - - unsafe fn initWithFrame_(self, frameRect: NSRect) -> id { - msg_send![self, initWithFrame: frameRect] - } - - unsafe fn bounds(self) -> NSRect { - msg_send![self, bounds] - } - - unsafe fn frame(self) -> NSRect { - msg_send![self, frame] - } - - unsafe fn setFrameSize(self, frameSize: NSSize) { - msg_send![self, setFrameSize: frameSize] - } - - unsafe fn setFrameOrigin(self, frameOrigin: NSPoint) { - msg_send![self, setFrameOrigin: frameOrigin] - } - - unsafe fn superview(self) -> id { - msg_send![self, superview] - } - - unsafe fn removeFromSuperview(self) { - msg_send![self, removeFromSuperview] - } - - unsafe fn setAutoresizingMask_(self, autoresizingMask: NSAutoresizingMaskOptions) { - msg_send![self, setAutoresizingMask: autoresizingMask] - } - - // API_AVAILABLE(macos(10.12)); - unsafe fn isEmphasized(self) -> BOOL { - msg_send![self, isEmphasized] - } - - // API_AVAILABLE(macos(10.12)); - unsafe fn setEmphasized_(self, emphasized: BOOL) { - msg_send![self, setEmphasized: emphasized] - } - - unsafe fn setMaterial_(self, material: NSVisualEffectMaterial) { - msg_send![self, setMaterial: material] - } - - unsafe fn setCornerRadius_(self, radius: f64) { - msg_send![self, setCornerRadius: radius] - } - - unsafe fn setState_(self, state: NSVisualEffectState) { - msg_send![self, setState: state] - } - - unsafe fn setBlendingMode_(self, mode: NSVisualEffectBlendingMode) { - msg_send![self, setBlendingMode: mode] - } + // TODO: Does not seem to be public? + fn set_corner_radius(view: &NSVisualEffectView, radius: CGFloat) { + unsafe { objc2::msg_send![view, setCornerRadius: radius] } } }