diff --git a/.changes/objc2.md b/.changes/objc2.md new file mode 100644 index 000000000..4b6aead5a --- /dev/null +++ b/.changes/objc2.md @@ -0,0 +1,5 @@ +--- +tao: patch +--- + +Use `objc2`. diff --git a/Cargo.toml b/Cargo.toml index 75728cac6..940b6ac2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,10 +104,41 @@ ndk-context = "0.1" tao-macros = { version = "0.1.0", path = "./tao-macros" } [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] -objc = "0.2" +objc2 = "0.5" [target."cfg(target_os = \"macos\")".dependencies] -cocoa = "0.26" +objc2-foundation = { version = "0.2", default-features = false, features = [ + "std", + "NSArray", + "NSAttributedString", + "NSAutoreleasePool", + "NSDate", + "NSDictionary", + "NSEnumerator", + "NSGeometry", + "NSObjCRuntime", + "NSRange", + "NSString", + "NSThread", + "NSURL", +] } +objc2-app-kit = { version = "0.2", default-features = false, features = [ + "std", + "NSApplication", + "NSButton", + "NSColor", + "NSControl", + "NSEvent", + "NSGraphics", + "NSImage", + "NSOpenGLView", + "NSPasteboard", + "NSResponder", + "NSRunningApplication", + "NSScreen", + "NSView", + "NSWindow", +] } core-foundation = "0.10" core-graphics = "0.24" dispatch = "0.2" diff --git a/src/event.rs b/src/event.rs index d6efde565..2765e4e8e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -37,8 +37,7 @@ //! describes what happens in what order. //! //! [event_loop_run]: crate::event_loop::EventLoop::run -use std::path::PathBuf; -use std::time::Instant; +use std::{path::PathBuf, time::Instant}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, diff --git a/src/lib.rs b/src/lib.rs index f5f2feda4..072dbb650 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,8 +167,8 @@ extern crate serde; #[macro_use] extern crate bitflags; #[cfg(any(target_os = "macos", target_os = "ios"))] -#[macro_use] -extern crate objc; +#[macro_use(class, msg_send, sel)] +extern crate objc2; pub use dpi; diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 4ffec9f94..1a252ae7c 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -363,7 +363,10 @@ impl MonitorHandleExtMacOS for MonitorHandle { } fn ns_screen(&self) -> Option<*mut c_void> { - self.inner.ns_screen().map(|s| s as *mut c_void) + self + .inner + .ns_screen() + .map(|s| objc2::rc::Retained::into_raw(s) as *mut c_void) } } @@ -388,36 +391,35 @@ pub trait EventLoopWindowTargetExtMacOS { impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget { fn hide_application(&self) { - let cls = objc::runtime::Class::get("NSApplication").unwrap(); - let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; - unsafe { msg_send![app, hide: 0] } + // TODO: Safety. + let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; + objc2_app_kit::NSApplication::sharedApplication(mtm).hide(None) } fn show_application(&self) { - let cls = objc::runtime::Class::get("NSApplication").unwrap(); - let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; - unsafe { msg_send![app, unhide: 0] } + // TODO: Safety. + let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; + unsafe { objc2_app_kit::NSApplication::sharedApplication(mtm).unhide(None) } } fn hide_other_applications(&self) { - let cls = objc::runtime::Class::get("NSApplication").unwrap(); - let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; - unsafe { msg_send![app, hideOtherApplications: 0] } + // TODO: Safety. + let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; + objc2_app_kit::NSApplication::sharedApplication(mtm).hideOtherApplications(None) } fn set_activation_policy_at_runtime(&self, activation_policy: ActivationPolicy) { - use cocoa::appkit; - - let cls = objc::runtime::Class::get("NSApplication").unwrap(); - let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; + use objc2_app_kit::NSApplicationActivationPolicy; let ns_activation_policy = match activation_policy { - ActivationPolicy::Regular => appkit::NSApplicationActivationPolicyRegular, - ActivationPolicy::Accessory => appkit::NSApplicationActivationPolicyAccessory, - ActivationPolicy::Prohibited => appkit::NSApplicationActivationPolicyProhibited, + ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular, + ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory, + ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited, }; - unsafe { msg_send![app, setActivationPolicy: ns_activation_policy] } + // TODO: Safety. + let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() }; + objc2_app_kit::NSApplication::sharedApplication(mtm).setActivationPolicy(ns_activation_policy); } fn set_badge_label(&self, label: Option) { diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs index 21b85fec9..d44f82d05 100644 --- a/src/platform_impl/ios/app_state.rs +++ b/src/platform_impl/ios/app_state.rs @@ -13,7 +13,7 @@ use std::{ time::Instant, }; -use objc::runtime::{BOOL, YES}; +use objc2::runtime::AnyObject; use crate::{ dpi::LogicalSize, @@ -475,10 +475,7 @@ impl AppState { // retains window pub unsafe fn set_key_window(window: id) { bug_assert!( - { - let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)]; - is_window == YES - }, + msg_send![window, isKindOfClass: class!(UIWindow)], "set_key_window called with an incorrect type" ); let mut this = AppState::get_mut(); @@ -505,10 +502,7 @@ pub unsafe fn set_key_window(window: id) { // retains window pub unsafe fn queue_gl_or_metal_redraw(window: id) { bug_assert!( - { - let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)]; - is_window == YES - }, + msg_send![window, isKindOfClass: class!(UIWindow)], "set_key_window called with an incorrect type" ); let mut this = AppState::get_mut(); @@ -582,7 +576,7 @@ pub unsafe fn did_finish_launching() { let () = msg_send![window, setScreen: screen]; let () = msg_send![screen, release]; let controller: id = msg_send![window, rootViewController]; - let () = msg_send![window, setRootViewController:ptr::null::<()>()]; + let () = msg_send![window, setRootViewController: ptr::null::()]; let () = msg_send![window, setRootViewController: controller]; let () = msg_send![window, makeKeyAndVisible]; } @@ -1019,7 +1013,7 @@ pub fn os_capabilities() -> OSCapabilities { static ref OS_CAPABILITIES: OSCapabilities = { let version: NSOperatingSystemVersion = unsafe { let process_info: id = msg_send![class!(NSProcessInfo), processInfo]; - let atleast_ios_8: BOOL = msg_send![ + let atleast_ios_8: bool = msg_send![ process_info, respondsToSelector: sel!(operatingSystemVersion) ]; @@ -1031,7 +1025,7 @@ pub fn os_capabilities() -> OSCapabilities { // // The minimum required iOS version is likely to grow in the future. assert!( - atleast_ios_8 == YES, + atleast_ios_8, "`tao` requires iOS version 8 or greater" ); msg_send![process_info, operatingSystemVersion] diff --git a/src/platform_impl/ios/badge.rs b/src/platform_impl/ios/badge.rs index a2f04bdb3..39873d500 100644 --- a/src/platform_impl/ios/badge.rs +++ b/src/platform_impl/ios/badge.rs @@ -1,10 +1,12 @@ -use objc::runtime::{Class, Object}; -use objc::{msg_send, sel, sel_impl}; +use objc2::{ + msg_send, + runtime::{AnyClass, AnyObject}, +}; pub fn set_badge_count(count: i32) { unsafe { - let ui_application = Class::get("UIApplication").expect("Failed to get UIApplication class"); - let app: *mut Object = msg_send![ui_application, sharedApplication]; + let ui_application = AnyClass::get("UIApplication").expect("Failed to get UIApplication class"); + let app: *mut AnyObject = msg_send![ui_application, sharedApplication]; let _: () = msg_send![app, setApplicationIconBadgeNumber:count]; } } diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs index ffc1b1bb8..c745379dc 100644 --- a/src/platform_impl/ios/event_loop.rs +++ b/src/platform_impl/ios/event_loop.rs @@ -143,7 +143,7 @@ impl EventLoop { F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget, &mut ControlFlow), { unsafe { - let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication]; + let application: id = msg_send![class!(UIApplication), sharedApplication]; assert_eq!( application, ptr::null_mut(), diff --git a/src/platform_impl/ios/ffi.rs b/src/platform_impl/ios/ffi.rs index e57fd9023..22a113c46 100644 --- a/src/platform_impl/ios/ffi.rs +++ b/src/platform_impl/ios/ffi.rs @@ -6,16 +6,26 @@ use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*}; -use objc::{runtime::Object, Encode, Encoding}; +use objc2::{ + encode::{Encode, Encoding}, + runtime::{AnyObject, Bool}, +}; use crate::{ dpi::LogicalSize, platform::ios::{Idiom, ScreenEdge, ValidOrientations}, }; -pub type id = *mut Object; +pub type id = *mut AnyObject; pub const nil: id = 0 as id; +#[allow(non_camel_case_types)] +pub type BOOL = Bool; +#[allow(deprecated)] +pub const YES: Bool = Bool::YES; +#[allow(deprecated)] +pub const NO: Bool = Bool::NO; + #[cfg(target_pointer_width = "32")] pub type CGFloat = f32; #[cfg(target_pointer_width = "64")] @@ -32,6 +42,17 @@ pub struct NSOperatingSystemVersion { pub patch: NSInteger, } +unsafe impl Encode for NSOperatingSystemVersion { + const ENCODING: Encoding = Encoding::Struct( + "?", + &[ + NSInteger::ENCODING, + NSInteger::ENCODING, + NSInteger::ENCODING, + ], + ); +} + #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq)] pub struct CGPoint { @@ -39,6 +60,10 @@ pub struct CGPoint { pub y: CGFloat, } +unsafe impl Encode for CGPoint { + const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]); +} + #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq)] pub struct CGSize { @@ -55,6 +80,10 @@ impl CGSize { } } +unsafe impl Encode for CGSize { + const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]); +} + #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq)] pub struct CGRect { @@ -69,18 +98,9 @@ impl CGRect { } unsafe impl Encode for CGRect { - fn encode() -> Encoding { - unsafe { - if cfg!(target_pointer_width = "32") { - Encoding::from_str("{CGRect={CGPoint=ff}{CGSize=ff}}") - } else if cfg!(target_pointer_width = "64") { - Encoding::from_str("{CGRect={CGPoint=dd}{CGSize=dd}}") - } else { - unimplemented!() - } - } - } + const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]); } + #[derive(Debug)] #[allow(dead_code)] #[repr(isize)] @@ -92,6 +112,10 @@ pub enum UITouchPhase { Cancelled, } +unsafe impl Encode for UITouchPhase { + const ENCODING: Encoding = isize::ENCODING; +} + #[derive(Debug, PartialEq)] #[allow(dead_code)] #[repr(isize)] @@ -101,6 +125,10 @@ pub enum UIForceTouchCapability { Available, } +unsafe impl Encode for UIForceTouchCapability { + const ENCODING: Encoding = isize::ENCODING; +} + #[derive(Debug, PartialEq)] #[allow(dead_code)] #[repr(isize)] @@ -110,6 +138,10 @@ pub enum UITouchType { Pencil, } +unsafe impl Encode for UITouchType { + const ENCODING: Encoding = isize::ENCODING; +} + #[repr(C)] #[derive(Debug, Clone)] pub struct UIEdgeInsets { @@ -119,14 +151,24 @@ pub struct UIEdgeInsets { pub right: CGFloat, } +unsafe impl Encode for UIEdgeInsets { + const ENCODING: Encoding = Encoding::Struct( + "UIEdgeInsets", + &[ + CGFloat::ENCODING, + CGFloat::ENCODING, + CGFloat::ENCODING, + CGFloat::ENCODING, + ], + ); +} + #[repr(transparent)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UIUserInterfaceIdiom(NSInteger); unsafe impl Encode for UIUserInterfaceIdiom { - fn encode() -> Encoding { - NSInteger::encode() - } + const ENCODING: Encoding = NSInteger::ENCODING; } impl UIUserInterfaceIdiom { @@ -167,9 +209,7 @@ impl Into for UIUserInterfaceIdiom { pub struct UIInterfaceOrientationMask(NSUInteger); unsafe impl Encode for UIInterfaceOrientationMask { - fn encode() -> Encoding { - NSUInteger::encode() - } + const ENCODING: Encoding = NSUInteger::ENCODING; } impl UIInterfaceOrientationMask { @@ -217,9 +257,7 @@ impl UIInterfaceOrientationMask { pub struct UIRectEdge(NSUInteger); unsafe impl Encode for UIRectEdge { - fn encode() -> Encoding { - NSUInteger::encode() - } + const ENCODING: Encoding = NSUInteger::ENCODING; } impl From for UIRectEdge { @@ -245,9 +283,7 @@ impl Into for UIRectEdge { pub struct UIScreenOverscanCompensation(NSInteger); unsafe impl Encode for UIScreenOverscanCompensation { - fn encode() -> Encoding { - NSInteger::encode() - } + const ENCODING: Encoding = NSInteger::ENCODING; } #[allow(dead_code)] @@ -378,7 +414,7 @@ pub trait NSStringRust: Sized { impl NSStringRust for id { unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id { - msg_send![self, initWithUTF8String: c_string as id] + msg_send![self, initWithUTF8String: c_string] } unsafe fn stringByAppendingString_(self, other: id) -> id { diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs index c0149563a..85716578c 100644 --- a/src/platform_impl/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -66,8 +66,8 @@ // window size/position. macro_rules! assert_main_thread { ($($t:tt)*) => { - let is_main_thread: ::objc::runtime::BOOL = msg_send!(class!(NSThread), isMainThread); - if is_main_thread == ::objc::runtime::NO { + let is_main_thread: bool = msg_send![class!(NSThread), isMainThread]; + if !is_main_thread { panic!($($t)*); } }; diff --git a/src/platform_impl/ios/monitor.rs b/src/platform_impl/ios/monitor.rs index 6887a2195..cc1bafb13 100644 --- a/src/platform_impl/ios/monitor.rs +++ b/src/platform_impl/ios/monitor.rs @@ -192,7 +192,7 @@ impl MonitorHandle { pub fn retained_new(uiscreen: id) -> MonitorHandle { unsafe { assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS"); - let () = msg_send![uiscreen, retain]; + let _: id = msg_send![uiscreen, retain]; } MonitorHandle { inner: Inner { uiscreen }, diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs index bed79b856..9c91e1519 100644 --- a/src/platform_impl/ios/view.rs +++ b/src/platform_impl/ios/view.rs @@ -4,10 +4,7 @@ use std::{collections::HashMap, ffi::c_char}; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel, BOOL, NO, YES}, -}; +use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}; use crate::{ dpi::PhysicalPosition, @@ -18,7 +15,7 @@ use crate::{ event_loop::{self, EventProxy, EventWrapper}, ffi::{ id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask, - UIRectEdge, UITouchPhase, UITouchType, + UIRectEdge, UITouchPhase, UITouchType, BOOL, NO, YES, }, window::PlatformSpecificWindowBuilderAttributes, DeviceId, @@ -52,8 +49,9 @@ macro_rules! add_property { let setter = if $capability { #[allow(non_snake_case)] extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) { + #[allow(deprecated)] // TODO: define_class! unsafe { - $object.set_ivar::<$t>(VAR_NAME, value); + *$object.get_mut_ivar::<$t>(VAR_NAME) = value; } $after_set } @@ -61,8 +59,9 @@ macro_rules! add_property { } else { #[allow(non_snake_case)] extern "C" fn $setter_name($object: &mut Object, _: Sel, value: $t) { + #[allow(deprecated)] // TODO: define_class! unsafe { - $object.set_ivar::<$t>(VAR_NAME, value); + *$object.get_mut_ivar::<$t>(VAR_NAME) = value; } $err(&app_state::os_capabilities(), "ignoring") } @@ -70,15 +69,16 @@ macro_rules! add_property { }; #[allow(non_snake_case)] extern "C" fn $getter_name($object: &Object, _: Sel) -> $t { + #[allow(deprecated)] // TODO: define_class! unsafe { *$object.get_ivar::<$t>(VAR_NAME) } } $decl.add_method( sel!($setter_name:), - setter as extern "C" fn(&mut Object, Sel, $t), + setter as extern "C" fn(_, _, _), ); $decl.add_method( sel!($getter_name), - $getter_name as extern "C" fn(&Object, Sel) -> $t, + $getter_name as extern "C" fn(_, _) -> _, ); } }; @@ -97,11 +97,8 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { classes.entry(root_view_class).or_insert_with(move || { let uiview_class = class!(UIView); - let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class]; - assert_eq!( - is_uiview, YES, - "`root_view_class` must inherit from `UIView`" - ); + let is_uiview: bool = msg_send![root_view_class, isSubclassOfClass: uiview_class]; + assert!(is_uiview, "`root_view_class` must inherit from `UIView`"); extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) { unsafe { @@ -155,7 +152,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { } extern "C" fn set_content_scale_factor( - object: &mut Object, + object: &Object, _: Sel, untrusted_scale_factor: CGFloat, ) { @@ -286,34 +283,28 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let mut decl = ClassDecl::new(&format!("TaoUIView{}", ID), root_view_class) .expect("Failed to declare class `TaoUIView`"); ID += 1; - decl.add_method( - sel!(drawRect:), - draw_rect as extern "C" fn(&Object, Sel, CGRect), - ); - decl.add_method( - sel!(layoutSubviews), - layout_subviews as extern "C" fn(&Object, Sel), - ); + decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _)); + decl.add_method(sel!(layoutSubviews), layout_subviews as extern "C" fn(_, _)); decl.add_method( sel!(setContentScaleFactor:), - set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat), + set_content_scale_factor as extern "C" fn(_, _, _), ); decl.add_method( sel!(touchesBegan:withEvent:), - handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id), + handle_touches as extern "C" fn(_, _, _, _), ); decl.add_method( sel!(touchesMoved:withEvent:), - handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id), + handle_touches as extern "C" fn(_, _, _, _), ); decl.add_method( sel!(touchesEnded:withEvent:), - handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id), + handle_touches as extern "C" fn(_, _, _, _), ); decl.add_method( sel!(touchesCancelled:withEvent:), - handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id), + handle_touches as extern "C" fn(_, _, _, _), ); decl.register() @@ -336,7 +327,7 @@ unsafe fn get_view_controller_class() -> &'static Class { .expect("Failed to declare class `TaoUIViewController`"); decl.add_method( sel!(shouldAutorotate), - should_autorotate as extern "C" fn(&Object, Sel) -> BOOL, + should_autorotate as extern "C" fn(_, _) -> _, ); add_property! { decl, @@ -419,11 +410,11 @@ unsafe fn get_window_class() -> &'static Class { ClassDecl::new("TaoUIWindow", uiwindow_class).expect("Failed to declare class `TaoUIWindow`"); decl.add_method( sel!(becomeKeyWindow), - become_key_window as extern "C" fn(&Object, Sel), + become_key_window as extern "C" fn(_, _), ); decl.add_method( sel!(resignKeyWindow), - resign_key_window as extern "C" fn(&Object, Sel), + resign_key_window as extern "C" fn(_, _), ); CLASS = Some(decl.register()); @@ -548,7 +539,7 @@ pub unsafe fn create_window( } pub fn create_delegate_class() { - extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL { + extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id, _: id) -> BOOL { unsafe { app_state::did_finish_launching(); } @@ -576,7 +567,7 @@ pub fn create_delegate_class() { // custom URL schemes // https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app extern "C" fn application_open_url( - _self: &mut Object, + _self: &Object, _cmd: Sel, _app: id, url: id, @@ -590,7 +581,7 @@ pub fn create_delegate_class() { // universal links // https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app extern "C" fn application_continue( - _: &mut Object, + _: &Object, _: Sel, _application: id, user_activity: id, @@ -630,8 +621,8 @@ pub fn create_delegate_class() { if window == nil { break; } - let is_tao_window: BOOL = msg_send![window, isKindOfClass: class!(TaoUIWindow)]; - if is_tao_window == YES { + let is_tao_window: bool = msg_send![window, isKindOfClass: class!(TaoUIWindow)]; + if is_tao_window { events.push(EventWrapper::StaticEvent(Event::WindowEvent { window_id: RootWindowId(window.into()), event: WindowEvent::Destroyed, @@ -650,39 +641,39 @@ pub fn create_delegate_class() { unsafe { decl.add_method( sel!(application:didFinishLaunchingWithOptions:), - did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL, + did_finish_launching as extern "C" fn(_, _, _, _) -> _, ); decl.add_method( sel!(application:openURL:options:), - application_open_url as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL, + application_open_url as extern "C" fn(_, _, _, _, _) -> _, ); decl.add_method( sel!(application:continueUserActivity:restorationHandler:), - application_continue as extern "C" fn(&mut Object, Sel, id, id, id) -> BOOL, + application_continue as extern "C" fn(_, _, _, _, _) -> _, ); decl.add_method( sel!(applicationDidBecomeActive:), - did_become_active as extern "C" fn(&Object, Sel, id), + did_become_active as extern "C" fn(_, _, _), ); decl.add_method( sel!(applicationWillResignActive:), - will_resign_active as extern "C" fn(&Object, Sel, id), + will_resign_active as extern "C" fn(_, _, _), ); decl.add_method( sel!(applicationWillEnterForeground:), - will_enter_foreground as extern "C" fn(&Object, Sel, id), + will_enter_foreground as extern "C" fn(_, _, _), ); decl.add_method( sel!(applicationDidEnterBackground:), - did_enter_background as extern "C" fn(&Object, Sel, id), + did_enter_background as extern "C" fn(_, _, _), ); decl.add_method( sel!(applicationWillTerminate:), - will_terminate as extern "C" fn(&Object, Sel, id), + will_terminate as extern "C" fn(_, _, _), ); decl.register(); diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 2103fbace..40265cc01 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -7,7 +7,7 @@ use std::{ ops::{Deref, DerefMut}, }; -use objc::runtime::{Class, Object, BOOL, NO, YES}; +use objc2::runtime::{AnyClass, AnyObject}; use crate::{ dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, @@ -21,7 +21,7 @@ use crate::{ event_loop::{self, EventProxy, EventWrapper}, ffi::{ id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask, UIRectEdge, - UIScreenOverscanCompensation, + UIScreenOverscanCompensation, NO, YES, }, monitor, set_badge_count, view, EventLoopWindowTarget, MonitorHandle, }, @@ -522,11 +522,11 @@ impl Window { let view = view::create_view(&window_attributes, &platform_attributes, frame.clone()); let gl_or_metal_backed = { - let view_class: id = msg_send![view, class]; - let layer_class: id = msg_send![view_class, layerClass]; - let is_metal: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)]; - let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)]; - is_metal == YES || is_gl == YES + let view_class: *const AnyClass = msg_send![view, class]; + let layer_class: *const AnyClass = msg_send![view_class, layerClass]; + let is_metal: bool = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)]; + let is_gl: bool = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)]; + is_metal || is_gl }; let view_controller = @@ -740,16 +740,16 @@ impl WindowId { unsafe impl Send for WindowId {} unsafe impl Sync for WindowId {} -impl From<&Object> for WindowId { - fn from(window: &Object) -> WindowId { +impl From<&AnyObject> for WindowId { + fn from(window: &AnyObject) -> WindowId { WindowId { window: window as *const _ as _, } } } -impl From<&mut Object> for WindowId { - fn from(window: &mut Object) -> WindowId { +impl From<&mut AnyObject> for WindowId { + fn from(window: &mut AnyObject) -> WindowId { WindowId { window: window as _, } @@ -764,7 +764,7 @@ impl From for WindowId { #[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { - pub root_view_class: &'static Class, + pub root_view_class: &'static AnyClass, pub scale_factor: Option, pub valid_orientations: ValidOrientations, pub prefers_home_indicator_hidden: bool, diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index 7a14813da..610b7e57d 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -4,16 +4,10 @@ use std::collections::VecDeque; -use cocoa::{ - appkit::{self, NSEvent}, - base::id, -}; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel}, -}; - -use super::{app_state::AppState, event::EventWrapper, util, DEVICE_ID}; +use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}; +use objc2_app_kit::{self as appkit, NSEvent, NSEventType}; + +use super::{app_state::AppState, event::EventWrapper, ffi::id, util, DEVICE_ID}; use crate::event::{DeviceEvent, ElementState, Event}; pub struct AppClass(pub *const Class); @@ -25,10 +19,7 @@ lazy_static! { let superclass = class!(NSApplication); let mut decl = ClassDecl::new("TaoApp", superclass).unwrap(); - decl.add_method( - sel!(sendEvent:), - send_event as extern "C" fn(&Object, Sel, id), - ); + decl.add_method(sel!(sendEvent:), send_event as extern "C" fn(_, _, _)); AppClass(decl.register()) }; @@ -37,17 +28,17 @@ lazy_static! { // Normally, holding Cmd + any key never sends us a `keyUp` event for that key. // Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196) // Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553) -extern "C" fn send_event(this: &Object, _sel: Sel, event: id) { +extern "C" fn send_event(this: &Object, _sel: Sel, event: &NSEvent) { unsafe { // For posterity, there are some undocumented event types // (https://github.com/servo/cocoa-rs/issues/155) // but that doesn't really matter here. - let event_type = event.eventType(); + let event_type = event.r#type(); let modifier_flags = event.modifierFlags(); if event_type == appkit::NSKeyUp && util::has_flag( modifier_flags, - appkit::NSEventModifierFlags::NSCommandKeyMask, + appkit::NSEventModifierFlags::NSEventModifierFlagCommand, ) { let key_window: id = msg_send![this, keyWindow]; @@ -60,13 +51,13 @@ extern "C" fn send_event(this: &Object, _sel: Sel, event: id) { } } -unsafe fn maybe_dispatch_device_event(event: id) { - let event_type = event.eventType(); +unsafe fn maybe_dispatch_device_event(event: &NSEvent) { + let event_type = event.r#type(); match event_type { - appkit::NSMouseMoved - | appkit::NSLeftMouseDragged - | appkit::NSOtherMouseDragged - | appkit::NSRightMouseDragged => { + NSEventType::MouseMoved + | NSEventType::LeftMouseDragged + | NSEventType::OtherMouseDragged + | NSEventType::RightMouseDragged => { let mut events = VecDeque::with_capacity(3); let delta_x = event.deltaX() as f64; @@ -103,7 +94,7 @@ unsafe fn maybe_dispatch_device_event(event: id) { AppState::queue_events(events); } - appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => { + NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => { let mut events = VecDeque::with_capacity(1); events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { @@ -116,7 +107,7 @@ unsafe fn maybe_dispatch_device_event(event: id) { AppState::queue_events(events); } - appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => { + NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => { let mut events = VecDeque::with_capacity(1); events.push_back(EventWrapper::StaticEvent(Event::DeviceEvent { diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 2bcf7b3b3..47fe9b8f7 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -2,24 +2,21 @@ // Copyright 2021-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState}; - -use cocoa::{ - base::{id, NO}, - foundation::NSString, -}; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel, BOOL}, +use crate::{ + platform::macos::ActivationPolicy, + platform_impl::platform::{ + app_state::AppState, + ffi::{id, BOOL, YES}, + }, }; + +use objc2::runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}; +use objc2_foundation::{NSArray, NSURL}; use std::{ cell::{RefCell, RefMut}, os::raw::c_void, }; -use cocoa::foundation::{NSArray, NSURL}; -use std::ffi::CStr; - static AUX_DELEGATE_STATE_NAME: &str = "auxState"; pub struct AuxDelegateState { @@ -40,28 +37,28 @@ lazy_static! { let superclass = class!(NSResponder); let mut decl = ClassDecl::new("TaoAppDelegateParent", superclass).unwrap(); - decl.add_class_method(sel!(new), new as extern "C" fn(&Class, Sel) -> id); - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_class_method(sel!(new), new as extern "C" fn(_, _) -> _); + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _)); decl.add_method( sel!(applicationDidFinishLaunching:), - did_finish_launching as extern "C" fn(&Object, Sel, id), + did_finish_launching as extern "C" fn(_, _, _), ); decl.add_method( sel!(applicationWillTerminate:), - application_will_terminate as extern "C" fn(&Object, Sel, id), + application_will_terminate as extern "C" fn(_, _, _), ); decl.add_method( sel!(application:openURLs:), - application_open_urls as extern "C" fn(&Object, Sel, id, id), + application_open_urls as extern "C" fn(_, _, _, _), ); decl.add_method( sel!(applicationShouldHandleReopen:hasVisibleWindows:), - application_should_handle_reopen as extern "C" fn(&Object, Sel, id, BOOL) -> BOOL, + application_should_handle_reopen as extern "C" fn(_, _, _, _) -> _, ); decl.add_method( sel!(applicationSupportsSecureRestorableState:), - application_supports_secure_restorable_state as extern "C" fn(&Object, Sel, id) -> BOOL, + application_supports_secure_restorable_state as extern "C" fn(_, _, _) -> _, ); decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME); @@ -70,6 +67,7 @@ lazy_static! { } /// Safety: Assumes that Object is an instance of APP_DELEGATE_CLASS +#[allow(deprecated)] // TODO: Use define_class! pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> { let ptr: *mut c_void = *this.get_ivar(AUX_DELEGATE_STATE_NAME); // Watch out that this needs to be the correct type @@ -77,21 +75,21 @@ pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> { } extern "C" fn new(class: &Class, _: Sel) -> id { + #[allow(deprecated)] // TODO: Use define_class! unsafe { let this: id = msg_send![class, alloc]; let this: id = msg_send![this, init]; - (*this).set_ivar( - AUX_DELEGATE_STATE_NAME, + *(*this).get_mut_ivar(AUX_DELEGATE_STATE_NAME) = Box::into_raw(Box::new(RefCell::new(AuxDelegateState { activation_policy: ActivationPolicy::Regular, activate_ignoring_other_apps: true, - }))) as *mut c_void, - ); + }))) as *mut c_void; this } } extern "C" fn dealloc(this: &Object, _: Sel) { + #[allow(deprecated)] // TODO: Use define_class! unsafe { let state_ptr: *mut c_void = *(this.get_ivar(AUX_DELEGATE_STATE_NAME)); // As soon as the box is constructed it is immediately dropped, releasing the underlying @@ -112,16 +110,12 @@ extern "C" fn application_will_terminate(_: &Object, _: Sel, _: id) { trace!("Completed `applicationWillTerminate`"); } -extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () { +extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: &NSArray) -> () { trace!("Trigger `application:openURLs:`"); let urls = unsafe { (0..urls.count()) - .map(|i| { - url::Url::parse( - &CStr::from_ptr(urls.objectAtIndex(i).absoluteString().UTF8String()).to_string_lossy(), - ) - }) + .map(|i| url::Url::parse(&urls.objectAtIndex(i).absoluteString().unwrap().to_string())) .flatten() .collect::>() }; @@ -137,7 +131,7 @@ extern "C" fn application_should_handle_reopen( has_visible_windows: BOOL, ) -> BOOL { trace!("Triggered `applicationShouldHandleReopen`"); - AppState::reopen(has_visible_windows != NO); + AppState::reopen(has_visible_windows.as_bool()); trace!("Completed `applicationShouldHandleReopen`"); has_visible_windows } @@ -145,5 +139,5 @@ extern "C" fn application_should_handle_reopen( extern "C" fn application_supports_secure_restorable_state(_: &Object, _: Sel, _: id) -> BOOL { trace!("Triggered `applicationSupportsSecureRestorableState`"); trace!("Completed `applicationSupportsSecureRestorableState`"); - objc::runtime::YES + YES } diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 65125cd4e..cc4fb5d0c 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -16,12 +16,9 @@ use std::{ time::Instant, }; -use cocoa::{ - appkit::{NSApp, NSApplication, NSWindow}, - base::{id, nil}, - foundation::{NSAutoreleasePool, NSSize}, -}; -use objc::runtime::{Object, NO, YES}; +use objc2::{msg_send_id, rc::Retained, runtime::AnyObject as Object}; +use objc2_app_kit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow}; +use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSSize}; use crate::{ dpi::LogicalSize, @@ -33,8 +30,9 @@ use crate::{ platform::{ event::{EventProxy, EventWrapper}, event_loop::{post_dummy_event, PanicInfo}, + ffi::{id, nil}, observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker}, - util::{self, IdRef, Never}, + util::{self, Never}, window::get_window_id, }, }, @@ -221,14 +219,14 @@ impl Handler { fn handle_scale_factor_changed_event( &self, callback: &mut Box, - ns_window: IdRef, + ns_window: &NSWindow, suggested_size: LogicalSize, scale_factor: f64, ) { let mut size = suggested_size.to_physical(scale_factor); let old_size = size.clone(); let event = Event::WindowEvent { - window_id: WindowId(get_window_id(*ns_window)), + window_id: WindowId(get_window_id(ns_window)), event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size: &mut size, @@ -240,7 +238,7 @@ impl Handler { if old_size != size { let logical_size = size.to_logical(scale_factor); let size = NSSize::new(logical_size.width, logical_size.height); - unsafe { NSWindow::setContentSize_(*ns_window, size) }; + NSWindow::setContentSize(ns_window, size); } } @@ -251,7 +249,7 @@ impl Handler { suggested_size, scale_factor, } => { - self.handle_scale_factor_changed_event(callback, ns_window, suggested_size, scale_factor) + self.handle_scale_factor_changed_event(callback, &ns_window, suggested_size, scale_factor) } } } @@ -285,14 +283,12 @@ impl AppState { pub fn launched(app_delegate: &Object) { apply_activation_policy(app_delegate); unsafe { - let ns_app = NSApp(); - window_activation_hack(ns_app); - let ignore = if get_aux_state_mut(app_delegate).activate_ignoring_other_apps { - YES - } else { - NO - }; - ns_app.activateIgnoringOtherApps_(ignore); + let mtm = MainThreadMarker::new().unwrap(); + let ns_app = NSApp(mtm); + window_activation_hack(&ns_app); + let ignore = get_aux_state_mut(app_delegate).activate_ignoring_other_apps; + #[allow(deprecated)] + ns_app.activateIgnoringOtherApps(ignore); }; HANDLER.set_ready(); HANDLER.waker().start(); @@ -399,12 +395,12 @@ impl AppState { HANDLER.set_in_callback(false); if HANDLER.should_exit() { unsafe { - let app: id = NSApp(); - let pool = NSAutoreleasePool::new(nil); - let () = msg_send![app, stop: nil]; + let mtm = MainThreadMarker::new().unwrap(); + let app = NSApp(mtm); + let _pool = NSAutoreleasePool::new(); + let () = msg_send![&app, stop: nil]; // To stop event loop immediately, we need to post some event here. - post_dummy_event(app); - pool.drain(); + post_dummy_event(&app); }; } HANDLER.update_start_time(); @@ -426,23 +422,24 @@ impl AppState { /// /// If this becomes too bothersome to maintain, it can probably be removed /// without too much damage. -unsafe fn window_activation_hack(ns_app: id) { +unsafe fn window_activation_hack(ns_app: &NSApplication) { // Get the application's windows // TODO: Proper ordering of the windows let ns_windows: id = msg_send![ns_app, windows]; let ns_enumerator: id = msg_send![ns_windows, objectEnumerator]; loop { // Enumerate over the windows - let ns_window: id = msg_send![ns_enumerator, nextObject]; - if ns_window == nil { + let ns_window: Option> = msg_send_id![ns_enumerator, nextObject]; + if ns_window.is_none() { break; } + let ns_window = ns_window.unwrap(); // And call `makeKeyAndOrderFront` if it was called on the window in `UnownedWindow::new` // This way we preserve the user's desired initial visiblity status // TODO: Also filter on the type/"level" of the window, and maybe other things? - if ns_window.isVisible() == YES { + if ns_window.isVisible() == true { trace!("Activating visible window"); - ns_window.makeKeyAndOrderFront_(nil); + ns_window.makeKeyAndOrderFront(None); } else { trace!("Skipping activating invisible window"); } @@ -450,16 +447,16 @@ unsafe fn window_activation_hack(ns_app: id) { } fn apply_activation_policy(app_delegate: &Object) { unsafe { - use cocoa::appkit::NSApplicationActivationPolicy::*; - let ns_app = NSApp(); + let mtm = MainThreadMarker::new().unwrap(); + let ns_app = NSApp(mtm); // We need to delay setting the activation policy and activating the app // until `applicationDidFinishLaunching` has been called. Otherwise the // menu bar won't be interactable. let act_pol = get_aux_state_mut(app_delegate).activation_policy; - ns_app.setActivationPolicy_(match act_pol { - ActivationPolicy::Regular => NSApplicationActivationPolicyRegular, - ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory, - ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited, + ns_app.setActivationPolicy(match act_pol { + ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular, + ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory, + ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited, }); } } diff --git a/src/platform_impl/macos/badge.rs b/src/platform_impl/macos/badge.rs index d7c5cf9e0..f48e649cf 100644 --- a/src/platform_impl/macos/badge.rs +++ b/src/platform_impl/macos/badge.rs @@ -1,12 +1,16 @@ -use cocoa::{appkit::NSApp, base::nil, foundation::NSString}; +use super::ffi::id; +use objc2_app_kit::NSApp; +use objc2_foundation::{MainThreadMarker, NSString}; pub fn set_badge_label(label: Option) { + // SAFETY: TODO + let mtm = unsafe { MainThreadMarker::new_unchecked() }; unsafe { let label = match label { - None => nil, - Some(label) => NSString::alloc(nil).init_str(&label), + None => None, + Some(label) => Some(NSString::from_str(&label)), }; - let dock_tile: cocoa::base::id = msg_send![NSApp(), dockTile]; - let _: cocoa::base::id = msg_send![dock_tile, setBadgeLabel: label]; + let dock_tile: id = msg_send![&NSApp(mtm), dockTile]; + let _: () = msg_send![dock_tile, setBadgeLabel: label.as_deref()]; } } diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index c671a6d13..8ed2f78e7 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -4,21 +4,17 @@ use std::{collections::HashSet, ffi::c_void, os::raw::c_ushort, sync::Mutex}; -use cocoa::{ - appkit::{NSEvent, NSEventModifierFlags}, - base::id, -}; +use objc2::{msg_send_id, rc::Retained}; +use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSWindow}; use core_foundation::{base::CFRelease, data::CFDataGetBytePtr}; +use objc2_foundation::NSString; use crate::{ dpi::LogicalSize, event::{ElementState, Event, KeyEvent}, keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NativeKeyCode}, - platform_impl::platform::{ - ffi, - util::{ns_string_to_rust, IdRef, Never}, - }, + platform_impl::platform::{ffi, util::Never}, }; lazy_static! { @@ -47,7 +43,7 @@ pub enum EventWrapper { pub enum EventProxy { #[non_exhaustive] DpiChangedProxy { - ns_window: IdRef, + ns_window: Retained, suggested_size: LogicalSize, scale_factor: f64, }, @@ -115,9 +111,10 @@ pub fn get_modifierless_char(scancode: u16) -> Key<'static> { Key::Character(insert_or_get_key_str(chars)) } -fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static> { - let characters: id = unsafe { msg_send![ns_event, charactersIgnoringModifiers] }; - let string = unsafe { ns_string_to_rust(characters) }; +fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key<'static> { + let characters: Retained = + unsafe { msg_send_id![ns_event, charactersIgnoringModifiers] }; + let string = characters.to_string(); if string.is_empty() { // Probably a dead key let first_char = modifierless_chars.chars().next(); @@ -128,7 +125,7 @@ fn get_logical_key_char(ns_event: id, modifierless_chars: &str) -> Key<'static> #[allow(clippy::unnecessary_unwrap)] pub fn create_key_event( - ns_event: id, + ns_event: &NSEvent, is_press: bool, is_repeat: bool, in_ime: bool, @@ -144,8 +141,8 @@ pub fn create_key_event( if key_override.is_some() { None } else { - let characters: id = unsafe { msg_send![ns_event, characters] }; - let characters = unsafe { ns_string_to_rust(characters) }; + let characters: Retained = unsafe { msg_send_id![ns_event, characters] }; + let characters = characters.to_string(); if characters.is_empty() { None } else { @@ -168,8 +165,8 @@ pub fn create_key_event( key_without_modifiers = get_modifierless_char(scancode); let modifiers = unsafe { NSEvent::modifierFlags(ns_event) }; - let has_alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask); - let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask); + let has_alt = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagOption); + let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl); if has_alt || has_ctrl || text_with_all_modifiers.is_none() || !is_press { let modifierless_chars = match key_without_modifiers.clone() { Key::Character(ch) => ch, @@ -308,29 +305,29 @@ pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode { } } -pub fn event_mods(event: id) -> ModifiersState { +pub fn event_mods(event: &NSEvent) -> ModifiersState { let flags = unsafe { NSEvent::modifierFlags(event) }; let mut m = ModifiersState::empty(); m.set( ModifiersState::SHIFT, - flags.contains(NSEventModifierFlags::NSShiftKeyMask), + flags.contains(NSEventModifierFlags::NSEventModifierFlagShift), ); m.set( ModifiersState::CONTROL, - flags.contains(NSEventModifierFlags::NSControlKeyMask), + flags.contains(NSEventModifierFlags::NSEventModifierFlagControl), ); m.set( ModifiersState::ALT, - flags.contains(NSEventModifierFlags::NSAlternateKeyMask), + flags.contains(NSEventModifierFlags::NSEventModifierFlagOption), ); m.set( ModifiersState::SUPER, - flags.contains(NSEventModifierFlags::NSCommandKeyMask), + flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand), ); m } -pub fn get_scancode(event: cocoa::base::id) -> c_ushort { +pub fn get_scancode(event: &NSEvent) -> c_ushort { // In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character, // and there is no easy way to navtively retrieve the layout-dependent character. // In tao, we use keycode to refer to the key's character, and so this function aligns diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 964252b65..b3bf12e57 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -14,12 +14,10 @@ use std::{ rc::{Rc, Weak}, }; -use cocoa::{ - appkit::{NSApp, NSEventModifierFlags, NSEventSubtype, NSEventType::NSApplicationDefined}, - base::{id, nil, YES}, - foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval}, -}; use crossbeam_channel::{self as channel, Receiver, Sender}; +use objc2::{msg_send_id, rc::Retained}; +use objc2_app_kit::{NSApp, NSApplication, NSEventModifierFlags, NSEventSubtype, NSEventType}; +use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSInteger, NSPoint, NSTimeInterval}; use scopeguard::defer; use crate::{ @@ -33,6 +31,7 @@ use crate::{ app::APP_CLASS, app_delegate::APP_DELEGATE_CLASS, app_state::AppState, + ffi::{id, nil, YES}, monitor::{self, MonitorHandle}, observer::*, util::{self, IdRef}, @@ -176,9 +175,8 @@ impl EventLoop { let app: id = msg_send![APP_CLASS.0, sharedApplication]; let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]); - let pool = NSAutoreleasePool::new(nil); + let _pool = NSAutoreleasePool::new(); let _: () = msg_send![app, setDelegate:*delegate]; - let _: () = msg_send![pool, drain]; delegate }; @@ -222,11 +220,11 @@ impl EventLoop { self._callback = Some(Rc::clone(&callback)); + let mtm = MainThreadMarker::new().unwrap(); + let exit_code = unsafe { - let pool = NSAutoreleasePool::new(nil); - defer!(pool.drain()); - let app = NSApp(); - assert_ne!(app, nil); + let _pool = NSAutoreleasePool::new(); + let app = NSApp(mtm); // A bit of juggling with the callback references to make sure // that `self.callback` is the only owner of the callback. @@ -234,7 +232,7 @@ impl EventLoop { mem::drop(callback); AppState::set_callback(weak_cb, Rc::clone(&self.window_target)); - let () = msg_send![app, run]; + let () = msg_send![&app, run]; if let Some(panic) = self.panic_info.take() { drop(self._callback.take()); @@ -253,17 +251,17 @@ impl EventLoop { } #[inline] -pub unsafe fn post_dummy_event(target: id) { +pub unsafe fn post_dummy_event(target: &NSApplication) { let event_class = class!(NSEvent); let dummy_event: id = msg_send![ event_class, - otherEventWithType: NSApplicationDefined + otherEventWithType: NSEventType::ApplicationDefined location: NSPoint::new(0.0, 0.0) modifierFlags: NSEventModifierFlags::empty() timestamp: 0 as NSTimeInterval windowNumber: 0 as NSInteger context: nil - subtype: NSEventSubtype::NSWindowExposedEventType + subtype: NSEventSubtype::WindowExposed data1: 0 as NSInteger data2: 0 as NSInteger ]; @@ -290,12 +288,12 @@ pub fn stop_app_on_panic R + UnwindSafe, R>( } unsafe { let app_class = class!(NSApplication); - let app: id = msg_send![app_class, sharedApplication]; - let () = msg_send![app, stop: nil]; + let app: Retained = msg_send_id![app_class, sharedApplication]; + let () = msg_send![&app, stop: nil]; // Posting a dummy event to get `stop` to take effect immediately. // See: https://stackoverflow.com/questions/48041279/stopping-the-nsapplication-main-event-loop/48064752#48064752 - post_dummy_event(app); + post_dummy_event(&app); } None } diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index b77bfdc84..3493d1c36 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -11,13 +11,8 @@ clippy::enum_variant_names )] -use std::ffi::c_void; +use std::{ffi::c_void, ptr}; -use cocoa::{ - appkit::CGPoint, - base::id, - foundation::{NSInteger, NSUInteger}, -}; use core_foundation::{ array::CFArrayRef, data::CFDataRef, dictionary::CFDictionaryRef, string::CFStringRef, uuid::CFUUIDRef, @@ -25,74 +20,26 @@ use core_foundation::{ use core_graphics::{ base::CGError, display::{boolean_t, CGDirectDisplayID, CGDisplayConfigRef}, - geometry::CGRect, + geometry::{CGPoint, CGRect}, }; -pub const NSNotFound: NSInteger = NSInteger::max_value(); - -#[repr(C)] -pub struct NSRange { - pub location: NSUInteger, - pub length: NSUInteger, -} - -impl NSRange { - #[inline] - pub fn new(location: NSUInteger, length: NSUInteger) -> NSRange { - NSRange { location, length } - } -} - -unsafe impl objc::Encode for NSRange { - fn encode() -> objc::Encoding { - let encoding = format!( - // TODO: Verify that this is correct - "{{NSRange={}{}}}", - NSUInteger::encode().as_str(), - NSUInteger::encode().as_str(), - ); - unsafe { objc::Encoding::from_str(&encoding) } - } -} - -pub trait NSMutableAttributedString: Sized { - unsafe fn alloc(_: Self) -> id { - msg_send![class!(NSMutableAttributedString), alloc] - } - - unsafe fn init(self) -> id; // *mut NSMutableAttributedString - unsafe fn initWithString(self, string: id) -> id; - unsafe fn initWithAttributedString(self, string: id) -> id; - - unsafe fn string(self) -> id; // *mut NSString - unsafe fn mutableString(self) -> id; // *mut NSMutableString - unsafe fn length(self) -> NSUInteger; -} - -impl NSMutableAttributedString for id { - unsafe fn init(self) -> id { - msg_send![self, init] - } - - unsafe fn initWithString(self, string: id) -> id { - msg_send![self, initWithString: string] - } - - unsafe fn initWithAttributedString(self, string: id) -> id { - msg_send![self, initWithAttributedString: string] - } +use objc2::{ + encode::{Encode, Encoding}, + runtime::{AnyObject, Bool}, +}; +use objc2_foundation::NSInteger; - unsafe fn string(self) -> id { - msg_send![self, string] - } +#[allow(non_camel_case_types)] +pub type id = *mut AnyObject; +pub const nil: id = ptr::null_mut(); - unsafe fn mutableString(self) -> id { - msg_send![self, mutableString] - } +#[allow(non_camel_case_types)] +pub type BOOL = Bool; +#[allow(deprecated)] +pub const YES: Bool = Bool::YES; +#[allow(deprecated)] +pub const NO: Bool = Bool::NO; - unsafe fn length(self) -> NSUInteger { - msg_send![self, length] - } -} +pub const NSNotFound: NSInteger = NSInteger::max_value(); pub const kCGBaseWindowLevelKey: NSInteger = 0; pub const kCGMinimumWindowLevelKey: NSInteger = 1; @@ -130,6 +77,10 @@ pub enum NSWindowLevel { NSScreenSaverWindowLevel = kCGScreenSaverWindowLevelKey as _, } +unsafe impl Encode for NSWindowLevel { + const ENCODING: Encoding = isize::ENCODING; +} + pub type CGDisplayFadeInterval = f32; pub type CGDisplayReservationInterval = f32; pub type CGDisplayBlendFraction = f32; diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index fd53a5e86..3d50867f3 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -25,7 +25,7 @@ use std::{fmt, ops::Deref, sync::Arc}; pub(crate) use self::event_loop::PlatformSpecificEventLoopAttributes; pub use self::{ - app_delegate::{get_aux_state_mut, AuxDelegateState}, + app_delegate::get_aux_state_mut, event::KeyEventExtra, event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy}, keycode::{keycode_from_scancode, keycode_to_scancode}, diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index 538a80b28..20c8baf8a 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -4,25 +4,23 @@ use std::{collections::VecDeque, fmt}; -use super::{ - ffi::{self, CGRectContainsPoint}, - util, -}; +use super::ffi::{self, id, nil, CGRectContainsPoint}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, }; -use cocoa::{ - appkit::{CGPoint, NSScreen}, - base::{id, nil}, - foundation::NSUInteger, -}; use core_foundation::{ array::{CFArrayGetCount, CFArrayGetValueAtIndex}, base::{CFRelease, TCFType}, string::CFString, }; -use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; +use core_graphics::{ + display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}, + geometry::CGPoint, +}; +use objc2::{msg_send_id, rc::Retained}; +use objc2_app_kit::NSScreen; +use objc2_foundation::{MainThreadMarker, NSString, NSUInteger}; #[derive(Clone)] pub struct VideoMode { @@ -237,7 +235,7 @@ impl MonitorHandle { Some(screen) => screen, None => return 1.0, // default to 1.0 when we can't find the screen }; - unsafe { NSScreen::backingScaleFactor(screen) as f64 } + NSScreen::backingScaleFactor(&screen) as f64 } pub fn video_modes(&self) -> impl Iterator { @@ -313,16 +311,18 @@ impl MonitorHandle { } } - pub(crate) fn ns_screen(&self) -> Option { + pub(crate) fn ns_screen(&self) -> Option> { + // SAFETY: TODO. + let mtm = unsafe { MainThreadMarker::new_unchecked() }; unsafe { let uuid = ffi::CGDisplayCreateUUIDFromDisplayID(self.0); - let screens = NSScreen::screens(nil); - let count: NSUInteger = msg_send![screens, count]; - let key = util::ns_string_id_ref("NSScreenNumber"); + let screens = NSScreen::screens(mtm); + let count: NSUInteger = msg_send![&screens, count]; + let key = NSString::from_str("NSScreenNumber"); for i in 0..count { - let screen = msg_send![screens, objectAtIndex: i as NSUInteger]; - let device_description = NSScreen::deviceDescription(screen); - let value: id = msg_send![device_description, objectForKey:*key]; + let screen: Retained = msg_send_id![&screens, objectAtIndex: i as NSUInteger]; + let device_description = NSScreen::deviceDescription(&screen); + let value: id = msg_send![&device_description, objectForKey: &*key]; if value != nil { let other_native_id: NSUInteger = msg_send![value, unsignedIntegerValue]; let other_uuid = diff --git a/src/platform_impl/macos/progress_bar.rs b/src/platform_impl/macos/progress_bar.rs index 218f93bed..91ae46c24 100644 --- a/src/platform_impl/macos/progress_bar.rs +++ b/src/platform_impl/macos/progress_bar.rs @@ -1,14 +1,13 @@ use std::sync::Once; -use cocoa::{ - base::{id, nil}, - foundation::{NSArray, NSPoint, NSRect, NSSize}, -}; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel, NO}, +use objc2::{ + msg_send_id, + rc::Retained, + runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}, }; +use objc2_foundation::{NSArray, NSInsetRect, NSPoint, NSRect, NSSize}; +use super::ffi::{id, nil, NO}; use crate::window::{ProgressBarState, ProgressState}; /// Set progress indicator in the Dock. @@ -30,8 +29,9 @@ pub fn set_progress_indicator(progress_state: ProgressBarState) { let _: () = msg_send![progress_indicator, setDoubleValue: progress]; let _: () = msg_send![progress_indicator, setHidden: NO]; } + #[allow(deprecated)] // TODO: Use define_class! if let Some(state) = progress_state.state { - (*progress_indicator).set_ivar("state", state as u8); + *(*progress_indicator).get_mut_ivar("state") = state as u8; let _: () = msg_send![ progress_indicator, setHidden: matches!(state, ProgressState::None) @@ -61,7 +61,7 @@ fn create_progress_indicator(ns_app: id, dock_tile: id) -> id { let progress_class = create_progress_indicator_class(); let progress_indicator: id = msg_send![progress_class, alloc]; let progress_indicator: id = msg_send![progress_indicator, initWithFrame: frame]; - let _: () = msg_send![progress_indicator, autorelease]; + let _: id = msg_send![progress_indicator, autorelease]; // set progress indicator to the dock tile let _: () = msg_send![image_view, addSubview: progress_indicator]; @@ -76,13 +76,14 @@ fn get_exist_progress_indicator(dock_tile: id) -> Option { if content_view == nil { return None; } - let subviews: id /* NSArray */ = msg_send![content_view, subviews]; - if subviews == nil { + let subviews: Option> = msg_send_id![content_view, subviews]; + if subviews.is_none() { return None; } + let subviews = subviews.unwrap(); for idx in 0..subviews.count() { - let subview: id = msg_send![subviews, objectAtIndex: idx]; + let subview: id = msg_send![&subviews, objectAtIndex: idx]; let is_progress_indicator: bool = msg_send![subview, isKindOfClass: class!(NSProgressIndicator)]; @@ -102,10 +103,7 @@ fn create_progress_indicator_class() -> *const Class { let superclass = class!(NSProgressIndicator); let mut decl = ClassDecl::new("TaoProgressIndicator", superclass).unwrap(); - decl.add_method( - sel!(drawRect:), - draw_progress_bar as extern "C" fn(&Object, _, NSRect), - ); + decl.add_method(sel!(drawRect:), draw_progress_bar as extern "C" fn(_, _, _)); // progress bar states, follows ProgressState decl.add_ivar::("state"); @@ -117,6 +115,7 @@ fn create_progress_indicator_class() -> *const Class { } extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) { + #[allow(deprecated)] // TODO: Use define_class! unsafe { let bar = NSRect::new( NSPoint { x: 0.0, y: 4.0 }, @@ -125,8 +124,8 @@ extern "C" fn draw_progress_bar(this: &Object, _: Sel, rect: NSRect) { height: 8.0, }, ); - let bar_inner = bar.inset(0.5, 0.5); - let mut bar_progress = bar.inset(1.0, 1.0); + let bar_inner = NSInsetRect(bar, 0.5, 0.5); + let mut bar_progress = NSInsetRect(bar, 1.0, 1.0); // set progress width let current_progress: f64 = msg_send![this, doubleValue]; diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index bfaae9875..a3aa9a6c5 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -7,25 +7,22 @@ use std::{ sync::{Mutex, Weak}, }; -use cocoa::{ - appkit::{CGFloat, NSScreen, NSWindow, NSWindowStyleMask}, - base::{id, nil}, - foundation::{NSPoint, NSSize, NSString}, -}; +use core_graphics::base::CGFloat; use dispatch::Queue; -use objc::{ - rc::autoreleasepool, - runtime::{BOOL, NO, YES}, -}; +use objc2::{rc::autoreleasepool, ClassType}; +use objc2_app_kit::{NSScreen, NSView, NSWindow, NSWindowStyleMask}; +use objc2_foundation::{MainThreadMarker, NSPoint, NSSize, NSString}; use crate::{ dpi::LogicalSize, - platform_impl::platform::{ffi, util::IdRef, window::SharedState}, + platform_impl::platform::{ + ffi::{self, id, NO, YES}, + window::SharedState, + }, }; pub fn is_main_thread() -> bool { - let is: BOOL = unsafe { msg_send!(class!(NSThread), isMainThread) }; - is == YES + unsafe { msg_send!(class!(NSThread), isMainThread) } } // Unsafe wrapper type that allows us to dispatch things that aren't Send. @@ -51,72 +48,76 @@ fn run_on_main(f: impl FnOnce() -> R + Send) -> R { } } -unsafe fn set_style_mask(ns_window: id, ns_view: id, mask: NSWindowStyleMask) { - ns_window.setStyleMask_(mask); +unsafe fn set_style_mask(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) { + ns_window.setStyleMask(mask); // If we don't do this, key handling will break // (at least until the window is clicked again/etc.) - ns_window.makeFirstResponder_(ns_view); + ns_window.makeFirstResponder(Some(ns_view)); } // Always use this function instead of trying to modify `styleMask` directly! // `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch. // Otherwise, this would vomit out errors about not being on the main thread // and fail to do anything. -pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowStyleMask) { - let ns_window = MainThreadSafe(ns_window); - let ns_view = MainThreadSafe(ns_view); +pub unsafe fn set_style_mask_async( + ns_window: &NSWindow, + ns_view: &NSView, + mask: NSWindowStyleMask, +) { + let ns_window = MainThreadSafe(ns_window.retain()); + let ns_view = MainThreadSafe(ns_view.retain()); Queue::main().exec_async(move || { - set_style_mask(*ns_window, *ns_view, mask); + set_style_mask(&*ns_window, &*ns_view, mask); }); } -pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) { +pub unsafe fn set_style_mask_sync(ns_window: &NSWindow, ns_view: &NSView, mask: NSWindowStyleMask) { if is_main_thread() { set_style_mask(ns_window, ns_view, mask); } else { - let ns_window = MainThreadSafe(ns_window); - let ns_view = MainThreadSafe(ns_view); + let ns_window = MainThreadSafe(ns_window.retain()); + let ns_view = MainThreadSafe(ns_view.retain()); Queue::main().exec_sync(move || { - set_style_mask(*ns_window, *ns_view, mask); + set_style_mask(&*ns_window, &*ns_view, mask); }) } } // `setContentSize:` isn't thread-safe either, though it doesn't log any errors // and just fails silently. Anyway, GCD to the rescue! -pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_content_size_async(ns_window: &NSWindow, size: LogicalSize) { + let ns_window = MainThreadSafe(ns_window.retain()); Queue::main().exec_async(move || { - ns_window.setContentSize_(NSSize::new(size.width as CGFloat, size.height as CGFloat)); + ns_window.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat)); }); } // `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy // to log errors. -pub unsafe fn set_frame_top_left_point_async(ns_window: id, point: NSPoint) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_frame_top_left_point_async(ns_window: &NSWindow, point: NSPoint) { + let ns_window = MainThreadSafe(ns_window.retain()); Queue::main().exec_async(move || { - ns_window.setFrameTopLeftPoint_(point); + ns_window.setFrameTopLeftPoint(point); }); } // `setFrameTopLeftPoint:` isn't thread-safe, and fails silently. -pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_level_async(ns_window: &NSWindow, level: ffi::NSWindowLevel) { + let ns_window = MainThreadSafe(ns_window.retain()); Queue::main().exec_async(move || { - ns_window.setLevel_(level as _); + ns_window.setLevel(level as _); }); } // `toggleFullScreen` is thread-safe, but our additional logic to account for // window styles isn't. pub unsafe fn toggle_full_screen_async( - ns_window: id, - ns_view: id, + ns_window: &NSWindow, + ns_view: &NSView, not_fullscreen: bool, shared_state: Weak>, ) { - let ns_window = MainThreadSafe(ns_window); - let ns_view = MainThreadSafe(ns_view); + let ns_window = MainThreadSafe(ns_window.retain()); + let ns_view = MainThreadSafe(ns_view.retain()); let shared_state = MainThreadSafe(shared_state); Queue::main().exec_async(move || { // `toggleFullScreen` doesn't work if the `StyleMask` is none, so we @@ -124,10 +125,9 @@ pub unsafe fn toggle_full_screen_async( // restored in `WindowDelegate::window_did_exit_fullscreen`. if not_fullscreen { let curr_mask = ns_window.styleMask(); - let required = - NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask; + let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable; if !curr_mask.contains(required) { - set_style_mask(*ns_window, *ns_view, required); + set_style_mask(&ns_window, &ns_view, required); if let Some(shared_state) = shared_state.upgrade() { trace!("Locked shared state in `toggle_full_screen_callback`"); let mut shared_state_lock = shared_state.lock().unwrap(); @@ -139,8 +139,8 @@ pub unsafe fn toggle_full_screen_async( // Window level must be restored from `CGShieldingWindowLevel() // + 1` back to normal in order for `toggleFullScreen` to do // anything - ns_window.setLevel_(0); - ns_window.toggleFullScreen_(nil); + ns_window.setLevel(0); + ns_window.toggleFullScreen(None); }); } @@ -153,12 +153,12 @@ pub unsafe fn restore_display_mode_async(ns_screen: u32) { // `setMaximized` is not thread-safe pub unsafe fn set_maximized_async( - ns_window: id, + ns_window: &NSWindow, is_zoomed: bool, maximized: bool, shared_state: Weak>, ) { - let ns_window = MainThreadSafe(ns_window); + let ns_window = MainThreadSafe(ns_window.retain()); let shared_state = MainThreadSafe(shared_state); Queue::main().exec_async(move || { if let Some(shared_state) = shared_state.upgrade() { @@ -167,7 +167,7 @@ pub unsafe fn set_maximized_async( // Save the standard frame sized if it is not zoomed if !is_zoomed { - shared_state_lock.standard_frame = Some(NSWindow::frame(*ns_window)); + shared_state_lock.standard_frame = Some(NSWindow::frame(&ns_window)); } shared_state_lock.maximized = maximized; @@ -176,20 +176,21 @@ pub unsafe fn set_maximized_async( if shared_state_lock.fullscreen.is_some() { // Handle it in window_did_exit_fullscreen return; - } else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) - && curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) + } else if curr_mask.contains(NSWindowStyleMask::Resizable) + && curr_mask.contains(NSWindowStyleMask::Titled) { // Just use the native zoom if resizable - ns_window.zoom_(nil); + ns_window.zoom(None); } else { // if it's not resizable, we set the frame directly let new_rect = if maximized { - let screen = NSScreen::mainScreen(nil); - NSScreen::visibleFrame(screen) + let mtm = MainThreadMarker::new_unchecked(); + let screen = NSScreen::mainScreen(mtm).unwrap(); + NSScreen::visibleFrame(&screen) } else { shared_state_lock.saved_standard_frame() }; - let _: () = msg_send![*ns_window, setFrame:new_rect display:NO animate: YES]; + let _: () = msg_send![&*ns_window, setFrame:new_rect display:NO animate: YES]; } trace!("Unlocked shared state in `set_maximized`"); @@ -199,38 +200,38 @@ pub unsafe fn set_maximized_async( // `orderOut:` isn't thread-safe. Calling it from another thread actually works, // but with an odd delay. -pub unsafe fn order_out_sync(ns_window: id) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn order_out_sync(ns_window: &NSWindow) { + let ns_window = MainThreadSafe(ns_window.retain()); run_on_main(move || { - ns_window.orderOut_(nil); + ns_window.orderOut(None); }); } // `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread // actually works, but with an odd delay. -pub unsafe fn make_key_and_order_front_sync(ns_window: id) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn make_key_and_order_front_sync(ns_window: &NSWindow) { + let ns_window = MainThreadSafe(ns_window.retain()); run_on_main(move || { - ns_window.makeKeyAndOrderFront_(nil); + ns_window.makeKeyAndOrderFront(None); }); } // `setTitle:` isn't thread-safe. Calling it from another thread invalidates the // window drag regions, which throws an exception when not done in the main // thread -pub unsafe fn set_title_async(ns_window: id, title: String) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_title_async(ns_window: &NSWindow, title: String) { + let ns_window = MainThreadSafe(ns_window.retain()); Queue::main().exec_async(move || { - let title = IdRef::new(NSString::alloc(nil).init_str(&title)); - ns_window.setTitle_(*title); + let title = NSString::from_str(&title); + ns_window.setTitle(&title); }); } // `setFocus:` isn't thread-safe. -pub unsafe fn set_focus(ns_window: id) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_focus(ns_window: &NSWindow) { + let ns_window = MainThreadSafe(ns_window.retain()); run_on_main(move || { - ns_window.makeKeyAndOrderFront_(nil); + ns_window.makeKeyAndOrderFront(None); let app: id = msg_send![class!(NSApplication), sharedApplication]; let () = msg_send![app, activateIgnoringOtherApps: YES]; }); @@ -238,22 +239,19 @@ pub unsafe fn set_focus(ns_window: id) { // `close:` is thread-safe, but we want the event to be triggered from the main // thread. Though, it's a good idea to look into that more... -// -// ArturKovacs: It's important that this operation keeps the underlying window alive -// through the `IdRef` because otherwise it would dereference free'd memory -pub unsafe fn close_async(ns_window: IdRef) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn close_async(ns_window: &NSWindow) { + let ns_window = MainThreadSafe(ns_window.retain()); run_on_main(move || { - autoreleasepool(move || { + autoreleasepool(move |_| { ns_window.close(); }); }); } // `setIgnoresMouseEvents_:` isn't thread-safe, and fails silently. -pub unsafe fn set_ignore_mouse_events(ns_window: id, ignore: bool) { - let ns_window = MainThreadSafe(ns_window); +pub unsafe fn set_ignore_mouse_events(ns_window: &NSWindow, ignore: bool) { + let ns_window = MainThreadSafe(ns_window.retain()); Queue::main().exec_async(move || { - ns_window.setIgnoresMouseEvents_(if ignore { YES } else { NO }); + ns_window.setIgnoresMouseEvents(ignore); }); } diff --git a/src/platform_impl/macos/util/cursor.rs b/src/platform_impl/macos/util/cursor.rs index ecccecdc1..a21206840 100644 --- a/src/platform_impl/macos/util/cursor.rs +++ b/src/platform_impl/macos/util/cursor.rs @@ -2,13 +2,16 @@ // Copyright 2021-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 -use cocoa::{ - appkit::NSImage, - base::{id, nil}, - foundation::{NSDictionary, NSPoint, NSString}, +use crate::platform_impl::platform::ffi::{id, nil, NO}; +use objc2::{ + msg_send_id, + rc::Retained, + runtime::{AnyObject, Sel}, + ClassType, }; -use objc::runtime::{Sel, NO}; -use std::{cell::RefCell, ptr::null_mut}; +use objc2_app_kit::NSImage; +use objc2_foundation::{NSDictionary, NSPoint, NSString}; +use std::{cell::RefCell, ffi::c_void, ptr::null_mut}; use crate::window::CursorIcon; @@ -108,26 +111,32 @@ impl Cursor { // instead you'll just get them all in a column. pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id { static CURSOR_ROOT: &str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors"; - let cursor_root = NSString::alloc(nil).init_str(CURSOR_ROOT); - let cursor_name = NSString::alloc(nil).init_str(cursor_name); - let cursor_pdf = NSString::alloc(nil).init_str("cursor.pdf"); - let cursor_plist = NSString::alloc(nil).init_str("info.plist"); - let key_x = NSString::alloc(nil).init_str("hotx"); - let key_y = NSString::alloc(nil).init_str("hoty"); - - let cursor_path: id = msg_send![cursor_root, stringByAppendingPathComponent: cursor_name]; - let pdf_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf]; - let info_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist]; - - let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path); - let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path); - let x = info.valueForKey_(key_x); - let y = info.valueForKey_(key_y); - let point = NSPoint::new(msg_send![x, doubleValue], msg_send![y, doubleValue]); + let cursor_root = NSString::from_str(CURSOR_ROOT); + let cursor_name = NSString::from_str(cursor_name); + let cursor_pdf = NSString::from_str("cursor.pdf"); + let cursor_plist = NSString::from_str("info.plist"); + let key_x = NSString::from_str("hotx"); + let key_y = NSString::from_str("hoty"); + + let cursor_path: Retained = + msg_send_id![&cursor_root, stringByAppendingPathComponent: &*cursor_name]; + let pdf_path: Retained = + msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_pdf]; + let info_path: Retained = + msg_send_id![&cursor_path, stringByAppendingPathComponent: &*cursor_plist]; + + let image = NSImage::initByReferencingFile(NSImage::alloc(), &pdf_path).unwrap(); + #[allow(deprecated)] + let info = + NSDictionary::::dictionaryWithContentsOfFile(&info_path).unwrap(); + let x = info.objectForKey(&key_x).unwrap(); + let y = info.objectForKey(&key_y).unwrap(); + let point = NSPoint::new(msg_send![&x, doubleValue], msg_send![&y, doubleValue]); let cursor: id = msg_send![class!(NSCursor), alloc]; - msg_send![cursor, - initWithImage:image - hotSpot:point + msg_send![ + cursor, + initWithImage:&*image, + hotSpot:point, ] } @@ -150,10 +159,11 @@ pub unsafe fn invisible_cursor() -> id { CURSOR_OBJECT.with(|cursor_obj| { if *cursor_obj.borrow() == nil { // Create a cursor from `CURSOR_BYTES` - let cursor_data: id = msg_send![class!(NSData), - dataWithBytesNoCopy:CURSOR_BYTES as *const [u8] - length:CURSOR_BYTES.len() - freeWhenDone:NO + let cursor_data: id = msg_send![ + class!(NSData), + dataWithBytesNoCopy:CURSOR_BYTES.as_ptr().cast::(), + length:CURSOR_BYTES.len(), + freeWhenDone:NO, ]; let ns_image: id = msg_send![class!(NSImage), alloc]; diff --git a/src/platform_impl/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs index 1ffc2049f..b9a39147e 100644 --- a/src/platform_impl/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -7,26 +7,20 @@ mod cursor; pub use self::{cursor::*, r#async::*}; -use std::{ - ops::{BitAnd, Deref}, - slice, str, -}; +use std::ops::{BitAnd, Deref}; -use cocoa::{ - appkit::{NSApp, NSWindowStyleMask}, - base::{id, nil}, - foundation::{NSAutoreleasePool, NSPoint, NSRect, NSString, NSUInteger}, -}; use core_graphics::display::CGDisplay; -use objc::{ +use objc2::{ class, - runtime::{Class, Object, Sel, BOOL, YES}, + runtime::{AnyClass as Class, AnyObject as Object, Sel}, }; +use objc2_app_kit::{NSApp, NSView, NSWindow, NSWindowStyleMask}; +use objc2_foundation::{MainThreadMarker, NSAutoreleasePool, NSPoint, NSRange, NSRect, NSUInteger}; use crate::{ dpi::{LogicalPosition, PhysicalPosition}, error::ExternalError, - platform_impl::platform::ffi, + platform_impl::platform::ffi::{self, id, nil, BOOL, YES}, }; // Replace with `!` once stable @@ -40,7 +34,7 @@ where bitset & flag == flag } -pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange { +pub const EMPTY_RANGE: NSRange = NSRange { location: ffi::NSNotFound as NSUInteger, length: 0, }; @@ -60,23 +54,14 @@ impl IdRef { } IdRef(inner) } - - pub fn non_nil(self) -> Option { - if self.0 == nil { - None - } else { - Some(self) - } - } } impl Drop for IdRef { fn drop(&mut self) { if self.0 != nil { unsafe { - let pool = NSAutoreleasePool::new(nil); + let _pool = NSAutoreleasePool::new(); let () = msg_send![self.0, release]; - pool.drain(); }; } } @@ -120,23 +105,12 @@ pub fn cursor_position() -> Result, ExternalError> { Ok(point.to_physical(super::monitor::primary_monitor().scale_factor())) } -pub unsafe fn ns_string_id_ref(s: &str) -> IdRef { - IdRef::new(NSString::alloc(nil).init_str(s)) -} - -/// Copies the contents of the ns string into a `String` which gets returned. -pub unsafe fn ns_string_to_rust(ns_string: id) -> String { - let slice = slice::from_raw_parts(ns_string.UTF8String() as *mut u8, ns_string.len()); - let string = str::from_utf8_unchecked(slice); - string.to_owned() -} - pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class { let superclass: *const Class = msg_send![this, superclass]; &*superclass } -pub unsafe fn create_input_context(view: id) -> IdRef { +pub unsafe fn create_input_context(view: &NSView) -> IdRef { let input_context: id = msg_send![class!(NSTextInputContext), alloc]; let input_context: id = msg_send![input_context, initWithClient: view]; IdRef::new(input_context) @@ -144,23 +118,28 @@ pub unsafe fn create_input_context(view: id) -> IdRef { #[allow(dead_code)] pub unsafe fn open_emoji_picker() { - let () = msg_send![NSApp(), orderFrontCharacterPalette: nil]; + // SAFETY: TODO + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + let () = msg_send![&NSApp(mtm), orderFrontCharacterPalette: nil]; } pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL { YES } -pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) { - use cocoa::appkit::NSWindow; - +pub unsafe fn toggle_style_mask( + window: &NSWindow, + view: &NSView, + mask: NSWindowStyleMask, + on: bool, +) { let current_style_mask = window.styleMask(); if on { - window.setStyleMask_(current_style_mask | mask); + window.setStyleMask(current_style_mask | mask); } else { - window.setStyleMask_(current_style_mask & (!mask)); + window.setStyleMask(current_style_mask & (!mask)); } // If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly! - window.makeFirstResponder_(view); + window.makeFirstResponder(Some(view)); } diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 19188c87c..bbae15205 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1,23 +1,31 @@ // Copyright 2014-2021 The winit contributors // Copyright 2021-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 +#![allow(unused_unsafe)] +#![allow(deprecated)] // TODO: Use define_class! use std::{ boxed::Box, collections::{HashSet, VecDeque}, os::raw::*, - ptr, slice, str, + ptr, sync::{Arc, Mutex, Weak}, }; -use cocoa::{ - appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton}, - base::{id, nil}, - foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger}, +use objc2::{ + msg_send_id, + rc::Retained, + runtime::{ + AnyClass as Class, AnyObject as Object, AnyProtocol as Protocol, ClassBuilder as ClassDecl, Sel, + }, + ClassType, +}; +use objc2_app_kit::{ + NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow, NSWindowButton, }; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES}, +use objc2_foundation::{ + MainThreadMarker, NSAttributedString, NSInteger, NSMutableAttributedString, NSPoint, NSRange, + NSRect, NSSize, NSString, NSUInteger, }; use crate::{ @@ -30,7 +38,7 @@ use crate::{ app_state::AppState, event::{code_to_key, create_key_event, event_mods, get_scancode, EventWrapper}, ffi::*, - util::{self, IdRef}, + util::{self}, window::get_window_id, DEVICE_ID, }, @@ -52,7 +60,7 @@ impl Default for CursorState { } pub(super) struct ViewState { - ns_window: id, + ns_window: objc2::rc::Weak, pub cursor_state: Arc>, ime_spot: Option<(f64, f64)>, @@ -75,15 +83,15 @@ pub(super) struct ViewState { impl ViewState { fn get_scale_factor(&self) -> f64 { - (unsafe { NSWindow::backingScaleFactor(self.ns_window) }) as f64 + (unsafe { NSWindow::backingScaleFactor(&self.ns_window.load().unwrap()) }) as f64 } } -pub fn new_view(ns_window: id) -> (IdRef, Weak>) { +pub fn new_view(ns_window: &NSWindow) -> (Option>, Weak>) { let cursor_state = Default::default(); let cursor_access = Arc::downgrade(&cursor_state); let state = ViewState { - ns_window, + ns_window: objc2::rc::Weak::from(ns_window), cursor_state, ime_spot: None, in_ime_preedit: false, @@ -97,19 +105,18 @@ pub fn new_view(ns_window: id) -> (IdRef, Weak>) { unsafe { // This is free'd in `dealloc` let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void; - let ns_view: id = msg_send![VIEW_CLASS.0, alloc]; - ( - IdRef::new(msg_send![ns_view, initWithTao: state_ptr]), - cursor_access, - ) + let ns_view = msg_send_id![VIEW_CLASS.0, alloc]; + (msg_send_id![ns_view, initWithTao: state_ptr], cursor_access) } } -pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) { - let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("taoState"); +pub unsafe fn set_ime_position(ns_view: &NSView, input_context: id, x: f64, y: f64) { + let state_ptr: *mut c_void = *ns_view.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - let content_rect = - NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window)); + let content_rect = NSWindow::contentRectForFrameRect( + &state.ns_window.load().unwrap(), + NSWindow::frame(&state.ns_window.load().unwrap()), + ); let base_x = content_rect.origin.x as f64; let base_y = (content_rect.origin.y + content_rect.size.height) as f64; state.ime_spot = Some((base_x + x, base_y - y)); @@ -123,17 +130,7 @@ fn is_arrow_key(keycode: KeyCode) -> bool { ) } -/// `view` must be the reference to the `TaoView` class -/// -/// Returns the mutable reference to the `markedText` field. -unsafe fn clear_marked_text(view: &mut Object) -> &mut id { - let marked_text_ref: &mut id = view.get_mut_ivar("markedText"); - let () = msg_send![(*marked_text_ref), release]; - *marked_text_ref = NSMutableAttributedString::alloc(nil); - marked_text_ref -} - -struct ViewClass(*const Class); +struct ViewClass(&'static Class); unsafe impl Send for ViewClass {} unsafe impl Sync for ViewClass {} @@ -141,161 +138,121 @@ lazy_static! { static ref VIEW_CLASS: ViewClass = unsafe { let superclass = class!(NSView); let mut decl = ClassDecl::new("TaoView", superclass).unwrap(); - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _)); decl.add_method( sel!(initWithTao:), - init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id, + init_with_tao as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(viewDidMoveToWindow), - view_did_move_to_window as extern "C" fn(&Object, Sel), - ); - decl.add_method( - sel!(drawRect:), - draw_rect as extern "C" fn(&Object, Sel, NSRect), + view_did_move_to_window as extern "C" fn(_, _), ); + decl.add_method(sel!(drawRect:), draw_rect as extern "C" fn(_, _, _)); decl.add_method( sel!(acceptsFirstResponder), - accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(touchBar), - touch_bar as extern "C" fn(&Object, Sel) -> BOOL, + accepts_first_responder as extern "C" fn(_, _) -> _, ); + decl.add_method(sel!(touchBar), touch_bar as extern "C" fn(_, _) -> _); decl.add_method( sel!(resetCursorRects), - reset_cursor_rects as extern "C" fn(&Object, Sel), + reset_cursor_rects as extern "C" fn(_, _), ); decl.add_method( sel!(hasMarkedText), - has_marked_text as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(markedRange), - marked_range as extern "C" fn(&Object, Sel) -> NSRange, + has_marked_text as extern "C" fn(_, _) -> _, ); + decl.add_method(sel!(markedRange), marked_range as extern "C" fn(_, _) -> _); decl.add_method( sel!(selectedRange), - selected_range as extern "C" fn(&Object, Sel) -> NSRange, + selected_range as extern "C" fn(_, _) -> _, ); decl.add_method( sel!(setMarkedText:selectedRange:replacementRange:), - set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange), - ); - decl.add_method( - sel!(unmarkText), - unmark_text as extern "C" fn(&mut Object, Sel), + set_marked_text as extern "C" fn(_, _, _, _, _), ); + decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(_, _)); decl.add_method( sel!(validAttributesForMarkedText), - valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id, + valid_attributes_for_marked_text as extern "C" fn(_, _) -> _, ); decl.add_method( sel!(attributedSubstringForProposedRange:actualRange:), - attributed_substring_for_proposed_range - as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id, + attributed_substring_for_proposed_range as extern "C" fn(_, _, _, _) -> _, ); decl.add_method( sel!(insertText:replacementRange:), - insert_text as extern "C" fn(&Object, Sel, id, NSRange), + insert_text as extern "C" fn(_, _, _, _), ); decl.add_method( sel!(characterIndexForPoint:), - character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger, + character_index_for_point as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(firstRectForCharacterRange:actualRange:), - first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect, + first_rect_for_character_range as extern "C" fn(_, _, _, _) -> _, ); decl.add_method( sel!(doCommandBySelector:), - do_command_by_selector as extern "C" fn(&Object, Sel, Sel), - ); - decl.add_method( - sel!(keyDown:), - key_down as extern "C" fn(&mut Object, Sel, id), - ); - decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id)); - decl.add_method( - sel!(flagsChanged:), - flags_changed as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(insertTab:), - insert_tab as extern "C" fn(&Object, Sel, id), + do_command_by_selector as extern "C" fn(_, _, _), ); + decl.add_method(sel!(keyDown:), key_down as extern "C" fn(_, _, _)); + decl.add_method(sel!(keyUp:), key_up as extern "C" fn(_, _, _)); + decl.add_method(sel!(flagsChanged:), flags_changed as extern "C" fn(_, _, _)); + decl.add_method(sel!(insertTab:), insert_tab as extern "C" fn(_, _, _)); decl.add_method( sel!(insertBackTab:), - insert_back_tab as extern "C" fn(&Object, Sel, id), + insert_back_tab as extern "C" fn(_, _, _), ); - decl.add_method( - sel!(mouseDown:), - mouse_down as extern "C" fn(&Object, Sel, id), - ); - decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id)); + decl.add_method(sel!(mouseDown:), mouse_down as extern "C" fn(_, _, _)); + decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(_, _, _)); decl.add_method( sel!(rightMouseDown:), - right_mouse_down as extern "C" fn(&Object, Sel, id), + right_mouse_down as extern "C" fn(_, _, _), ); decl.add_method( sel!(rightMouseUp:), - right_mouse_up as extern "C" fn(&Object, Sel, id), + right_mouse_up as extern "C" fn(_, _, _), ); decl.add_method( sel!(otherMouseDown:), - other_mouse_down as extern "C" fn(&Object, Sel, id), + other_mouse_down as extern "C" fn(_, _, _), ); decl.add_method( sel!(otherMouseUp:), - other_mouse_up as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseMoved:), - mouse_moved as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseDragged:), - mouse_dragged as extern "C" fn(&Object, Sel, id), + other_mouse_up as extern "C" fn(_, _, _), ); + decl.add_method(sel!(mouseMoved:), mouse_moved as extern "C" fn(_, _, _)); + decl.add_method(sel!(mouseDragged:), mouse_dragged as extern "C" fn(_, _, _)); decl.add_method( sel!(rightMouseDragged:), - right_mouse_dragged as extern "C" fn(&Object, Sel, id), + right_mouse_dragged as extern "C" fn(_, _, _), ); decl.add_method( sel!(otherMouseDragged:), - other_mouse_dragged as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseEntered:), - mouse_entered as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(mouseExited:), - mouse_exited as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(scrollWheel:), - scroll_wheel as extern "C" fn(&Object, Sel, id), + other_mouse_dragged as extern "C" fn(_, _, _), ); + decl.add_method(sel!(mouseEntered:), mouse_entered as extern "C" fn(_, _, _)); + decl.add_method(sel!(mouseExited:), mouse_exited as extern "C" fn(_, _, _)); + decl.add_method(sel!(scrollWheel:), scroll_wheel as extern "C" fn(_, _, _)); decl.add_method( sel!(pressureChangeWithEvent:), - pressure_change_with_event as extern "C" fn(&Object, Sel, id), + pressure_change_with_event as extern "C" fn(_, _, _), ); decl.add_method( sel!(_wantsKeyDownForEvent:), - wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL, + wants_key_down_for_event as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(cancelOperation:), - cancel_operation as extern "C" fn(&Object, Sel, id), + cancel_operation as extern "C" fn(_, _, _), ); decl.add_method( sel!(frameDidChange:), - frame_did_change as extern "C" fn(&Object, Sel, id), + frame_did_change as extern "C" fn(_, _, _), ); decl.add_method( sel!(acceptsFirstMouse:), - accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, + accepts_first_mouse as extern "C" fn(_, _, _) -> _, ); decl.add_ivar::<*mut c_void>("taoState"); decl.add_ivar::("markedText"); @@ -308,7 +265,7 @@ lazy_static! { extern "C" fn dealloc(this: &Object, _sel: Sel) { unsafe { let state: *mut c_void = *this.get_ivar("taoState"); - let marked_text: id = *this.get_ivar("markedText"); + let marked_text: *mut NSMutableAttributedString = *this.get_ivar("markedText"); let _: () = msg_send![marked_text, release]; drop(Box::from_raw(state as *mut ViewState)); } @@ -318,19 +275,18 @@ extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id unsafe { let this: id = msg_send![this, init]; if this != nil { - (*this).set_ivar("taoState", state); - let marked_text = - ::init(NSMutableAttributedString::alloc(nil)); - (*this).set_ivar("markedText", marked_text); + *(*this).get_mut_ivar("taoState") = state; + let marked_text = Retained::into_raw(NSMutableAttributedString::new()); + *(*this).get_mut_ivar("markedText") = marked_text; let _: () = msg_send![this, setPostsFrameChangedNotifications: YES]; let notification_center: &Object = msg_send![class!(NSNotificationCenter), defaultCenter]; - let notification_name = NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification"); + let notification_name = NSString::from_str("NSViewFrameDidChangeNotification"); let _: () = msg_send![ notification_center, addObserver: this selector: sel!(frameDidChange:) - name: notification_name + name: &*notification_name object: this ]; } @@ -387,11 +343,11 @@ extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) { let state = &mut *(state_ptr as *mut ViewState); if let Some(position) = state.traffic_light_inset { - let window = state.ns_window; - inset_traffic_lights(window, position); + let window = state.ns_window.load().unwrap(); + inset_traffic_lights(&window, position); } - AppState::handle_redraw(WindowId(get_window_id(state.ns_window))); + AppState::handle_redraw(WindowId(get_window_id(&state.ns_window.load().unwrap()))); let superclass = util::superclass(this); let () = msg_send![super(this, superclass), drawRect: rect]; @@ -405,8 +361,8 @@ extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL { // This is necessary to prevent a beefy terminal error on MacBook Pros: // IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem // TODO: Add an API extension for using `NSTouchBar` -extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL { - NO +extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> id { + nil } extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) { @@ -434,16 +390,16 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) { extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL { unsafe { trace!("Triggered `hasMarkedText`"); - let marked_text: id = *this.get_ivar("markedText"); + let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText"); trace!("Completed `hasMarkedText`"); - (marked_text.length() > 0) as BOOL + (marked_text.length() > 0).into() } } extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange { unsafe { trace!("Triggered `markedRange`"); - let marked_text: id = *this.get_ivar("markedText"); + let marked_text: &NSMutableAttributedString = *this.get_ivar("markedText"); let length = marked_text.length(); trace!("Completed `markedRange`"); if length > 0 { @@ -472,13 +428,21 @@ extern "C" fn set_marked_text( ) { trace!("Triggered `setMarkedText`"); unsafe { - let marked_text_ref = clear_marked_text(this); - let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)]; - if has_attr != NO { - marked_text_ref.initWithAttributedString(string); + let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)]; + let marked_text = if has_attr { + NSMutableAttributedString::initWithAttributedString( + NSMutableAttributedString::alloc(), + &*(string as *const NSAttributedString), + ) } else { - marked_text_ref.initWithString(string); + NSMutableAttributedString::initWithString( + NSMutableAttributedString::alloc(), + &*(string as *const NSString), + ) }; + let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText"); + let () = msg_send![(*marked_text_ref), release]; + *marked_text_ref = Retained::into_raw(marked_text); let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); @@ -491,7 +455,9 @@ extern "C" fn set_marked_text( extern "C" fn unmark_text(this: &mut Object, _sel: Sel) { trace!("Triggered `unmarkText`"); unsafe { - clear_marked_text(this); + let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText"); + let () = msg_send![(*marked_text_ref), release]; + *marked_text_ref = Retained::into_raw(NSMutableAttributedString::new()); let input_context: id = msg_send![this, inputContext]; let _: () = msg_send![input_context, discardMarkedText]; } @@ -532,8 +498,10 @@ extern "C" fn first_rect_for_character_range( let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); let (x, y) = state.ime_spot.unwrap_or_else(|| { - let content_rect = - NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window)); + let content_rect = NSWindow::contentRectForFrameRect( + &state.ns_window.load().unwrap(), + NSWindow::frame(&state.ns_window.load().unwrap()), + ); let x = content_rect.origin.x; let y = util::bottom_left_to_top_left(content_rect); (x, y) @@ -543,14 +511,19 @@ extern "C" fn first_rect_for_character_range( } } -extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) { +extern "C" fn insert_text( + this: &Object, + _sel: Sel, + string: &NSString, + _replacement_range: NSRange, +) { trace!("Triggered `insertText`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - let has_attr: BOOL = msg_send![string, isKindOfClass: class!(NSAttributedString)]; - let characters = if has_attr != NO { + let has_attr: bool = msg_send![string, isKindOfClass: class!(NSAttributedString)]; + let characters = if has_attr { // This is a *mut NSAttributedString msg_send![string, string] } else { @@ -558,8 +531,8 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran string }; - let slice = slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len()); - let string: String = str::from_utf8_unchecked(slice) + let string: String = characters + .to_string() .chars() .filter(|c| !is_corporate_character(*c)) .collect(); @@ -569,7 +542,7 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran //let event: id = msg_send![NSApp(), currentEvent]; AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::ReceivedImeText(string), })); if state.in_ime_preedit { @@ -598,7 +571,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) { // // 1) as a reminder for how `doCommandBySelector` works // // 2) to make our use of carriage return explicit // events.push_back(EventWrapper::StaticEvent(Event::WindowEvent { - // window_id: WindowId(get_window_id(state.ns_window)), + // window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), // event: WindowEvent::ReceivedCharacter('\r'), // })); // } else { @@ -609,7 +582,7 @@ extern "C" fn do_command_by_selector(_this: &Object, _sel: Sel, _command: Sel) { // .filter(|c| !is_corporate_character(*c)) // { // events.push_back(EventWrapper::StaticEvent(Event::WindowEvent { - // window_id: WindowId(get_window_id(state.ns_window)), + // window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), // event: WindowEvent::ReceivedCharacter(character), // })); // } @@ -679,45 +652,46 @@ fn is_corporate_character(c: char) -> bool { // } // Update `state.modifiers` if `event` has something different -fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) { +fn update_potentially_stale_modifiers(state: &mut ViewState, event: &NSEvent) { let event_modifiers = event_mods(event); if state.modifiers != event_modifiers { state.modifiers = event_modifiers; AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::ModifiersChanged(state.modifiers), })); } } -extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) { +extern "C" fn key_down(this: &mut Object, _sel: Sel, event: &NSEvent) { trace!("Triggered `keyDown`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - let window_id = WindowId(get_window_id(state.ns_window)); + let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap())); // keyboard refactor: don't seems to be needed anymore // let characters = get_characters(event, false); //state.raw_characters = Some(characters); - let is_repeat: BOOL = msg_send![event, isARepeat]; - let is_repeat = is_repeat == YES; + let is_repeat: bool = msg_send![event, isARepeat]; update_potentially_stale_modifiers(state, event); let pass_along = !is_repeat || !state.is_key_down; if pass_along { // See below for why we do this. - clear_marked_text(this); + let marked_text_ref: &mut *mut NSMutableAttributedString = this.get_mut_ivar("markedText"); + let () = msg_send![(*marked_text_ref), release]; + *marked_text_ref = Retained::into_raw(NSMutableAttributedString::new()); state.key_triggered_ime = false; // Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do... // So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some // keys to generate twice as many characters. let array: id = msg_send![class!(NSArray), arrayWithObject: event]; - let () = msg_send![this, interpretKeyEvents: array]; + let () = msg_send![&*this, interpretKeyEvents: array]; } // The `interpretKeyEvents` above, may invoke `set_marked_text` or `insert_text`, // if the event corresponds to an IME event. @@ -751,7 +725,7 @@ extern "C" fn key_down(this: &mut Object, _sel: Sel, event: id) { trace!("Completed `keyDown`"); } -extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { +extern "C" fn key_up(this: &Object, _sel: Sel, event: &NSEvent) { trace!("Triggered `keyUp`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); @@ -762,7 +736,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { update_potentially_stale_modifiers(state, event); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, event: create_key_event(event, false, false, false, None), @@ -774,7 +748,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { trace!("Completed `keyUp`"); } -extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) { +extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: &NSEvent) { use KeyCode::{ AltLeft, AltRight, ControlLeft, ControlRight, ShiftLeft, ShiftRight, SuperLeft, SuperRight, }; @@ -844,46 +818,46 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, ns_event: id) { } process_event!( ModifiersState::SHIFT, - NSEventModifierFlags::NSShiftKeyMask, + NSEventModifierFlags::NSEventModifierFlagShift, ShiftLeft ); process_event!( ModifiersState::SHIFT, - NSEventModifierFlags::NSShiftKeyMask, + NSEventModifierFlags::NSEventModifierFlagShift, ShiftRight ); process_event!( ModifiersState::CONTROL, - NSEventModifierFlags::NSControlKeyMask, + NSEventModifierFlags::NSEventModifierFlagControl, ControlLeft ); process_event!( ModifiersState::CONTROL, - NSEventModifierFlags::NSControlKeyMask, + NSEventModifierFlags::NSEventModifierFlagControl, ControlRight ); process_event!( ModifiersState::ALT, - NSEventModifierFlags::NSAlternateKeyMask, + NSEventModifierFlags::NSEventModifierFlagOption, AltLeft ); process_event!( ModifiersState::ALT, - NSEventModifierFlags::NSAlternateKeyMask, + NSEventModifierFlags::NSEventModifierFlagOption, AltRight ); process_event!( ModifiersState::SUPER, - NSEventModifierFlags::NSCommandKeyMask, + NSEventModifierFlags::NSEventModifierFlagCommand, SuperLeft ); process_event!( ModifiersState::SUPER, - NSEventModifierFlags::NSCommandKeyMask, + NSEventModifierFlags::NSEventModifierFlagCommand, SuperRight ); - let window_id = WindowId(get_window_id(state.ns_window)); + let window_id = WindowId(get_window_id(&state.ns_window.load().unwrap())); for event in events { AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { @@ -927,21 +901,22 @@ extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { trace!("Triggered `cancelOperation`"); unsafe { + let mtm = MainThreadMarker::new_unchecked(); let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - let event: id = msg_send![NSApp(), currentEvent]; - update_potentially_stale_modifiers(state, event); + let event: Retained = msg_send_id![&NSApp(mtm), currentEvent]; + update_potentially_stale_modifiers(state, &event); let scancode = 0x2f; let key = KeyCode::from_scancode(scancode); debug_assert_eq!(key, KeyCode::Period); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::KeyboardInput { device_id: DEVICE_ID, - event: create_key_event(event, true, false, false, Some(key)), + event: create_key_event(&event, true, false, false, Some(key)), is_synthetic: false, }, }; @@ -950,7 +925,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { trace!("Completed `cancelOperation`"); } -fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) { +fn mouse_click(this: &Object, event: &NSEvent, button: MouseButton, button_state: ElementState) { unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); @@ -958,7 +933,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem update_potentially_stale_modifiers(state, event); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::MouseInput { device_id: DEVICE_ID, state: button_state, @@ -971,47 +946,44 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem } } -extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) { +extern "C" fn mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Left, ElementState::Pressed); } -extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) { +extern "C" fn mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Left, ElementState::Released); } -extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) { +extern "C" fn right_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Right, ElementState::Pressed); } -extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) { +extern "C" fn right_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Right, ElementState::Released); } -extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) { +extern "C" fn other_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Middle, ElementState::Pressed); } -extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) { +extern "C" fn other_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); mouse_click(this, event, MouseButton::Middle, ElementState::Released); } -fn mouse_motion(this: &Object, event: id) { +fn mouse_motion(this: &NSView, event: &NSEvent) { unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); - // We have to do this to have access to the `NSView` trait... - let view: id = this as *const _ as *mut _; - let window_point = event.locationInWindow(); - let view_point = view.convertPoint_fromView_(window_point, nil); - let view_rect = NSView::frame(view); + let view_point = this.convertPoint_fromView(window_point, None); + let view_rect = NSView::frame(this); if view_point.x.is_sign_negative() || view_point.y.is_sign_negative() @@ -1032,7 +1004,7 @@ fn mouse_motion(this: &Object, event: id) { update_potentially_stale_modifiers(state, event); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::CursorMoved { device_id: DEVICE_ID, position: logical_position.to_physical(state.get_scale_factor()), @@ -1044,19 +1016,19 @@ fn mouse_motion(this: &Object, event: id) { } } -extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { +extern "C" fn mouse_moved(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); } -extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) { +extern "C" fn mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); } -extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) { +extern "C" fn right_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); } -extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { +extern "C" fn other_mouse_dragged(this: &NSView, _sel: Sel, event: &NSEvent) { mouse_motion(this, event); } @@ -1067,7 +1039,7 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) { let state = &mut *(state_ptr as *mut ViewState); let enter_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::CursorEntered { device_id: DEVICE_ID, }, @@ -1085,7 +1057,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { let state = &mut *(state_ptr as *mut ViewState); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::CursorLeft { device_id: DEVICE_ID, }, @@ -1096,7 +1068,7 @@ extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) { trace!("Completed `mouseExited`"); } -extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { +extern "C" fn scroll_wheel(this: &NSView, _sel: Sel, event: &NSEvent) { trace!("Triggered `scrollWheel`"); mouse_motion(this, event); @@ -1108,7 +1080,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { let delta = { // macOS horizontal sign convention is the inverse of tao. let (x, y) = (event.scrollingDeltaX() * -1.0, event.scrollingDeltaY()); - if event.hasPreciseScrollingDeltas() == YES { + if event.hasPreciseScrollingDeltas() { let delta = LogicalPosition::new(x, y).to_physical(state.get_scale_factor()); MouseScrollDelta::PixelDelta(delta) } else { @@ -1116,8 +1088,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { } }; let phase = match event.phase() { - NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, - NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended, + NSEventPhase::MayBegin | NSEventPhase::Began => TouchPhase::Started, + NSEventPhase::Ended => TouchPhase::Ended, _ => TouchPhase::Moved, }; @@ -1132,7 +1104,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { update_potentially_stale_modifiers(state, event); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta, @@ -1147,7 +1119,7 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { trace!("Completed `scrollWheel`"); } -extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { +extern "C" fn pressure_change_with_event(this: &NSView, _sel: Sel, event: &NSEvent) { trace!("Triggered `pressureChangeWithEvent`"); mouse_motion(this, event); @@ -1160,11 +1132,11 @@ extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) { let stage = event.stage(); let window_event = Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id: WindowId(get_window_id(&state.ns_window.load().unwrap())), event: WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure, - stage, + stage: stage as i64, }, }; @@ -1184,27 +1156,33 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL YES } -pub unsafe fn inset_traffic_lights(window: W, position: LogicalPosition) { +pub unsafe fn inset_traffic_lights(window: &NSWindow, position: LogicalPosition) { let (x, y) = (position.x, position.y); - let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton); - let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton); - let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton); + let close = window + .standardWindowButton(NSWindowButton::NSWindowCloseButton) + .unwrap(); + let miniaturize = window + .standardWindowButton(NSWindowButton::NSWindowMiniaturizeButton) + .unwrap(); + let zoom = window + .standardWindowButton(NSWindowButton::NSWindowZoomButton) + .unwrap(); - let title_bar_container_view = close.superview().superview(); + let title_bar_container_view = close.superview().unwrap().superview().unwrap(); - let close_rect = NSView::frame(close); + let close_rect = NSView::frame(&close); let title_bar_frame_height = close_rect.size.height + y; - let mut title_bar_rect = NSView::frame(title_bar_container_view); + let mut title_bar_rect = NSView::frame(&title_bar_container_view); title_bar_rect.size.height = title_bar_frame_height; title_bar_rect.origin.y = window.frame().size.height - title_bar_frame_height; - let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect]; + let _: () = msg_send![&title_bar_container_view, setFrame: title_bar_rect]; - let window_buttons = vec![close, miniaturize, zoom]; - let space_between = NSView::frame(miniaturize).origin.x - close_rect.origin.x; + let window_buttons = vec![close, miniaturize.clone(), zoom]; + let space_between = NSView::frame(&miniaturize).origin.x - close_rect.origin.x; for (i, button) in window_buttons.into_iter().enumerate() { - let mut rect = NSView::frame(button); + let mut rect = NSView::frame(&button); rect.origin.x = x + (i as f64 * space_between); button.setFrameOrigin(rect.origin); } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 62c14b665..d3503109a 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -1,6 +1,7 @@ // Copyright 2014-2021 The winit contributors // Copyright 2021-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 +#![allow(unused_unsafe)] use std::{ collections::VecDeque, @@ -38,26 +39,31 @@ use crate::{ WindowAttributes, WindowId as RootWindowId, WindowSizeConstraints, }, }; -use cocoa::{ - appkit::{ - self, CGFloat, NSApp, NSApplication, NSApplicationPresentationOptions, NSColor, NSEvent, - NSEventModifierFlags, NSEventSubtype, NSEventType, NSRequestUserAttentionType, NSScreen, - NSView, NSWindow, NSWindowButton, NSWindowCollectionBehavior, NSWindowOrderingMode, - NSWindowStyleMask, - }, - base::{id, nil}, - foundation::{ - NSArray, NSAutoreleasePool, NSDictionary, NSInteger, NSPoint, NSRect, NSSize, NSString, - NSTimeInterval, NSUInteger, - }, +use core_graphics::{ + base::CGFloat, + display::{CGDisplay, CGDisplayMode}, + geometry::CGPoint, +}; +use objc2::{ + msg_send_id, + rc::Retained, + runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}, +}; +use objc2_app_kit::{ + self as appkit, NSApp, NSApplicationPresentationOptions, NSBackingStoreType, NSColor, NSEvent, + NSEventModifierFlags, NSEventSubtype, NSEventType, NSRequestUserAttentionType, NSScreen, NSView, + NSWindow, NSWindowButton, NSWindowCollectionBehavior, NSWindowFullScreenButton, + NSWindowOrderingMode, NSWindowStyleMask, }; -use core_graphics::display::{CGDisplay, CGDisplayMode}; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel, BOOL, NO, YES}, +use objc2_foundation::{ + MainThreadMarker, NSArray, NSAutoreleasePool, NSDictionary, NSInteger, NSPoint, NSRect, NSSize, + NSString, NSTimeInterval, NSUInteger, }; -use super::{util::ns_string_to_rust, view::ViewState}; +use super::{ + ffi::{id, nil, NO}, + view::ViewState, +}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id(pub usize); @@ -68,10 +74,10 @@ impl Id { } } -// Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier +// Convert the `NSWindow` associated with a window to a usize to use as a unique identifier // for the window. -pub fn get_window_id(window_cocoa_id: id) -> Id { - Id(window_cocoa_id as *const Object as _) +pub fn get_window_id(window_cocoa_id: &NSWindow) -> Id { + Id(window_cocoa_id as *const NSWindow as _) } #[non_exhaustive] @@ -120,15 +126,17 @@ impl Default for PlatformSpecificWindowBuilderAttributes { } unsafe fn create_view( - ns_window: id, + ns_window: &NSWindow, pl_attribs: &PlatformSpecificWindowBuilderAttributes, -) -> Option<(IdRef, Weak>)> { +) -> Option<(Retained, Weak>)> { let (ns_view, cursor_state) = new_view(ns_window); - ns_view.non_nil().map(|ns_view| { + ns_view.map(|ns_view| { if !pl_attribs.disallow_hidpi { - ns_view.setWantsBestResolutionOpenGLSurface_(YES); + #[allow(deprecated)] + ns_view.setWantsBestResolutionOpenGLSurface(true); } + #[allow(deprecated)] // TODO: Use define_class! if let Some(position) = pl_attribs.traffic_light_inset { let state_ptr: *mut c_void = *(**ns_view).get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); @@ -143,7 +151,7 @@ unsafe fn create_view( // explicitly make the view layer-backed up front so that AppKit doesn't do it // itself and break the association with its context. if f64::floor(appkit::NSAppKitVersionNumber) > appkit::NSAppKitVersionNumber10_12 { - ns_view.setWantsLayer(YES); + ns_view.setWantsLayer(true); } (ns_view, cursor_state) @@ -153,25 +161,26 @@ unsafe fn create_view( fn create_window( attrs: &WindowAttributes, pl_attrs: &PlatformSpecificWindowBuilderAttributes, -) -> Option { +) -> Option> { unsafe { - let pool = NSAutoreleasePool::new(nil); + let mtm = MainThreadMarker::new_unchecked(); + let _pool = NSAutoreleasePool::new(); let screen = match attrs.fullscreen { Some(Fullscreen::Borderless(Some(RootMonitorHandle { inner: ref monitor }))) | Some(Fullscreen::Exclusive(RootVideoMode { video_mode: VideoMode { ref monitor, .. }, })) => { let monitor_screen = monitor.ns_screen(); - Some(monitor_screen.unwrap_or_else(|| appkit::NSScreen::mainScreen(nil))) + Some(monitor_screen.unwrap_or_else(|| appkit::NSScreen::mainScreen(mtm).unwrap())) } - Some(Fullscreen::Borderless(None)) => Some(appkit::NSScreen::mainScreen(nil)), + Some(Fullscreen::Borderless(None)) => Some(appkit::NSScreen::mainScreen(mtm).unwrap()), None => None, }; - let frame = match screen { + let frame = match &screen { Some(screen) => NSScreen::frame(screen), None => { - let screen = NSScreen::mainScreen(nil); - let scale_factor = NSScreen::backingScaleFactor(screen) as f64; + let screen = NSScreen::mainScreen(mtm).unwrap(); + let scale_factor = NSScreen::backingScaleFactor(&screen) as f64; let desired_size = attrs .inner_size .unwrap_or_else(|| PhysicalSize::new(800, 600).into()); @@ -197,112 +206,117 @@ fn create_window( let mut masks = if !attrs.decorations && screen.is_none() || pl_attrs.titlebar_hidden { // Resizable UnownedWindow without a titlebar or borders // if decorations is set to false, ignore pl_attrs - NSWindowStyleMask::NSBorderlessWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask + NSWindowStyleMask::Borderless + | NSWindowStyleMask::Resizable + | NSWindowStyleMask::Miniaturizable } else { // default case, resizable window with titlebar and titlebar buttons - NSWindowStyleMask::NSClosableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSTitledWindowMask + NSWindowStyleMask::Closable + | NSWindowStyleMask::Miniaturizable + | NSWindowStyleMask::Resizable + | NSWindowStyleMask::Titled }; if !attrs.resizable { - masks &= !NSWindowStyleMask::NSResizableWindowMask; + masks &= !NSWindowStyleMask::Resizable; } if !attrs.minimizable { - masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + masks &= !NSWindowStyleMask::Miniaturizable; } if !attrs.closable { - masks &= !NSWindowStyleMask::NSClosableWindowMask; + masks &= !NSWindowStyleMask::Closable; } if pl_attrs.fullsize_content_view { - masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask; + masks |= NSWindowStyleMask::FullSizeContentView; } - let ns_window: id = msg_send![WINDOW_CLASS.0, alloc]; - let ns_window = IdRef::new(ns_window.initWithContentRect_styleMask_backing_defer_( - frame, - masks, - appkit::NSBackingStoreBuffered, - NO, - )); - let res = ns_window.non_nil().map(|ns_window| { - let title = util::ns_string_id_ref(&attrs.title); - ns_window.setReleasedWhenClosed_(NO); - ns_window.setTitle_(*title); - ns_window.setAcceptsMouseMovedEvents_(YES); + let ns_window = msg_send_id![WINDOW_CLASS.0, alloc]; + let ns_window: Option> = msg_send_id![ + ns_window, + initWithContentRect: frame, + styleMask: masks, + backing: NSBackingStoreType::NSBackingStoreBuffered, + defer: NO, + ]; + let res = ns_window.map(|ns_window| { + let title = NSString::from_str(&attrs.title); + ns_window.setReleasedWhenClosed(false); + ns_window.setTitle(&title); + ns_window.setAcceptsMouseMovedEvents(true); if pl_attrs.titlebar_transparent { - ns_window.setTitlebarAppearsTransparent_(YES); + ns_window.setTitlebarAppearsTransparent(true); } if pl_attrs.title_hidden { - ns_window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden); + ns_window.setTitleVisibility(appkit::NSWindowTitleVisibility::NSWindowTitleHidden); } if pl_attrs.titlebar_buttons_hidden { for titlebar_button in &[ - NSWindowButton::NSWindowFullScreenButton, + NSWindowFullScreenButton, NSWindowButton::NSWindowMiniaturizeButton, NSWindowButton::NSWindowCloseButton, NSWindowButton::NSWindowZoomButton, ] { - let button = ns_window.standardWindowButton_(*titlebar_button); - let _: () = msg_send![button, setHidden: YES]; + let button = ns_window.standardWindowButton(*titlebar_button).unwrap(); + let _: () = msg_send![&button, setHidden: true]; } } if pl_attrs.movable_by_window_background { - ns_window.setMovableByWindowBackground_(YES); + ns_window.setMovableByWindowBackground(true); } if attrs.always_on_top { let _: () = msg_send![ - *ns_window, + &ns_window, setLevel: ffi::NSWindowLevel::NSFloatingWindowLevel ]; } if attrs.always_on_bottom { let _: () = msg_send![ - *ns_window, + &ns_window, setLevel: ffi::NSWindowLevel::BelowNormalWindowLevel ]; } if attrs.content_protection { - let _: () = msg_send![*ns_window, setSharingType: 0]; + let _: () = msg_send![&ns_window, setSharingType: 0]; } if !attrs.maximizable { - let button = ns_window.standardWindowButton_(NSWindowButton::NSWindowZoomButton); - let _: () = msg_send![button, setEnabled: NO]; + let button = ns_window + .standardWindowButton(NSWindowButton::NSWindowZoomButton) + .unwrap(); + let _: () = msg_send![&button, setEnabled: NO]; } if let Some(increments) = pl_attrs.resize_increments { let (x, y) = (increments.width, increments.height); if x >= 1.0 && y >= 1.0 { let size = NSSize::new(x as CGFloat, y as CGFloat); - ns_window.setResizeIncrements_(size); + ns_window.setResizeIncrements(size); } } if let Parent::ChildOf(parent) = pl_attrs.parent { - let _: () = msg_send![parent as id, addChildWindow: *ns_window ordered: NSWindowOrderingMode::NSWindowAbove]; + let _: () = + msg_send![parent as id, addChildWindow: &*ns_window ordered: NSWindowOrderingMode::NSWindowAbove]; } if !pl_attrs.automatic_tabbing { - NSWindow::setAllowsAutomaticWindowTabbing_(*ns_window, NO); + NSWindow::setAllowsAutomaticWindowTabbing(false, mtm); } if let Some(tabbing_identifier) = &pl_attrs.tabbing_identifier { - let _: () = msg_send![*ns_window, setTabbingIdentifier: NSString::alloc(nil).init_str(tabbing_identifier)]; + let _: () = + msg_send![&ns_window, setTabbingIdentifier: &*NSString::from_str(tabbing_identifier)]; } if !pl_attrs.has_shadow { - ns_window.setHasShadow_(NO); + ns_window.setHasShadow(false); } if attrs.position.is_none() { ns_window.center(); @@ -310,29 +324,28 @@ fn create_window( ns_window }); - pool.drain(); res } } pub(super) fn get_ns_theme() -> Theme { unsafe { - let mut appearances: Vec = Vec::new(); - appearances.push(NSString::alloc(nil).init_str("NSAppearanceNameAqua")); - appearances.push(NSString::alloc(nil).init_str("NSAppearanceNameDarkAqua")); + let mut appearances: Vec> = Vec::new(); + appearances.push(NSString::from_str("NSAppearanceNameAqua")); + appearances.push(NSString::from_str("NSAppearanceNameDarkAqua")); let app_class = class!(NSApplication); let app: id = msg_send![app_class, sharedApplication]; - let has_theme: BOOL = msg_send![app, respondsToSelector: sel!(effectiveAppearance)]; - if has_theme == NO { + let has_theme: bool = msg_send![app, respondsToSelector: sel!(effectiveAppearance)]; + if has_theme { return Theme::Light; } let appearance: id = msg_send![app, effectiveAppearance]; - let name: id = msg_send![ + let name: Retained = msg_send_id![ appearance, - bestMatchFromAppearancesWithNames: NSArray::arrayWithObjects(nil, &appearances) + bestMatchFromAppearancesWithNames: &*NSArray::from_id_slice(&appearances) ]; - let name = ns_string_to_rust(name); - match &name[..] { + let name = name.to_string(); + match &*name { "NSAppearanceNameDarkAqua" => Theme::Dark, _ => Theme::Light, } @@ -343,14 +356,14 @@ pub(super) fn set_ns_theme(theme: Option) { unsafe { let app_class = class!(NSApplication); let app: id = msg_send![app_class, sharedApplication]; - let has_theme: BOOL = msg_send![app, respondsToSelector: sel!(effectiveAppearance)]; - if has_theme == YES { + let has_theme: bool = msg_send![app, respondsToSelector: sel!(effectiveAppearance)]; + if has_theme { let appearance = if let Some(theme) = theme { - let name = NSString::alloc(nil).init_str(match theme { + let name = NSString::from_str(match theme { Theme::Dark => "NSAppearanceNameDarkAqua", Theme::Light => "NSAppearanceNameAqua", }); - msg_send![class!(NSAppearance), appearanceNamed: name] + msg_send![class!(NSAppearance), appearanceNamed: &*name] } else { nil }; @@ -359,7 +372,7 @@ pub(super) fn set_ns_theme(theme: Option) { } } -struct WindowClass(*const Class); +struct WindowClass(&'static Class); unsafe impl Send for WindowClass {} unsafe impl Sync for WindowClass {} @@ -369,29 +382,26 @@ lazy_static! { let mut decl = ClassDecl::new("TaoWindow", window_superclass).unwrap(); decl.add_method( sel!(canBecomeMainWindow), - util::yes as extern "C" fn(&Object, Sel) -> BOOL, + util::yes as extern "C" fn(_, _) -> _, ); decl.add_method( sel!(canBecomeKeyWindow), - util::yes as extern "C" fn(&Object, Sel) -> BOOL, - ); - decl.add_method( - sel!(sendEvent:), - send_event as extern "C" fn(&Object, Sel, id), + util::yes as extern "C" fn(_, _) -> _, ); + decl.add_method(sel!(sendEvent:), send_event as extern "C" fn(_, _, _)); WindowClass(decl.register()) }; } -extern "C" fn send_event(this: &Object, _sel: Sel, event: id) { +extern "C" fn send_event(this: &Object, _sel: Sel, event: &NSEvent) { unsafe { - let event_type = event.eventType(); + let event_type = event.r#type(); match event_type { - appkit::NSLeftMouseDown => { + NSEventType::LeftMouseDown => { // When wkwebview is set on NSWindow, `WindowBuilder::with_movable_by_window_background` is not working. // Because of this, we need to invoke `[NSWindow performWindowDragWithEvent]` in NSLeftMouseDown event. - let is_movable_window: BOOL = msg_send![this, isMovableByWindowBackground]; - if is_movable_window == YES { + let is_movable_window: bool = msg_send![this, isMovableByWindowBackground]; + if is_movable_window { let _: () = msg_send![this, performWindowDragWithEvent: event]; } } @@ -453,9 +463,9 @@ impl From for SharedState { } pub struct UnownedWindow { - pub ns_window: IdRef, // never changes - pub ns_view: IdRef, // never changes - input_context: IdRef, // never changes + pub ns_window: Retained, // never changes + pub ns_view: Retained, // never changes + input_context: IdRef, // never changes pub shared_state: Arc>, decorations: AtomicBool, cursor_state: Weak>, @@ -476,66 +486,55 @@ impl UnownedWindow { } trace!("Creating new window"); - let pool = unsafe { NSAutoreleasePool::new(nil) }; - let ns_window = create_window(&win_attribs, &pl_attribs).ok_or_else(|| { - unsafe { pool.drain() }; - os_error!(OsError::CreationError("Couldn't create `NSWindow`")) - })?; + let _pool = unsafe { NSAutoreleasePool::new() }; + let ns_window = create_window(&win_attribs, &pl_attribs) + .ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?; - let (ns_view, cursor_state) = - unsafe { create_view(*ns_window, &pl_attribs) }.ok_or_else(|| { - unsafe { pool.drain() }; - os_error!(OsError::CreationError("Couldn't create `NSView`")) - })?; + let (ns_view, cursor_state) = unsafe { create_view(&ns_window, &pl_attribs) } + .ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSView`")))?; // Configure the new view as the "key view" for the window unsafe { - ns_window.setContentView_(*ns_view); - ns_window.setInitialFirstResponder_(*ns_view); + ns_window.setContentView(Some(&ns_view)); + ns_window.setInitialFirstResponder(Some(&ns_view)); } - let input_context = unsafe { util::create_input_context(*ns_view) }; + let input_context = unsafe { util::create_input_context(&ns_view) }; - let scale_factor = unsafe { NSWindow::backingScaleFactor(*ns_window) as f64 }; + let scale_factor = unsafe { NSWindow::backingScaleFactor(&ns_window) as f64 }; unsafe { if win_attribs.transparent { - ns_window.setOpaque_(NO); + ns_window.setOpaque(false); } if win_attribs.transparent || win_attribs.background_color.is_some() { let color = win_attribs .background_color .map(|(r, g, b, a)| { - NSColor::colorWithRed_green_blue_alpha_( - nil, - r as f64, - g as f64, - b as f64, - a as f64 / 255.0, - ) + NSColor::colorWithRed_green_blue_alpha(r as f64, g as f64, b as f64, a as f64 / 255.0) }) - .unwrap_or_else(|| NSColor::clearColor(nil)); - ns_window.setBackgroundColor_(color); + .unwrap_or_else(|| NSColor::clearColor()); + ns_window.setBackgroundColor(Some(&color)); } if win_attribs.inner_size_constraints.has_min() { let min_size = win_attribs .inner_size_constraints .min_size_logical(scale_factor); - set_min_inner_size(*ns_window, min_size); + set_min_inner_size(&ns_window, min_size); } if win_attribs.inner_size_constraints.has_max() { let max_size = win_attribs .inner_size_constraints .max_size_logical(scale_factor); - set_max_inner_size(*ns_window, max_size); + set_max_inner_size(&ns_window, max_size); } // register for drag and drop operations. let () = msg_send![ - *ns_window, - registerForDraggedTypes: NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType) + &ns_window, + registerForDraggedTypes: &*NSArray::arrayWithObject(appkit::NSFilenamesPboardType) ]; } @@ -592,9 +591,9 @@ impl UnownedWindow { if visible { if focused { // Tightly linked with `app_state::window_activation_hack` - unsafe { window.ns_window.makeKeyAndOrderFront_(nil) }; + unsafe { window.ns_window.makeKeyAndOrderFront(None) }; } else { - unsafe { window.ns_window.orderFront_(nil) }; + unsafe { window.ns_window.orderFront(None) }; } } @@ -602,40 +601,35 @@ impl UnownedWindow { window.set_maximized(maximized); } - unsafe { pool.drain() }; - Ok((window, delegate)) } fn set_style_mask_async(&self, mask: NSWindowStyleMask) { - unsafe { util::set_style_mask_async(*self.ns_window, *self.ns_view, mask) }; + unsafe { util::set_style_mask_async(&self.ns_window, &self.ns_view, mask) }; } fn set_style_mask_sync(&self, mask: NSWindowStyleMask) { - unsafe { util::set_style_mask_sync(*self.ns_window, *self.ns_view, mask) }; + unsafe { util::set_style_mask_sync(&self.ns_window, &self.ns_view, mask) }; } pub fn id(&self) -> Id { - get_window_id(*self.ns_window) + get_window_id(&self.ns_window) } pub fn set_title(&self, title: &str) { unsafe { - util::set_title_async(*self.ns_window, title.to_string()); + util::set_title_async(&self.ns_window, title.to_string()); } } pub fn title(&self) -> String { - unsafe { - let title = self.ns_window.title(); - ns_string_to_rust(title) - } + self.ns_window.title().to_string() } pub fn set_visible(&self, visible: bool) { match visible { - true => unsafe { util::make_key_and_order_front_sync(*self.ns_window) }, - false => unsafe { util::order_out_sync(*self.ns_window) }, + true => unsafe { util::make_key_and_order_front_sync(&self.ns_window) }, + false => unsafe { util::order_out_sync(&self.ns_window) }, } } @@ -643,10 +637,10 @@ impl UnownedWindow { // Shortener for set_visible(true) pub fn set_focus(&self) { unsafe { - let is_minimized: BOOL = msg_send![*self.ns_window, isMiniaturized]; - let is_visible: BOOL = msg_send![*self.ns_window, isVisible]; - if is_minimized == NO && is_visible == YES { - util::set_focus(*self.ns_window); + let is_minimized: bool = msg_send![&self.ns_window, isMiniaturized]; + let is_visible: bool = msg_send![&self.ns_window, isVisible]; + if is_minimized && is_visible { + util::set_focus(&self.ns_window); } } } @@ -654,8 +648,8 @@ impl UnownedWindow { #[inline] pub fn is_focused(&self) -> bool { unsafe { - let is_key_window: BOOL = msg_send![*self.ns_window, isKeyWindow]; - is_key_window == YES + let is_key_window: bool = msg_send![&self.ns_window, isKeyWindow]; + is_key_window } } @@ -664,7 +658,7 @@ impl UnownedWindow { } pub fn outer_position(&self) -> Result, NotSupportedError> { - let frame_rect = unsafe { NSWindow::frame(*self.ns_window) }; + let frame_rect = unsafe { NSWindow::frame(&self.ns_window) }; let position = LogicalPosition::new( frame_rect.origin.x as f64, util::bottom_left_to_top_left(frame_rect), @@ -675,7 +669,7 @@ impl UnownedWindow { pub fn inner_position(&self) -> Result, NotSupportedError> { let content_rect = unsafe { - NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window)) + NSWindow::contentRectForFrameRect(&self.ns_window, NSWindow::frame(&self.ns_window)) }; let position = LogicalPosition::new( content_rect.origin.x as f64, @@ -689,13 +683,13 @@ impl UnownedWindow { let scale_factor = self.scale_factor(); let position = position.to_logical(scale_factor); unsafe { - util::set_frame_top_left_point_async(*self.ns_window, util::window_position(position)); + util::set_frame_top_left_point_async(&self.ns_window, util::window_position(position)); } } #[inline] pub fn inner_size(&self) -> PhysicalSize { - let view_frame = unsafe { NSView::frame(*self.ns_view) }; + let view_frame = unsafe { NSView::frame(&self.ns_view) }; let logical: LogicalSize = (view_frame.size.width as f64, view_frame.size.height as f64).into(); let scale_factor = self.scale_factor(); @@ -704,7 +698,7 @@ impl UnownedWindow { #[inline] pub fn outer_size(&self) -> PhysicalSize { - let view_frame = unsafe { NSWindow::frame(*self.ns_window) }; + let view_frame = unsafe { NSWindow::frame(&self.ns_window) }; let logical: LogicalSize = (view_frame.size.width as f64, view_frame.size.height as f64).into(); let scale_factor = self.scale_factor(); @@ -715,7 +709,7 @@ impl UnownedWindow { pub fn set_inner_size(&self, size: Size) { unsafe { let scale_factor = self.scale_factor(); - util::set_content_size_async(*self.ns_window, size.to_logical(scale_factor)); + util::set_content_size_async(&self.ns_window, size.to_logical(scale_factor)); } } @@ -726,7 +720,7 @@ impl UnownedWindow { })); let scale_factor = self.scale_factor(); unsafe { - set_min_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + set_min_inner_size(&self.ns_window, dimensions.to_logical(scale_factor)); } } @@ -737,7 +731,7 @@ impl UnownedWindow { })); let scale_factor = self.scale_factor(); unsafe { - set_max_inner_size(*self.ns_window, dimensions.to_logical(scale_factor)); + set_max_inner_size(&self.ns_window, dimensions.to_logical(scale_factor)); } } @@ -745,9 +739,9 @@ impl UnownedWindow { let scale_factor = self.scale_factor(); unsafe { let min_size = constraints.min_size_logical(scale_factor); - set_min_inner_size(*self.ns_window, min_size); + set_min_inner_size(&self.ns_window, min_size); let max_size = constraints.max_size_logical(scale_factor); - set_max_inner_size(*self.ns_window, max_size); + set_max_inner_size(&self.ns_window, max_size); } } @@ -763,9 +757,9 @@ impl UnownedWindow { if !fullscreen { let mut mask = unsafe { self.ns_window.styleMask() }; if resizable { - mask |= NSWindowStyleMask::NSResizableWindowMask; + mask |= NSWindowStyleMask::Resizable; } else { - mask &= !NSWindowStyleMask::NSResizableWindowMask; + mask &= !NSWindowStyleMask::Resizable; } self.set_style_mask_sync(mask); } // Otherwise, we don't change the mask until we exit fullscreen. @@ -775,9 +769,9 @@ impl UnownedWindow { pub fn set_minimizable(&self, minimizable: bool) { let mut mask = unsafe { self.ns_window.styleMask() }; if minimizable { - mask |= NSWindowStyleMask::NSMiniaturizableWindowMask; + mask |= NSWindowStyleMask::Miniaturizable; } else { - mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + mask &= !NSWindowStyleMask::Miniaturizable; } self.set_style_mask_sync(mask); } @@ -787,8 +781,9 @@ impl UnownedWindow { unsafe { let button = self .ns_window - .standardWindowButton_(NSWindowButton::NSWindowZoomButton); - let _: () = msg_send![button, setEnabled: maximizable]; + .standardWindowButton(NSWindowButton::NSWindowZoomButton) + .unwrap(); + let _: () = msg_send![&button, setEnabled: maximizable]; } } @@ -796,9 +791,9 @@ impl UnownedWindow { pub fn set_closable(&self, closable: bool) { let mut mask = unsafe { self.ns_window.styleMask() }; if closable { - mask |= NSWindowStyleMask::NSClosableWindowMask; + mask |= NSWindowStyleMask::Closable; } else { - mask &= !NSWindowStyleMask::NSClosableWindowMask; + mask &= !NSWindowStyleMask::Closable; } self.set_style_mask_sync(mask); } @@ -809,8 +804,9 @@ impl UnownedWindow { cursor_access.lock().unwrap().cursor = cursor; } unsafe { - let _: () = msg_send![*self.ns_window, - invalidateCursorRectsForView:*self.ns_view + let _: () = msg_send![ + &self.ns_window, + invalidateCursorRectsForView: &*self.ns_view ]; } } @@ -830,8 +826,9 @@ impl UnownedWindow { cursor_state.visible = visible; drop(cursor_state); unsafe { - let _: () = msg_send![*self.ns_window, - invalidateCursorRectsForView:*self.ns_view + let _: () = msg_send![ + &self.ns_window, + invalidateCursorRectsForView:&*self.ns_view ]; } } @@ -845,7 +842,7 @@ impl UnownedWindow { #[inline] pub fn scale_factor(&self) -> f64 { - unsafe { NSWindow::backingScaleFactor(*self.ns_window) as _ } + NSWindow::backingScaleFactor(&self.ns_window) as _ } #[inline] @@ -854,7 +851,7 @@ impl UnownedWindow { let scale_factor = self.scale_factor(); let window_position = physical_window_position.to_logical::(scale_factor); let logical_cursor_position = cursor_position.to_logical::(scale_factor); - let point = appkit::CGPoint { + let point = CGPoint { x: logical_cursor_position.x + window_position.x, y: logical_cursor_position.y + window_position.y, }; @@ -871,29 +868,29 @@ impl UnownedWindow { unsafe { let color = color .map(|(r, g, b, a)| { - NSColor::colorWithRed_green_blue_alpha_( - nil, + Some(NSColor::colorWithRed_green_blue_alpha( r as f64, g as f64, b as f64, a as f64 / 255.0, - ) + )) }) .unwrap_or_else(|| { if self.transparent { - NSColor::clearColor(nil) + Some(NSColor::clearColor()) } else { - nil + None } }); - self.ns_window.setBackgroundColor_(color); + self.ns_window.setBackgroundColor(color.as_deref()); } } #[inline] pub fn drag_window(&self) -> Result<(), ExternalError> { unsafe { - let mut event: id = msg_send![NSApp(), currentEvent]; + let mtm = MainThreadMarker::new_unchecked(); + let mut event: id = msg_send![&NSApp(mtm), currentEvent]; let event_type: NSUInteger = msg_send![event, type]; if event_type == 0x15 { @@ -904,19 +901,19 @@ impl UnownedWindow { event = msg_send![ class!(NSEvent), - mouseEventWithType: NSEventType::NSLeftMouseDown + mouseEventWithType: NSEventType::LeftMouseDown location: mouse_location modifierFlags: event_modifier_flags timestamp: event_timestamp windowNumber: event_window_number context: nil - eventNumber: NSEventSubtype::NSWindowExposedEventType + eventNumber: NSEventSubtype::WindowExposed clickCount: 1 pressure: 1.0 ]; } - let _: () = msg_send![*self.ns_window, performWindowDragWithEvent: event]; + let _: () = msg_send![&self.ns_window, performWindowDragWithEvent: event]; } Ok(()) @@ -929,7 +926,7 @@ impl UnownedWindow { #[inline] pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> { unsafe { - util::set_ignore_mouse_events(*self.ns_window, ignore); + util::set_ignore_mouse_events(&self.ns_window, ignore); } Ok(()) @@ -940,20 +937,20 @@ impl UnownedWindow { // we make it resizable temporalily. let curr_mask = unsafe { self.ns_window.styleMask() }; - let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask; + let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable; let needs_temp_mask = !curr_mask.contains(required); if needs_temp_mask { self.set_style_mask_sync(required); } - let is_zoomed: BOOL = unsafe { msg_send![*self.ns_window, isZoomed] }; + let is_zoomed: bool = unsafe { msg_send![&self.ns_window, isZoomed] }; // Roll back temp styles if needs_temp_mask { self.set_style_mask_sync(curr_mask); } - is_zoomed != NO + is_zoomed } fn saved_style(&self, shared_state: &mut SharedState) -> NSWindowStyleMask { @@ -962,9 +959,9 @@ impl UnownedWindow { .take() .unwrap_or_else(|| unsafe { self.ns_window.styleMask() }); if shared_state.resizable { - base_mask | NSWindowStyleMask::NSResizableWindowMask + base_mask | NSWindowStyleMask::Resizable } else { - base_mask & !NSWindowStyleMask::NSResizableWindowMask + base_mask & !NSWindowStyleMask::Resizable } } @@ -989,19 +986,18 @@ impl UnownedWindow { #[inline] pub fn set_minimized(&self, minimized: bool) { - let is_minimized: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturized] }; - let is_minimized: bool = is_minimized == YES; + let is_minimized: bool = unsafe { msg_send![&self.ns_window, isMiniaturized] }; if is_minimized == minimized { return; } if minimized { unsafe { - NSWindow::miniaturize_(*self.ns_window, *self.ns_window); + NSWindow::miniaturize(&self.ns_window, Some(&self.ns_window)); } } else { unsafe { - NSWindow::deminiaturize_(*self.ns_window, *self.ns_window); + NSWindow::deminiaturize(&self.ns_window, Some(&self.ns_window)); } } } @@ -1014,7 +1010,7 @@ impl UnownedWindow { }; unsafe { util::set_maximized_async( - *self.ns_window, + &self.ns_window, is_zoomed, maximized, Arc::downgrade(&self.shared_state), @@ -1030,8 +1026,7 @@ impl UnownedWindow { #[inline] pub fn is_visible(&self) -> bool { - let is_visible: BOOL = unsafe { msg_send![*self.ns_window, isVisible] }; - is_visible == YES + unsafe { msg_send![&self.ns_window, isVisible] } } #[inline] @@ -1046,38 +1041,36 @@ impl UnownedWindow { #[inline] pub fn is_minimized(&self) -> bool { - let is_minimized: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturized] }; - is_minimized == YES + unsafe { msg_send![&self.ns_window, isMiniaturized] } } #[inline] pub fn is_resizable(&self) -> bool { - let is_resizable: BOOL = unsafe { msg_send![*self.ns_window, isResizable] }; - is_resizable == YES + unsafe { msg_send![&self.ns_window, isResizable] } } #[inline] pub fn is_minimizable(&self) -> bool { - let is_minimizable: BOOL = unsafe { msg_send![*self.ns_window, isMiniaturizable] }; - is_minimizable == YES + unsafe { msg_send![&self.ns_window, isMiniaturizable] } } #[inline] pub fn is_maximizable(&self) -> bool { - let is_maximizable: BOOL; + let is_maximizable: bool; unsafe { let button = self .ns_window - .standardWindowButton_(NSWindowButton::NSWindowZoomButton); - is_maximizable = msg_send![button, isEnabled]; + .standardWindowButton(NSWindowButton::NSWindowZoomButton) + .unwrap(); + is_maximizable = msg_send![&button, isEnabled]; } - is_maximizable == YES + is_maximizable } #[inline] pub fn is_closable(&self) -> bool { - let is_closable: BOOL = unsafe { msg_send![*self.ns_window, hasCloseBox] }; - is_closable == YES + let is_closable: bool = unsafe { msg_send![&self.ns_window, hasCloseBox] }; + is_closable } #[inline] @@ -1087,6 +1080,8 @@ impl UnownedWindow { #[inline] pub fn set_fullscreen(&self, fullscreen: Option) { + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + trace!("Locked shared state in `set_fullscreen`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); if shared_state_lock.is_simple_fullscreen { @@ -1127,13 +1122,13 @@ impl UnownedWindow { .unwrap(); unsafe { - let old_screen = NSWindow::screen(*self.ns_window); - if old_screen != new_screen { - let mut screen_frame: NSRect = msg_send![new_screen, frame]; + let old_screen = NSWindow::screen(&self.ns_window); + if old_screen.as_ref() != Some(&new_screen) { + let mut screen_frame: NSRect = msg_send![&new_screen, frame]; // The coordinate system here has its origin at bottom-left // and Y goes up screen_frame.origin.y += screen_frame.size.height; - util::set_frame_top_left_point_async(*self.ns_window, screen_frame.origin); + util::set_frame_top_left_point_async(&self.ns_window, screen_frame.origin); } } } @@ -1157,10 +1152,10 @@ impl UnownedWindow { if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) { unsafe { - let app = NSApp(); + let app = NSApp(mtm); trace!("Locked shared state in `set_fullscreen`"); let mut shared_state_lock = self.shared_state.lock().unwrap(); - shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); + shared_state_lock.save_presentation_opts = Some(app.presentationOptions()); } } @@ -1217,8 +1212,8 @@ impl UnownedWindow { match (&old_fullscreen, &fullscreen) { (&None, &Some(_)) => unsafe { util::toggle_full_screen_async( - *self.ns_window, - *self.ns_view, + &self.ns_window, + &self.ns_view, old_fullscreen.is_none(), Arc::downgrade(&self.shared_state), ); @@ -1226,8 +1221,8 @@ impl UnownedWindow { (&Some(Fullscreen::Borderless(_)), &None) => unsafe { // State is restored by `window_did_exit_fullscreen` util::toggle_full_screen_async( - *self.ns_window, - *self.ns_view, + &self.ns_window, + &self.ns_view, old_fullscreen.is_none(), Arc::downgrade(&self.shared_state), ); @@ -1236,8 +1231,8 @@ impl UnownedWindow { util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); // Rest of the state is restored by `window_did_exit_fullscreen` util::toggle_full_screen_async( - *self.ns_window, - *self.ns_view, + &self.ns_window, + &self.ns_view, old_fullscreen.is_none(), Arc::downgrade(&self.shared_state), ); @@ -1251,17 +1246,17 @@ impl UnownedWindow { // of the menu bar, and this looks broken, so we must make sure // that the menu bar is disabled. This is done in the window // delegate in `window:willUseFullScreenPresentationOptions:`. - let app = NSApp(); + let app = NSApp(mtm); trace!("Locked shared state in `set_fullscreen`"); - shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); + shared_state_lock.save_presentation_opts = Some(app.presentationOptions()); let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen | NSApplicationPresentationOptions::NSApplicationPresentationHideDock | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; - app.setPresentationOptions_(presentation_options); + app.setPresentationOptions(presentation_options); - let () = msg_send![*self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; + let () = msg_send![&self.ns_window, setLevel: ffi::CGShieldingWindowLevel() + 1]; }, ( &Some(Fullscreen::Exclusive(RootVideoMode { ref video_mode })), @@ -1272,14 +1267,14 @@ impl UnownedWindow { | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar }); - NSApp().setPresentationOptions_(presentation_options); + NSApp(mtm).setPresentationOptions(presentation_options); util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. let () = msg_send![ - *self.ns_window, + &self.ns_window, setLevel: ffi::NSWindowLevel::NSNormalWindowLevel ]; }, @@ -1311,15 +1306,15 @@ impl UnownedWindow { let new_mask = { let mut new_mask = if decorations { - NSWindowStyleMask::NSClosableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSTitledWindowMask + NSWindowStyleMask::Closable + | NSWindowStyleMask::Miniaturizable + | NSWindowStyleMask::Resizable + | NSWindowStyleMask::Titled } else { - NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask + NSWindowStyleMask::Borderless | NSWindowStyleMask::Resizable }; if !resizable { - new_mask &= !NSWindowStyleMask::NSResizableWindowMask; + new_mask &= !NSWindowStyleMask::Resizable; } new_mask }; @@ -1334,7 +1329,7 @@ impl UnownedWindow { } else { ffi::NSWindowLevel::NSNormalWindowLevel }; - unsafe { util::set_level_async(*self.ns_window, level) }; + unsafe { util::set_level_async(&self.ns_window, level) }; } #[inline] @@ -1344,7 +1339,7 @@ impl UnownedWindow { } else { ffi::NSWindowLevel::NSNormalWindowLevel }; - unsafe { util::set_level_async(*self.ns_window, level) }; + unsafe { util::set_level_async(&self.ns_window, level) }; } #[inline] @@ -1365,7 +1360,7 @@ impl UnownedWindow { let logical_spot = spot.to_logical(scale_factor); unsafe { view::set_ime_position( - *self.ns_view, + &self.ns_view, *self.input_context, logical_spot.x, logical_spot.y, @@ -1380,8 +1375,9 @@ impl UnownedWindow { UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest, }); unsafe { + let mtm = MainThreadMarker::new_unchecked(); if let Some(ty) = ns_request_type { - NSApp().requestUserAttention_(ty); + NSApp(mtm).requestUserAttention(ty); } } } @@ -1390,11 +1386,11 @@ impl UnownedWindow { // Allow directly accessing the current monitor internally without unwrapping. pub(crate) fn current_monitor_inner(&self) -> RootMonitorHandle { unsafe { - let screen: id = msg_send![*self.ns_window, screen]; - let desc = NSScreen::deviceDescription(screen); - let key = util::ns_string_id_ref("NSScreenNumber"); - let value = NSDictionary::valueForKey_(desc, *key); - let display_id: NSUInteger = msg_send![value, unsignedIntegerValue]; + let screen: Retained = msg_send_id![&self.ns_window, screen]; + let desc = NSScreen::deviceDescription(&screen); + let key = NSString::from_str("NSScreenNumber"); + let value = NSDictionary::objectForKey(&desc, &key).unwrap(); + let display_id: NSUInteger = msg_send![&value, unsignedIntegerValue]; RootMonitorHandle { inner: MonitorHandle::new(display_id.try_into().unwrap()), } @@ -1477,7 +1473,7 @@ impl UnownedWindow { pub fn set_content_protection(&self, enabled: bool) { unsafe { - let _: () = msg_send![*self.ns_window, setSharingType: !enabled as i32]; + let _: () = msg_send![&self.ns_window, setSharingType: !enabled as usize]; } } @@ -1485,13 +1481,11 @@ impl UnownedWindow { unsafe { let mut collection_behavior = self.ns_window.collectionBehavior(); if visible { - collection_behavior |= - NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces; + collection_behavior |= NSWindowCollectionBehavior::CanJoinAllSpaces; } else { - collection_behavior &= - !NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces; + collection_behavior &= !NSWindowCollectionBehavior::CanJoinAllSpaces; }; - self.ns_window.setCollectionBehavior_(collection_behavior) + self.ns_window.setCollectionBehavior(collection_behavior) } } @@ -1507,12 +1501,12 @@ impl UnownedWindow { impl WindowExtMacOS for UnownedWindow { #[inline] fn ns_window(&self) -> *mut c_void { - *self.ns_window as *mut _ + &*self.ns_window as *const NSWindow as *mut _ } #[inline] fn ns_view(&self) -> *mut c_void { - unsafe { (*self.ns_window).contentView() as *mut _ } + &*self.ns_window.contentView().unwrap() as *const NSView as *mut _ } #[inline] @@ -1526,7 +1520,8 @@ impl WindowExtMacOS for UnownedWindow { let mut shared_state_lock = self.shared_state.lock().unwrap(); unsafe { - let app = NSApp(); + let mtm = MainThreadMarker::new_unchecked(); + let app = NSApp(mtm); let is_native_fullscreen = shared_state_lock.fullscreen.is_some(); let is_simple_fullscreen = shared_state_lock.is_simple_fullscreen; @@ -1541,12 +1536,12 @@ impl WindowExtMacOS for UnownedWindow { if fullscreen { // Remember the original window's settings // Exclude title bar - shared_state_lock.standard_frame = Some(NSWindow::contentRectForFrameRect_( - *self.ns_window, - NSWindow::frame(*self.ns_window), + shared_state_lock.standard_frame = Some(NSWindow::contentRectForFrameRect( + &self.ns_window, + NSWindow::frame(&self.ns_window), )); shared_state_lock.saved_style = Some(self.ns_window.styleMask()); - shared_state_lock.save_presentation_opts = Some(app.presentationOptions_()); + shared_state_lock.save_presentation_opts = Some(app.presentationOptions()); // Tell our window's state that we're in fullscreen shared_state_lock.is_simple_fullscreen = true; @@ -1555,35 +1550,35 @@ impl WindowExtMacOS for UnownedWindow { let presentation_options = NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar; - app.setPresentationOptions_(presentation_options); + app.setPresentationOptions(presentation_options); // Hide the titlebar util::toggle_style_mask( - *self.ns_window, - *self.ns_view, - NSWindowStyleMask::NSTitledWindowMask, + &self.ns_window, + &self.ns_view, + NSWindowStyleMask::Titled, false, ); // Set the window frame to the screen frame size - let screen = self.ns_window.screen(); - let screen_frame = NSScreen::frame(screen); - NSWindow::setFrame_display_(*self.ns_window, screen_frame, YES); + let screen = self.ns_window.screen().unwrap(); + let screen_frame = NSScreen::frame(&screen); + NSWindow::setFrame_display(&self.ns_window, screen_frame, true); // Fullscreen windows can't be resized, minimized, or moved util::toggle_style_mask( - *self.ns_window, - *self.ns_view, - NSWindowStyleMask::NSMiniaturizableWindowMask, + &self.ns_window, + &self.ns_view, + NSWindowStyleMask::Miniaturizable, false, ); util::toggle_style_mask( - *self.ns_window, - *self.ns_view, - NSWindowStyleMask::NSResizableWindowMask, + &self.ns_window, + &self.ns_view, + NSWindowStyleMask::Resizable, false, ); - NSWindow::setMovable_(*self.ns_window, NO); + NSWindow::setMovable(&self.ns_window, false); true } else { @@ -1592,12 +1587,12 @@ impl WindowExtMacOS for UnownedWindow { shared_state_lock.is_simple_fullscreen = false; if let Some(presentation_opts) = shared_state_lock.save_presentation_opts { - app.setPresentationOptions_(presentation_opts); + app.setPresentationOptions(presentation_opts); } let frame = shared_state_lock.saved_standard_frame(); - NSWindow::setFrame_display_(*self.ns_window, frame, YES); - NSWindow::setMovable_(*self.ns_window, YES); + NSWindow::setFrame_display(&self.ns_window, frame, true); + NSWindow::setMovable(&self.ns_window, true); true } @@ -1606,23 +1601,20 @@ impl WindowExtMacOS for UnownedWindow { #[inline] fn has_shadow(&self) -> bool { - unsafe { self.ns_window.hasShadow() == YES } + self.ns_window.hasShadow() } #[inline] fn set_has_shadow(&self, has_shadow: bool) { - unsafe { - self - .ns_window - .setHasShadow_(if has_shadow { YES } else { NO }) - } + self.ns_window.setHasShadow(has_shadow) } #[inline] fn set_traffic_light_inset>(&self, position: P) { let position: Position = position.into(); + #[allow(deprecated)] // TODO: Use define_class! unsafe { - let state_ptr: *mut c_void = *(**self.ns_view).get_ivar("taoState"); + let state_ptr: *mut c_void = *(*&self.ns_view).get_ivar("taoState"); let state = &mut *(state_ptr as *mut ViewState); state.traffic_light_inset = Some(position.to_logical(self.scale_factor())); } @@ -1630,70 +1622,57 @@ impl WindowExtMacOS for UnownedWindow { #[inline] fn set_is_document_edited(&self, edited: bool) { - unsafe { - self - .ns_window - .setDocumentEdited_(if edited { YES } else { NO }) - } + self.ns_window.setDocumentEdited(edited) } #[inline] fn is_document_edited(&self) -> bool { unsafe { - let is_document_edited: BOOL = msg_send![*self.ns_window, isDocumentEdited]; - is_document_edited == YES + let is_document_edited: bool = msg_send![&self.ns_window, isDocumentEdited]; + is_document_edited } } #[inline] fn set_allows_automatic_window_tabbing(&self, enabled: bool) { - unsafe { - NSWindow::setAllowsAutomaticWindowTabbing_(*self.ns_window, if enabled { YES } else { NO }) - } + let mtm = MainThreadMarker::new().unwrap(); + NSWindow::setAllowsAutomaticWindowTabbing(enabled, mtm) } #[inline] fn allows_automatic_window_tabbing(&self) -> bool { - unsafe { - let allows_tabbing: BOOL = NSWindow::allowsAutomaticWindowTabbing(*self.ns_window); - allows_tabbing == YES - } + let mtm = MainThreadMarker::new().unwrap(); + NSWindow::allowsAutomaticWindowTabbing(mtm) } #[inline] fn set_tabbing_identifier(&self, identifier: &str) { unsafe { let _: () = - msg_send![*self.ns_window, setTabbingIdentifier: NSString::alloc(nil).init_str(identifier)]; + msg_send![&self.ns_window, setTabbingIdentifier: &*NSString::from_str(identifier)]; } } #[inline] fn tabbing_identifier(&self) -> String { - unsafe { - let tabbing_identifier = NSWindow::tabbingIdentifier(*self.ns_window); - ns_string_to_rust(tabbing_identifier) - } + let tabbing_identifier = NSWindow::tabbingIdentifier(&self.ns_window); + tabbing_identifier.to_string() } #[inline] fn set_fullsize_content_view(&self, fullsize: bool) { - let mut mask = unsafe { self.ns_window.styleMask() }; + let mut mask = self.ns_window.styleMask(); if fullsize { - mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask; + mask |= NSWindowStyleMask::FullSizeContentView; } else { - mask &= !NSWindowStyleMask::NSFullSizeContentViewWindowMask; + mask &= !NSWindowStyleMask::FullSizeContentView; } self.set_style_mask_sync(mask); } #[inline] fn set_titlebar_transparent(&self, transparent: bool) { - unsafe { - self - .ns_window - .setTitlebarAppearsTransparent_(transparent as BOOL); - } + self.ns_window.setTitlebarAppearsTransparent(transparent); } fn set_badge_label(&self, label: Option) { @@ -1705,56 +1684,54 @@ impl Drop for UnownedWindow { fn drop(&mut self) { trace!("Dropping `UnownedWindow` ({:?})", self as *mut _); // Close the window if it has not yet been closed. - if *self.ns_window != nil { - unsafe { util::close_async(self.ns_window.clone()) }; - } + unsafe { util::close_async(&self.ns_window) }; } } -unsafe fn set_min_inner_size(window: V, mut min_size: LogicalSize) { +unsafe fn set_min_inner_size(window: &NSWindow, mut min_size: LogicalSize) { let mut current_rect = NSWindow::frame(window); - let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window)); + let content_rect = NSWindow::contentRectForFrameRect(window, NSWindow::frame(window)); // Convert from client area size to window size min_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0 min_size.height += (current_rect.size.height - content_rect.size.height) as f64; - window.setMinSize_(NSSize { + window.setMinSize(NSSize { width: min_size.width as CGFloat, height: min_size.height as CGFloat, }); // If necessary, resize the window to match constraint if current_rect.size.width < min_size.width { current_rect.size.width = min_size.width; - window.setFrame_display_(current_rect, NO) + window.setFrame_display(current_rect, false) } if current_rect.size.height < min_size.height { // The origin point of a rectangle is at its bottom left in Cocoa. // To ensure the window's top-left point remains the same: current_rect.origin.y += current_rect.size.height - min_size.height; current_rect.size.height = min_size.height; - window.setFrame_display_(current_rect, NO) + window.setFrame_display(current_rect, false) } } -unsafe fn set_max_inner_size(window: V, mut max_size: LogicalSize) { +unsafe fn set_max_inner_size(window: &NSWindow, mut max_size: LogicalSize) { let mut current_rect = NSWindow::frame(window); - let content_rect = NSWindow::contentRectForFrameRect_(window, NSWindow::frame(window)); + let content_rect = NSWindow::contentRectForFrameRect(window, NSWindow::frame(window)); // Convert from client area size to window size max_size.width += (current_rect.size.width - content_rect.size.width) as f64; // this tends to be 0 max_size.height += (current_rect.size.height - content_rect.size.height) as f64; - window.setMaxSize_(NSSize { + window.setMaxSize(NSSize { width: max_size.width as CGFloat, height: max_size.height as CGFloat, }); // If necessary, resize the window to match constraint if current_rect.size.width > max_size.width { current_rect.size.width = max_size.width; - window.setFrame_display_(current_rect, NO) + window.setFrame_display(current_rect, false) } if current_rect.size.height > max_size.height { // The origin point of a rectangle is at its bottom left in Cocoa. // To ensure the window's top-left point remains the same: current_rect.origin.y += current_rect.size.height - max_size.height; current_rect.size.height = max_size.height; - window.setFrame_display_(current_rect, NO) + window.setFrame_display(current_rect, false) } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 83ad6e035..affb812fd 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -4,19 +4,20 @@ use std::{ f64, + ffi::CStr, os::raw::c_void, sync::{Arc, Weak}, }; -use cocoa::{ - appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow}, - base::{id, nil}, - foundation::{NSAutoreleasePool, NSString, NSUInteger}, +use objc2::{ + msg_send_id, + rc::Retained, + runtime::{AnyClass as Class, AnyObject as Object, ClassBuilder as ClassDecl, Sel}, }; -use objc::{ - declare::ClassDecl, - runtime::{Class, Object, Sel, BOOL, NO, YES}, +use objc2_app_kit::{ + self as appkit, NSApplicationPresentationOptions, NSPasteboard, NSView, NSWindow, }; +use objc2_foundation::{NSArray, NSAutoreleasePool, NSString, NSUInteger}; use crate::{ dpi::{LogicalPosition, LogicalSize}, @@ -25,6 +26,7 @@ use crate::{ platform_impl::platform::{ app_state::AppState, event::{EventProxy, EventWrapper}, + ffi::{id, nil, BOOL, NO, YES}, util::{self, IdRef}, view::ViewState, window::{get_ns_theme, get_window_id, UnownedWindow}, @@ -33,10 +35,10 @@ use crate::{ }; pub struct WindowDelegateState { - ns_window: IdRef, // never changes + ns_window: Retained, // never changes // We keep this ns_view because we still need its view state for some extern function // like didResignKey - ns_view: IdRef, // never changes + ns_view: Retained, // never changes window: Weak, @@ -77,8 +79,8 @@ impl WindowDelegateState { delegate_state } - fn ns_view(&self) -> id { - unsafe { (*self.ns_window).contentView() } + fn ns_view(&self) -> Retained { + self.ns_window.contentView().unwrap() } fn with_window(&mut self, callback: F) -> Option @@ -90,7 +92,7 @@ impl WindowDelegateState { pub fn emit_event(&mut self, event: WindowEvent<'static>) { let event = Event::WindowEvent { - window_id: WindowId(get_window_id(*self.ns_window)), + window_id: WindowId(get_window_id(&self.ns_window)), event, }; AppState::queue_event(EventWrapper::StaticEvent(event)); @@ -104,7 +106,7 @@ impl WindowDelegateState { self.previous_scale_factor = scale_factor; let wrapper = EventWrapper::EventProxy(EventProxy::DpiChangedProxy { - ns_window: IdRef::retain(*self.ns_window), + ns_window: self.ns_window.clone(), suggested_size: self.view_size(), scale_factor, }); @@ -112,7 +114,7 @@ impl WindowDelegateState { } pub fn emit_resize_event(&mut self) { - let rect = unsafe { NSView::frame(self.ns_view()) }; + let rect = NSView::frame(&self.ns_view()); let scale_factor = self.get_scale_factor(); let logical_size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64); let size = logical_size.to_physical(scale_factor); @@ -120,7 +122,7 @@ impl WindowDelegateState { } fn emit_move_event(&mut self) { - let rect = unsafe { NSWindow::frame(*self.ns_window) }; + let rect = NSWindow::frame(&self.ns_window); let x = rect.origin.x as f64; let y = util::bottom_left_to_top_left(rect); let moved = self.previous_position != Some((x, y)); @@ -133,11 +135,11 @@ impl WindowDelegateState { } fn get_scale_factor(&self) -> f64 { - (unsafe { NSWindow::backingScaleFactor(*self.ns_window) }) as f64 + NSWindow::backingScaleFactor(&self.ns_window) as f64 } fn view_size(&self) -> LogicalSize { - let ns_size = unsafe { NSView::frame(self.ns_view()).size }; + let ns_size = NSView::frame(&self.ns_view()).size; LogicalSize::new(ns_size.width as f64, ns_size.height as f64) } } @@ -161,102 +163,101 @@ lazy_static! { let superclass = class!(NSResponder); let mut decl = ClassDecl::new("TaoWindowDelegate", superclass).unwrap(); - decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); + decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _)); decl.add_method( sel!(initWithTao:), - init_with_tao as extern "C" fn(&Object, Sel, *mut c_void) -> id, + init_with_tao as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(markIsCheckingZoomedIn), - mark_is_checking_zoomed_in as extern "C" fn(&Object, Sel), + mark_is_checking_zoomed_in as extern "C" fn(_, _), ); decl.add_method( sel!(clearIsCheckingZoomedIn), - clear_is_checking_zoomed_in as extern "C" fn(&Object, Sel), + clear_is_checking_zoomed_in as extern "C" fn(_, _), ); decl.add_method( sel!(windowShouldClose:), - window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + window_should_close as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(windowWillClose:), - window_will_close as extern "C" fn(&Object, Sel, id), + window_will_close as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidResize:), - window_did_resize as extern "C" fn(&Object, Sel, id), + window_did_resize as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidMove:), - window_did_move as extern "C" fn(&Object, Sel, id), + window_did_move as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidChangeBackingProperties:), - window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), + window_did_change_backing_properties as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidBecomeKey:), - window_did_become_key as extern "C" fn(&Object, Sel, id), + window_did_become_key as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidResignKey:), - window_did_resign_key as extern "C" fn(&Object, Sel, id), + window_did_resign_key as extern "C" fn(_, _, _), ); decl.add_method( sel!(draggingEntered:), - dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL, + dragging_entered as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(prepareForDragOperation:), - prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + prepare_for_drag_operation as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(performDragOperation:), - perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + perform_drag_operation as extern "C" fn(_, _, _) -> _, ); decl.add_method( sel!(concludeDragOperation:), - conclude_drag_operation as extern "C" fn(&Object, Sel, id), + conclude_drag_operation as extern "C" fn(_, _, _), ); decl.add_method( sel!(draggingExited:), - dragging_exited as extern "C" fn(&Object, Sel, id), + dragging_exited as extern "C" fn(_, _, _), ); decl.add_method( sel!(window:willUseFullScreenPresentationOptions:), - window_will_use_fullscreen_presentation_options - as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger, + window_will_use_fullscreen_presentation_options as extern "C" fn(_, _, _, _) -> _, ); decl.add_method( sel!(windowDidEnterFullScreen:), - window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id), + window_did_enter_fullscreen as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowWillEnterFullScreen:), - window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id), + window_will_enter_fullscreen as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidExitFullScreen:), - window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id), + window_did_exit_fullscreen as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowWillExitFullScreen:), - window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id), + window_will_exit_fullscreen as extern "C" fn(_, _, _), ); decl.add_method( sel!(windowDidFailToEnterFullScreen:), - window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id), + window_did_fail_to_enter_fullscreen as extern "C" fn(_, _, _), ); decl.add_method( sel!(effectiveAppearanceDidChange:), - effective_appearance_did_change as extern "C" fn(&Object, Sel, id), + effective_appearance_did_change as extern "C" fn(_, _, _), ); decl.add_method( sel!(effectiveAppearanceDidChangedOnMainThread:), - effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id), + effective_appearance_did_changed_on_main_thread as extern "C" fn(_, _, _), ); decl.add_ivar::<*mut c_void>("taoState"); @@ -267,6 +268,7 @@ lazy_static! { // This function is definitely unsafe, but labeling that would increase // boilerplate and wouldn't really clarify anything... fn with_state T, T>(this: &Object, callback: F) { + #[allow(deprecated)] // TODO: Use define_class! let state_ptr = unsafe { let state_ptr: *mut c_void = *this.get_ivar("taoState"); &mut *(state_ptr as *mut WindowDelegateState) @@ -281,23 +283,24 @@ extern "C" fn dealloc(this: &Object, _sel: Sel) { } extern "C" fn init_with_tao(this: &Object, _sel: Sel, state: *mut c_void) -> id { + #[allow(deprecated)] // TODO: Use define_class! unsafe { let this: id = msg_send![this, init]; if this != nil { - (*this).set_ivar("taoState", state); + *(*this).get_mut_ivar("taoState") = state; with_state(&*this, |state| { - let () = msg_send![*state.ns_window, setDelegate: this]; + let () = msg_send![&state.ns_window, setDelegate: this]; }); } let notification_center: &Object = msg_send![class!(NSDistributedNotificationCenter), defaultCenter]; - let notification_name = NSString::alloc(nil).init_str("AppleInterfaceThemeChangedNotification"); + let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification"); let _: () = msg_send![ notification_center, addObserver: this selector: sel!(effectiveAppearanceDidChange:) - name: notification_name + name: &*notification_name object: nil ]; @@ -328,11 +331,10 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) { trace!("Triggered `windowWillClose:`"); with_state(this, |state| unsafe { // `setDelegate:` retains the previous value and then autoreleases it - let pool = NSAutoreleasePool::new(nil); + let _pool = NSAutoreleasePool::new(); // Since El Capitan, we need to be careful that delegate methods can't // be called after the window closes. - let () = msg_send![*state.ns_window, setDelegate: nil]; - pool.drain(); + let () = msg_send![&state.ns_window, setDelegate: nil]; state.emit_event(WindowEvent::Destroyed); }); trace!("Completed `windowWillClose:`"); @@ -390,8 +392,9 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { // Here we (very unsafely) acquire the taoState (a ViewState) from the // Object referenced by state.ns_view (an IdRef, which is dereferenced // to an id) + #[allow(deprecated)] // TODO: Use define_class! let view_state: &mut ViewState = unsafe { - let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref"); + let ns_view: &Object = &state.ns_view; let state_ptr: *mut c_void = *ns_view.get_ivar("taoState"); &mut *(state_ptr as *mut ViewState) }; @@ -411,17 +414,17 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL { trace!("Triggered `draggingEntered:`"); - use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; - let pb: id = unsafe { msg_send![sender, draggingPasteboard] }; - let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) }; + let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; + let filenames = + unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap(); - for file in unsafe { filenames.iter() } { - use std::ffi::CStr; + for file in unsafe { Retained::cast::(filenames) } { + let file = unsafe { Retained::cast::(file) }; unsafe { - let f = NSString::UTF8String(file); + let f = NSString::UTF8String(&file); let path = CStr::from_ptr(f).to_string_lossy().into_owned(); with_state(this, |state| { @@ -445,17 +448,17 @@ extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL { extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL { trace!("Triggered `performDragOperation:`"); - use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; use std::path::PathBuf; - let pb: id = unsafe { msg_send![sender, draggingPasteboard] }; - let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) }; + let pb: Retained = unsafe { msg_send_id![sender, draggingPasteboard] }; + let filenames = + unsafe { NSPasteboard::propertyListForType(&pb, appkit::NSFilenamesPboardType) }.unwrap(); - for file in unsafe { filenames.iter() } { - use std::ffi::CStr; + for file in unsafe { Retained::cast::(filenames) } { + let file = unsafe { Retained::cast::(file) }; unsafe { - let f = NSString::UTF8String(file); + let f = NSString::UTF8String(&file); let path = CStr::from_ptr(f).to_string_lossy().into_owned(); with_state(this, |state| { @@ -634,7 +637,8 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) }); if state.initial_fullscreen { let _: () = unsafe { - msg_send![*state.ns_window, + msg_send![ + &state.ns_window, performSelector:sel!(toggleFullScreen:) withObject:nil afterDelay: 0.5