Skip to content

Commit

Permalink
Migrate from objc/cocoa to objc2 (#150)
Browse files Browse the repository at this point in the history
* Migrate from objc/cocoa to objc2

* fmt

---------

Co-authored-by: amrbashir <[email protected]>
  • Loading branch information
madsmtm and amrbashir authored Sep 8, 2024
1 parent 4e1add2 commit 3325c24
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 156 deletions.
5 changes: 5 additions & 0 deletions .changes/use-objc2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"window-vibrancy": patch
---

Use `objc2` internally, leading to better memory safety.
31 changes: 19 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
[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"
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"
Expand All @@ -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"] }
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
)),
Expand Down
181 changes: 40 additions & 141 deletions src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NSVisualEffectState>,
pub unsafe fn apply_vibrancy(
ns_view: NonNull<c_void>,
appearance: super::NSVisualEffectMaterial,
state: Option<super::NSVisualEffectState>,
radius: Option<f64>,
) -> 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] }
}
}

0 comments on commit 3325c24

Please sign in to comment.