diff --git a/Cargo.toml b/Cargo.toml index 2d65e460..dc3ff217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ uuid = { version = "0.8", features = ["v4"], optional = true } [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.24.0" core-foundation = "0.9.1" -objc = "0.2.7" +objc2 = "0.6" uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] diff --git a/src/gl/macos.rs b/src/gl/macos.rs index f68b796a..32942222 100644 --- a/src/gl/macos.rs +++ b/src/gl/macos.rs @@ -1,7 +1,3 @@ -// This is required because the objc crate is causing a lot of warnings: https://github.com/SSheldon/rust-objc/issues/125 -// Eventually we should migrate to the objc2 crate and remove this. -#![allow(unexpected_cfgs)] - use std::ffi::c_void; use std::str::FromStr; @@ -21,7 +17,8 @@ use core_foundation::base::TCFType; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use core_foundation::string::CFString; -use objc::{msg_send, sel, sel_impl}; +use objc2::msg_send; +use objc2::runtime::AnyObject; use super::{GlConfig, GlError, Profile}; @@ -98,15 +95,16 @@ impl GlContext { NSOpenGLView::display_(view); parent_view.addSubview_(view); - let context: id = msg_send![view, openGLContext]; - let () = msg_send![context, retain]; + let context_any: *mut AnyObject = msg_send![view as *mut AnyObject, openGLContext]; + let _: *mut AnyObject = msg_send![context_any, retain]; + let context: id = context_any as id; context.setValues_forParameter_( &(config.vsync as i32), NSOpenGLContextParameter::NSOpenGLCPSwapInterval, ); - let () = msg_send![pixel_format, release]; + let () = msg_send![pixel_format as *mut AnyObject, release]; Ok(GlContext { view, context }) } @@ -131,7 +129,7 @@ impl GlContext { pub fn swap_buffers(&self) { unsafe { self.context.flushBuffer(); - let () = msg_send![self.view, setNeedsDisplay: YES]; + let () = msg_send![self.view as *mut AnyObject, setNeedsDisplay: YES]; } } @@ -139,7 +137,7 @@ impl GlContext { pub(crate) fn resize(&self, size: NSSize) { unsafe { NSView::setFrameSize(self.view, size) }; unsafe { - let _: () = msg_send![self.view, setNeedsDisplay: YES]; + let _: () = msg_send![self.view as *mut AnyObject, setNeedsDisplay: YES]; } } } @@ -147,8 +145,8 @@ impl GlContext { impl Drop for GlContext { fn drop(&mut self) { unsafe { - let () = msg_send![self.context, release]; - let () = msg_send![self.view, release]; + let () = msg_send![self.context as *mut AnyObject, release]; + let () = msg_send![self.view as *mut AnyObject, release]; } } } diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs index 18f3bfe5..1830f534 100644 --- a/src/macos/keyboard.rs +++ b/src/macos/keyboard.rs @@ -24,7 +24,8 @@ use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType}; use cocoa::base::id; use cocoa::foundation::NSString; use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Modifiers}; -use objc::{msg_send, sel, sel_impl}; +use objc2::msg_send; +use objc2::runtime::AnyObject; use crate::keyboard::code_to_location; @@ -282,10 +283,23 @@ impl KeyboardState { pub(crate) fn process_native_event(&self, event: id) -> Option { unsafe { let event_type = event.eventType(); - let key_code = event.keyCode(); + let raw_mods = event.modifierFlags(); + // `-[NSEvent keyCode]` is documented to raise + // `NSInternalInconsistencyException` when sent to non-key events. + // AppKit occasionally dispatches non-key events into + // `keyDown:` / `keyUp:` / `flagsChanged:` selectors + // (e.g. `NSAppKitDefined`, `NSSystemDefined`, or sync events + // around Cmd-Tab / input-source switches), so gate the call. + // Without this gate, the exception unwinds out of the + // `extern "C-unwind"` callback and silently swallows the event. + let key_code = match event_type { + NSEventType::NSKeyDown | NSEventType::NSKeyUp | NSEventType::NSFlagsChanged => { + event.keyCode() + } + _ => return None, + }; let code = key_code_to_code(key_code); let location = code_to_location(code); - let raw_mods = event.modifierFlags(); let modifiers = make_modifiers(raw_mods); let state = match event_type { NSEventType::NSKeyDown => KeyState::Down, @@ -311,10 +325,24 @@ impl KeyboardState { return None; } } + // Already filtered above; reachable only via newly-introduced + // event types we haven't taught the layer above about. _ => unreachable!(), }; let is_composing = false; - let repeat: bool = event_type == NSEventType::NSKeyDown && msg_send![event, isARepeat]; + // `-[NSEvent isARepeat]` is documented to raise + // `NSInternalInconsistencyException` when sent to anything other + // than keyDown/keyUp. Gate the query on the event types that + // actually carry a meaningful repeat flag — without this gate, + // the exception unwinds out of the `extern "C-unwind"` callback + // on every `flagsChanged:` press, dropping the modifier-state + // KeyboardEvent that downstream layers (e.g. baseview consumers + // that track Alt/Cmd/Shift via flagsChanged) rely on. + let repeat: bool = if event_type == NSEventType::NSKeyDown { + msg_send![event as *mut AnyObject, isARepeat] + } else { + false + }; let key = if let Some(key) = code_to_key(code) { key } else { diff --git a/src/macos/mod.rs b/src/macos/mod.rs index d5e7f591..02a0e36f 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -1,7 +1,3 @@ -// This is required because the objc crate is causing a lot of warnings: https://github.com/SSheldon/rust-objc/issues/125 -// Eventually we should migrate to the objc2 crate and remove this. -#![allow(unexpected_cfgs)] - mod keyboard; mod view; mod window; diff --git a/src/macos/view.rs b/src/macos/view.rs index 063ba24c..8f5cda91 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -1,18 +1,67 @@ -use std::ffi::c_void; +use std::ffi::{c_void, CStr, CString}; use cocoa::appkit::{NSEvent, NSFilenamesPboardType, NSView, NSWindow}; -use cocoa::base::{id, nil, BOOL, NO, YES}; +use cocoa::base::{id, nil, NO}; use cocoa::foundation::{NSArray, NSPoint, NSRect, NSSize, NSUInteger}; -use objc::{ - class, - declare::ClassDecl, - msg_send, - runtime::{Class, Object, Sel}, - sel, sel_impl, +use objc2::{ + class, msg_send, + runtime::{AnyClass, AnyObject, Bool as ObjcBool, ClassBuilder, Sel}, + sel, Encode, Encoding, }; use uuid::Uuid; +/// `CGPoint`/`CGSize`/`CGRect` clones carrying an `objc2::Encode` impl. Layout-identical to +/// cocoa's `NSPoint`/`NSSize`/`NSRect`, so `From` is a field-wise copy. We need these because +/// cocoa's types are external and can't implement objc2's `Encode` trait. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +struct CgPoint { + x: f64, + y: f64, +} +#[repr(C)] +#[derive(Clone, Copy, Debug)] +struct CgSize { + width: f64, + height: f64, +} +#[repr(C)] +#[derive(Clone, Copy, Debug)] +struct CgRect { + origin: CgPoint, + size: CgSize, +} + +unsafe impl Encode for CgPoint { + const ENCODING: Encoding = + Encoding::Struct("CGPoint", &[::ENCODING, ::ENCODING]); +} +unsafe impl Encode for CgSize { + const ENCODING: Encoding = + Encoding::Struct("CGSize", &[::ENCODING, ::ENCODING]); +} +unsafe impl Encode for CgRect { + const ENCODING: Encoding = + Encoding::Struct("CGRect", &[::ENCODING, ::ENCODING]); +} + +impl From for NSRect { + fn from(r: CgRect) -> Self { + NSRect::new(NSPoint::new(r.origin.x, r.origin.y), NSSize::new(r.size.width, r.size.height)) + } +} +impl From for CgPoint { + fn from(p: NSPoint) -> Self { + CgPoint { x: p.x, y: p.y } + } +} +impl From for NSPoint { + fn from(p: CgPoint) -> Self { + NSPoint::new(p.x, p.y) + } +} + use crate::MouseEvent::{ButtonPressed, ButtonReleased}; use crate::{ DropData, DropEffect, Event, EventStatus, MouseButton, MouseEvent, Point, ScrollDelta, Size, @@ -38,15 +87,15 @@ extern "C" { macro_rules! add_simple_mouse_class_method { ($class:ident, $sel:ident, $event:expr) => { #[allow(non_snake_case)] - extern "C" fn $sel(this: &Object, _: Sel, _: id){ - let state = unsafe { WindowState::from_view(this) }; + extern "C-unwind" fn $sel(this: *const AnyObject, _: Sel, _: *mut AnyObject){ + let state = unsafe { WindowState::from_view(&*this) }; state.trigger_event(Event::Mouse($event)); } $class.add_method( sel!($sel:), - $sel as extern "C" fn(&Object, Sel, id), + $sel as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); }; } @@ -56,10 +105,10 @@ macro_rules! add_simple_mouse_class_method { macro_rules! add_mouse_button_class_method { ($class:ident, $sel:ident, $event_ty:ident, $button:expr) => { #[allow(non_snake_case)] - extern "C" fn $sel(this: &Object, _: Sel, event: id){ - let state = unsafe { WindowState::from_view(this) }; + extern "C-unwind" fn $sel(this: *const AnyObject, _: Sel, event: *mut AnyObject){ + let state = unsafe { WindowState::from_view(&*this) }; - let modifiers = unsafe { NSEvent::modifierFlags(event) }; + let modifiers = unsafe { NSEvent::modifierFlags(event as id) }; state.trigger_event(Event::Mouse($event_ty { button: $button, @@ -69,7 +118,7 @@ macro_rules! add_mouse_button_class_method { $class.add_method( sel!($sel:), - $sel as extern "C" fn(&Object, Sel, id), + $sel as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); }; } @@ -77,17 +126,17 @@ macro_rules! add_mouse_button_class_method { macro_rules! add_simple_keyboard_class_method { ($class:ident, $sel:ident) => { #[allow(non_snake_case)] - extern "C" fn $sel(this: &Object, _: Sel, event: id){ - let state = unsafe { WindowState::from_view(this) }; + extern "C-unwind" fn $sel(this: *const AnyObject, _: Sel, event: *mut AnyObject){ + let state = unsafe { WindowState::from_view(&*this) }; if let Some(key_event) = state.process_native_key_event(event){ let status = state.trigger_event(Event::Keyboard(key_event)); if let EventStatus::Ignored = status { unsafe { - let superclass = msg_send![this, superclass]; + let superclass: &AnyClass = msg_send![this, superclass]; - let () = msg_send![super(this, superclass), $sel:event]; + let () = msg_send![super(&*this, superclass), $sel:event]; } } } @@ -95,27 +144,29 @@ macro_rules! add_simple_keyboard_class_method { $class.add_method( sel!($sel:), - $sel as extern "C" fn(&Object, Sel, id), + $sel as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); }; } unsafe fn register_notification(observer: id, notification_name: id, object: id) { - let notification_center: id = msg_send![class!(NSNotificationCenter), defaultCenter]; + let notification_center: *mut AnyObject = + msg_send![class!(NSNotificationCenter), defaultCenter]; let _: () = msg_send![ notification_center, - addObserver:observer - selector:sel!(handleNotification:) - name:notification_name - object:object + addObserver: observer as *mut AnyObject, + selector: sel!(handleNotification:), + name: notification_name as *mut AnyObject, + object: object as *mut AnyObject, ]; } pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id { let class = create_view_class(); - let view: id = msg_send![class, alloc]; + let view_any: *mut AnyObject = msg_send![class, alloc]; + let view: id = view_any as id; let size = window_options.size; @@ -124,91 +175,118 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id { register_notification(view, NSWindowDidBecomeKeyNotification, nil); register_notification(view, NSWindowDidResignKeyNotification, nil); - let _: id = msg_send![ - view, - registerForDraggedTypes: NSArray::arrayWithObjects(nil, &[NSFilenamesPboardType]) - ]; + let drag_types = NSArray::arrayWithObjects(nil, &[NSFilenamesPboardType]) as *mut AnyObject; + let _: () = msg_send![view as *mut AnyObject, registerForDraggedTypes: drag_types]; view } -unsafe fn create_view_class() -> &'static Class { +unsafe fn create_view_class() -> &'static AnyClass { // Use unique class names so that there are no conflicts between different // instances. The class is deleted when the view is released. Previously, // the class was stored in a OnceCell after creation. This way, we didn't // have to recreate it each time a view was opened, but now we don't leave // any class definitions lying around when the plugin is closed. - let class_name = format!("BaseviewNSView_{}", Uuid::new_v4().to_simple()); - let mut class = ClassDecl::new(&class_name, class!(NSView)).unwrap(); + let class_name = + CString::new(format!("BaseviewNSView_{}", Uuid::new_v4().to_simple())).unwrap(); + let mut class = ClassBuilder::new(&class_name, class!(NSView)).unwrap(); class.add_method( sel!(acceptsFirstResponder), - property_yes as extern "C" fn(&Object, Sel) -> BOOL, + property_yes as extern "C-unwind" fn(*const AnyObject, Sel) -> ObjcBool, ); class.add_method( sel!(becomeFirstResponder), - become_first_responder as extern "C" fn(&Object, Sel) -> BOOL, + become_first_responder as extern "C-unwind" fn(*const AnyObject, Sel) -> ObjcBool, ); class.add_method( sel!(resignFirstResponder), - resign_first_responder as extern "C" fn(&Object, Sel) -> BOOL, + resign_first_responder as extern "C-unwind" fn(*const AnyObject, Sel) -> ObjcBool, + ); + class.add_method( + sel!(isFlipped), + property_yes as extern "C-unwind" fn(*const AnyObject, Sel) -> ObjcBool, ); - class.add_method(sel!(isFlipped), property_yes as extern "C" fn(&Object, Sel) -> BOOL); class.add_method( sel!(preservesContentInLiveResize), - property_no as extern "C" fn(&Object, Sel) -> BOOL, + property_no as extern "C-unwind" fn(*const AnyObject, Sel) -> ObjcBool, ); class.add_method( sel!(acceptsFirstMouse:), - accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL, + accepts_first_mouse + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> ObjcBool, ); class.add_method( sel!(windowShouldClose:), - window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + window_should_close + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> ObjcBool, ); - class.add_method(sel!(dealloc), dealloc as extern "C" fn(&mut Object, Sel)); + class.add_method(sel!(dealloc), dealloc as extern "C-unwind" fn(*mut AnyObject, Sel)); class.add_method( sel!(viewWillMoveToWindow:), - view_will_move_to_window as extern "C" fn(&Object, Sel, id), + view_will_move_to_window as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); class.add_method( sel!(updateTrackingAreas:), - update_tracking_areas as extern "C" fn(&Object, Sel, id), + update_tracking_areas as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); - class.add_method(sel!(mouseMoved:), mouse_moved as extern "C" fn(&Object, Sel, id)); - class.add_method(sel!(mouseDragged:), mouse_moved as extern "C" fn(&Object, Sel, id)); - class.add_method(sel!(rightMouseDragged:), mouse_moved as extern "C" fn(&Object, Sel, id)); - class.add_method(sel!(otherMouseDragged:), mouse_moved as extern "C" fn(&Object, Sel, id)); + class.add_method( + sel!(mouseMoved:), + mouse_moved as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), + ); + class.add_method( + sel!(mouseDragged:), + mouse_moved as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), + ); + class.add_method( + sel!(rightMouseDragged:), + mouse_moved as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), + ); + class.add_method( + sel!(otherMouseDragged:), + mouse_moved as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), + ); - class.add_method(sel!(scrollWheel:), scroll_wheel as extern "C" fn(&Object, Sel, id)); + class.add_method( + sel!(scrollWheel:), + scroll_wheel as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), + ); class.add_method( sel!(viewDidChangeBackingProperties:), - view_did_change_backing_properties as extern "C" fn(&Object, Sel, id), + view_did_change_backing_properties + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); class.add_method( sel!(draggingEntered:), - dragging_entered as extern "C" fn(&Object, Sel, id) -> NSUInteger, + dragging_entered + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> NSUInteger, ); class.add_method( sel!(prepareForDragOperation:), - prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + prepare_for_drag_operation + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> ObjcBool, ); class.add_method( sel!(performDragOperation:), - perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + perform_drag_operation + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> ObjcBool, ); class.add_method( sel!(draggingUpdated:), - dragging_updated as extern "C" fn(&Object, Sel, id) -> NSUInteger, + dragging_updated + as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject) -> NSUInteger, + ); + class.add_method( + sel!(draggingExited:), + dragging_exited as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); - class.add_method(sel!(draggingExited:), dragging_exited as extern "C" fn(&Object, Sel, id)); class.add_method( sel!(handleNotification:), - handle_notification as extern "C" fn(&Object, Sel, id), + handle_notification as extern "C-unwind" fn(*const AnyObject, Sel, *mut AnyObject), ); add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left); @@ -224,30 +302,33 @@ unsafe fn create_view_class() -> &'static Class { add_simple_keyboard_class_method!(class, keyUp); add_simple_keyboard_class_method!(class, flagsChanged); - class.add_ivar::<*mut c_void>(BASEVIEW_STATE_IVAR); + let ivar_name = CString::new(BASEVIEW_STATE_IVAR).unwrap(); + class.add_ivar::<*mut c_void>(&ivar_name); class.register() } -extern "C" fn property_yes(_this: &Object, _sel: Sel) -> BOOL { - YES +extern "C-unwind" fn property_yes(_this: *const AnyObject, _sel: Sel) -> ObjcBool { + ObjcBool::YES } -extern "C" fn property_no(_this: &Object, _sel: Sel) -> BOOL { - NO +extern "C-unwind" fn property_no(_this: *const AnyObject, _sel: Sel) -> ObjcBool { + ObjcBool::NO } -extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL { - YES +extern "C-unwind" fn accepts_first_mouse( + _this: *const AnyObject, _sel: Sel, _event: *mut AnyObject, +) -> ObjcBool { + ObjcBool::YES } -extern "C" fn become_first_responder(this: &Object, _sel: Sel) -> BOOL { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn become_first_responder(this: *const AnyObject, _sel: Sel) -> ObjcBool { + let state = unsafe { WindowState::from_view(&*this) }; let is_key_window = unsafe { - let window: id = msg_send![this, window]; - if window != nil { - let is_key_window: BOOL = msg_send![window, isKeyWindow]; - is_key_window == YES + let window: *mut AnyObject = msg_send![this, window]; + if !window.is_null() { + let is_key_window: ObjcBool = msg_send![window, isKeyWindow]; + is_key_window.as_bool() } else { false } @@ -255,47 +336,52 @@ extern "C" fn become_first_responder(this: &Object, _sel: Sel) -> BOOL { if is_key_window { state.trigger_deferrable_event(Event::Window(WindowEvent::Focused)); } - YES + ObjcBool::YES } -extern "C" fn resign_first_responder(this: &Object, _sel: Sel) -> BOOL { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn resign_first_responder(this: *const AnyObject, _sel: Sel) -> ObjcBool { + let state = unsafe { WindowState::from_view(&*this) }; state.trigger_deferrable_event(Event::Window(WindowEvent::Unfocused)); - YES + ObjcBool::YES } -extern "C" fn window_should_close(this: &Object, _: Sel, _sender: id) -> BOOL { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn window_should_close( + this: *const AnyObject, _: Sel, _sender: *mut AnyObject, +) -> ObjcBool { + let state = unsafe { WindowState::from_view(&*this) }; state.trigger_event(Event::Window(WindowEvent::WillClose)); state.window_inner.close(); - NO + ObjcBool::NO } -extern "C" fn dealloc(this: &mut Object, _sel: Sel) { +extern "C-unwind" fn dealloc(this: *mut AnyObject, _sel: Sel) { unsafe { - let class = msg_send![this, class]; + let class: *const AnyClass = msg_send![this, class]; - let superclass = msg_send![this, superclass]; - let () = msg_send![super(this, superclass), dealloc]; + let superclass: &AnyClass = msg_send![this, superclass]; + let () = msg_send![super(&mut *this, superclass), dealloc]; // Delete class - ::objc::runtime::objc_disposeClassPair(class); + objc2::ffi::objc_disposeClassPair(class as *mut _); } } -extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { +extern "C-unwind" fn view_did_change_backing_properties( + this: *const AnyObject, _: Sel, _: *mut AnyObject, +) { unsafe { - let ns_window: *mut Object = msg_send![this, window]; + let ns_window: *mut AnyObject = msg_send![this, window]; let scale_factor: f64 = - if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) }; + if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window as id) }; - let state = WindowState::from_view(this); + let state = WindowState::from_view(&*this); - let bounds: NSRect = msg_send![this, bounds]; + let bounds_raw: CgRect = msg_send![this, bounds]; + let bounds: NSRect = bounds_raw.into(); let new_window_info = WindowInfo::from_logical_size( Size::new(bounds.size.width, bounds.size.height), @@ -319,7 +405,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) { /// https://developer.apple.com/documentation/appkit/nstrackingarea /// https://developer.apple.com/documentation/appkit/nstrackingarea/options /// https://developer.apple.com/documentation/appkit/nstrackingareaoptions -unsafe fn reinit_tracking_area(this: &Object, tracking_area: *mut Object) { +unsafe fn reinit_tracking_area(this: *const AnyObject, tracking_area: *mut AnyObject) { let options: usize = { let mouse_entered_and_exited = 0x01; let tracking_mouse_moved = 0x02; @@ -336,69 +422,85 @@ unsafe fn reinit_tracking_area(this: &Object, tracking_area: *mut Object) { | tracking_enabled_during_mouse_drag }; - let bounds: NSRect = msg_send![this, bounds]; + let bounds_raw: CgRect = msg_send![this, bounds]; - *tracking_area = msg_send![tracking_area, - initWithRect:bounds - options:options - owner:this - userInfo:nil + let _: *mut AnyObject = msg_send![tracking_area, + initWithRect: bounds_raw, + options: options, + owner: this, + userInfo: std::ptr::null_mut::(), ]; } -extern "C" fn view_will_move_to_window(this: &Object, _self: Sel, new_window: id) { +extern "C-unwind" fn view_will_move_to_window( + this: *const AnyObject, _self: Sel, new_window: *mut AnyObject, +) { unsafe { - let tracking_areas: *mut Object = msg_send![this, trackingAreas]; - let tracking_area_count = NSArray::count(tracking_areas); + let tracking_areas: *mut AnyObject = msg_send![this, trackingAreas]; + let tracking_area_count = NSArray::count(tracking_areas as id); - if new_window == nil { + if new_window.is_null() { if tracking_area_count != 0 { - let tracking_area = NSArray::objectAtIndex(tracking_areas, 0); + let tracking_area = NSArray::objectAtIndex(tracking_areas as id, 0); - let _: () = msg_send![this, removeTrackingArea: tracking_area]; - let _: () = msg_send![tracking_area, release]; + let _: () = msg_send![this, removeTrackingArea: tracking_area as *mut AnyObject]; + let _: () = msg_send![tracking_area as *mut AnyObject, release]; } } else { if tracking_area_count == 0 { - let class = Class::get("NSTrackingArea").unwrap(); + let class = + AnyClass::get(CStr::from_bytes_with_nul(b"NSTrackingArea\0").unwrap()).unwrap(); - let tracking_area: *mut Object = msg_send![class, alloc]; + let tracking_area: *mut AnyObject = msg_send![class, alloc]; reinit_tracking_area(this, tracking_area); let _: () = msg_send![this, addTrackingArea: tracking_area]; } - let _: () = msg_send![new_window, setAcceptsMouseMovedEvents: YES]; - let _: () = msg_send![new_window, makeFirstResponder: this]; + let _: () = msg_send![new_window, setAcceptsMouseMovedEvents: ObjcBool::YES]; + let _: ObjcBool = msg_send![new_window, makeFirstResponder: this]; } } unsafe { - let superclass = msg_send![this, superclass]; + let superclass: &AnyClass = msg_send![this, superclass]; - let () = msg_send![super(this, superclass), viewWillMoveToWindow: new_window]; + let () = msg_send![super(&*this, superclass), viewWillMoveToWindow: new_window]; } } -extern "C" fn update_tracking_areas(this: &Object, _self: Sel, _: id) { +extern "C-unwind" fn update_tracking_areas(this: *const AnyObject, _self: Sel, _: *mut AnyObject) { unsafe { - let tracking_areas: *mut Object = msg_send![this, trackingAreas]; - let tracking_area = NSArray::objectAtIndex(tracking_areas, 0); + let tracking_areas: *mut AnyObject = msg_send![this, trackingAreas]; + // Guard against `objectAtIndex:` raising NSRangeException — the + // companion `view_will_move_to_window` site already does this; mirror + // it here so an unwind out of this `extern "C-unwind"` callback can't + // happen if AppKit ever invokes `updateTrackingAreas:` before the + // first tracking area has been installed. + if NSArray::count(tracking_areas as id) == 0 { + return; + } + let tracking_area = NSArray::objectAtIndex(tracking_areas as id, 0); - reinit_tracking_area(this, tracking_area); + reinit_tracking_area(this, tracking_area as *mut AnyObject); } } -extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn mouse_moved(this: *const AnyObject, _sel: Sel, event: *mut AnyObject) { + let state = unsafe { WindowState::from_view(&*this) }; let point: NSPoint = unsafe { - let point = NSEvent::locationInWindow(event); - - msg_send![this, convertPoint:point fromView:nil] + let raw: CgPoint = CgPoint::from(NSEvent::locationInWindow(event as id)); + + let converted: CgPoint = msg_send![ + this, + convertPoint: raw, + fromView: std::ptr::null_mut::(), + ]; + converted.into() }; - let modifiers = unsafe { NSEvent::modifierFlags(event) }; + let modifiers = unsafe { NSEvent::modifierFlags(event as id) }; let position = Point { x: point.x, y: point.y }; @@ -408,21 +510,21 @@ extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) { })); } -extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn scroll_wheel(this: *const AnyObject, _: Sel, event: *mut AnyObject) { + let state = unsafe { WindowState::from_view(&*this) }; let delta = unsafe { - let x = NSEvent::scrollingDeltaX(event) as f32; - let y = NSEvent::scrollingDeltaY(event) as f32; + let x = NSEvent::scrollingDeltaX(event as id) as f32; + let y = NSEvent::scrollingDeltaY(event as id) as f32; - if NSEvent::hasPreciseScrollingDeltas(event) != NO { + if NSEvent::hasPreciseScrollingDeltas(event as id) != NO { ScrollDelta::Pixels { x, y } } else { ScrollDelta::Lines { x, y } } }; - let modifiers = unsafe { NSEvent::modifierFlags(event) }; + let modifiers = unsafe { NSEvent::modifierFlags(event as id) }; state.trigger_event(Event::Mouse(MouseEvent::WheelScrolled { delta, @@ -431,7 +533,7 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) { } fn get_drag_position(sender: id) -> Point { - let point: NSPoint = unsafe { msg_send![sender, draggingLocation] }; + let point: CgPoint = unsafe { msg_send![sender as *mut AnyObject, draggingLocation] }; Point::new(point.x, point.y) } @@ -441,16 +543,17 @@ fn get_drop_data(sender: id) -> DropData { } unsafe { - let pasteboard: id = msg_send![sender, draggingPasteboard]; - let file_list: id = msg_send![pasteboard, propertyListForType: NSFilenamesPboardType]; + let pasteboard: *mut AnyObject = msg_send![sender as *mut AnyObject, draggingPasteboard]; + let pboard_type = NSFilenamesPboardType as *mut AnyObject; + let file_list: *mut AnyObject = msg_send![pasteboard, propertyListForType: pboard_type]; - if file_list == nil { + if file_list.is_null() { return DropData::None; } let mut files = vec![]; - for i in 0..NSArray::count(file_list) { - let data = NSArray::objectAtIndex(file_list, i); + for i in 0..NSArray::count(file_list as id) { + let data = NSArray::objectAtIndex(file_list as id, i); files.push(from_nsstring(data).into()); } @@ -469,13 +572,15 @@ fn on_event(window_state: &WindowState, event: MouseEvent) -> NSUInteger { } } -extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteger { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn dragging_entered( + this: *const AnyObject, _sel: Sel, sender: *mut AnyObject, +) -> NSUInteger { + let state = unsafe { WindowState::from_view(&*this) }; let modifiers = state.keyboard_state().last_mods(); - let drop_data = get_drop_data(sender); + let drop_data = get_drop_data(sender as id); let event = MouseEvent::DragEntered { - position: get_drag_position(sender), + position: get_drag_position(sender as id), modifiers: make_modifiers(modifiers), data: drop_data, }; @@ -483,13 +588,15 @@ extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteg on_event(&state, event) } -extern "C" fn dragging_updated(this: &Object, _sel: Sel, sender: id) -> NSUInteger { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn dragging_updated( + this: *const AnyObject, _sel: Sel, sender: *mut AnyObject, +) -> NSUInteger { + let state = unsafe { WindowState::from_view(&*this) }; let modifiers = state.keyboard_state().last_mods(); - let drop_data = get_drop_data(sender); + let drop_data = get_drop_data(sender as id); let event = MouseEvent::DragMoved { - position: get_drag_position(sender), + position: get_drag_position(sender as id), modifiers: make_modifiers(modifiers), data: drop_data, }; @@ -497,56 +604,62 @@ extern "C" fn dragging_updated(this: &Object, _sel: Sel, sender: id) -> NSUInteg on_event(&state, event) } -extern "C" fn prepare_for_drag_operation(_this: &Object, _sel: Sel, _sender: id) -> BOOL { +extern "C-unwind" fn prepare_for_drag_operation( + _this: *const AnyObject, _sel: Sel, _sender: *mut AnyObject, +) -> ObjcBool { // Always accept drag operation if we get this far // This function won't be called unless dragging_entered/updated // has returned an acceptable operation - YES + ObjcBool::YES } -extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, sender: id) -> BOOL { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn perform_drag_operation( + this: *const AnyObject, _sel: Sel, sender: *mut AnyObject, +) -> ObjcBool { + let state = unsafe { WindowState::from_view(&*this) }; let modifiers = state.keyboard_state().last_mods(); - let drop_data = get_drop_data(sender); + let drop_data = get_drop_data(sender as id); let event = MouseEvent::DragDropped { - position: get_drag_position(sender), + position: get_drag_position(sender as id), modifiers: make_modifiers(modifiers), data: drop_data, }; let event_status = state.trigger_event(Event::Mouse(event)); match event_status { - EventStatus::AcceptDrop(_) => YES, - _ => NO, + EventStatus::AcceptDrop(_) => ObjcBool::YES, + _ => ObjcBool::NO, } } -extern "C" fn dragging_exited(this: &Object, _sel: Sel, _sender: id) { - let state = unsafe { WindowState::from_view(this) }; +extern "C-unwind" fn dragging_exited(this: *const AnyObject, _sel: Sel, _sender: *mut AnyObject) { + let state = unsafe { WindowState::from_view(&*this) }; on_event(&state, MouseEvent::DragLeft); } -extern "C" fn handle_notification(this: &Object, _cmd: Sel, notification: id) { +extern "C-unwind" fn handle_notification( + this: *const AnyObject, _cmd: Sel, notification: *mut AnyObject, +) { unsafe { - let state = WindowState::from_view(this); + let state = WindowState::from_view(&*this); // The subject of the notication, in this case an NSWindow object. - let notification_object: id = msg_send![notification, object]; + let notification_object: *mut AnyObject = msg_send![notification, object]; // The NSWindow object associated with our NSView. - let window: id = msg_send![this, window]; + let window: *mut AnyObject = msg_send![this, window]; - let first_responder: id = msg_send![window, firstResponder]; + let first_responder: *mut AnyObject = msg_send![window, firstResponder]; // Only trigger focus events if the NSWindow that's being notified about is our window, // and if the window's first responder is our NSView. // If the first responder isn't our NSView, the focus events will instead be triggered // by the becomeFirstResponder and resignFirstResponder methods on the NSView itself. - if notification_object == window && first_responder == this as *const Object as id { - let is_key_window: BOOL = msg_send![window, isKeyWindow]; - state.trigger_event(Event::Window(if is_key_window == YES { + if notification_object == window && first_responder == this as *mut AnyObject { + let is_key_window: ObjcBool = msg_send![window, isKeyWindow]; + state.trigger_event(Event::Window(if is_key_window.as_bool() { WindowEvent::Focused } else { WindowEvent::Unfocused diff --git a/src/macos/window.rs b/src/macos/window.rs index 57bca108..79d82464 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -8,14 +8,15 @@ use cocoa::appkit::{ NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSPasteboard, NSView, NSWindow, NSWindowStyleMask, }; -use cocoa::base::{id, nil, BOOL, NO, YES}; +use cocoa::base::{id, nil, NO, YES}; use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; use core_foundation::runloop::{ - CFRunLoop, CFRunLoopTimer, CFRunLoopTimerContext, __CFRunLoopTimer, kCFRunLoopDefaultMode, + __CFRunLoopTimer, kCFRunLoopDefaultMode, CFRunLoop, CFRunLoopTimer, CFRunLoopTimerContext, }; use keyboard_types::KeyboardEvent; -use objc::class; -use objc::{msg_send, runtime::Object, sel, sel_impl}; +use objc2::class; +use objc2::msg_send; +use objc2::runtime::{AnyObject, Bool as ObjcBool}; use raw_window_handle::{ AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, @@ -74,7 +75,9 @@ impl WindowInner { self.open.set(false); unsafe { // Take back ownership of the NSView's Rc - let state_ptr: *const c_void = *(*self.ns_view).get_ivar(BASEVIEW_STATE_IVAR); + let ns_view_any = &*(self.ns_view as *mut AnyObject); + #[allow(deprecated)] + let state_ptr: *const c_void = *ns_view_any.get_ivar(BASEVIEW_STATE_IVAR); let window_state = Rc::from_raw(state_ptr as *mut WindowState); // Cancel the frame timer @@ -83,9 +86,10 @@ impl WindowInner { } // Deregister NSView from NotificationCenter. - let notification_center: id = + let notification_center: *mut AnyObject = msg_send![class!(NSNotificationCenter), defaultCenter]; - let () = msg_send![notification_center, removeObserver:self.ns_view]; + let () = + msg_send![notification_center, removeObserver: self.ns_view as *mut AnyObject]; drop(window_state); @@ -96,7 +100,7 @@ impl WindowInner { // Ensure that the NSView is detached from the parent window self.ns_view.removeFromSuperview(); - let () = msg_send![self.ns_view as id, release]; + let () = msg_send![self.ns_view as *mut AnyObject, release]; // If in non-parented mode, we want to also quit the app altogether let app = self.ns_app.take(); @@ -166,9 +170,12 @@ impl<'a> Window<'a> { let window_handle = Self::init(window_inner, window_info, build); unsafe { - let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; + let _: () = msg_send![ + handle.ns_view as *mut AnyObject, + addSubview: ns_view as *mut AnyObject + ]; - let () = msg_send![pool, drain]; + let () = msg_send![pool as *mut AnyObject, drain]; } window_handle @@ -244,7 +251,7 @@ impl<'a> Window<'a> { ns_window.setContentView_(ns_view); ns_window.setDelegate_(ns_view); - let () = msg_send![pool, drain]; + let () = msg_send![pool as *mut AnyObject, drain]; app.run(); } @@ -273,7 +280,12 @@ impl<'a> Window<'a> { let window_state_ptr = Rc::into_raw(Rc::clone(&window_state)); unsafe { - (*ns_view).set_ivar(BASEVIEW_STATE_IVAR, window_state_ptr as *const c_void); + let ns_view_any = &mut *(ns_view as *mut AnyObject); + #[allow(deprecated)] + { + *ns_view_any.get_mut_ivar::<*const c_void>(BASEVIEW_STATE_IVAR) = + window_state_ptr as *const c_void; + } WindowState::setup_timer(window_state_ptr); } @@ -287,24 +299,24 @@ impl<'a> Window<'a> { pub fn has_focus(&mut self) -> bool { unsafe { - let view = self.inner.ns_view.as_mut().unwrap(); - let window: id = msg_send![view, window]; - if window == nil { + let view = self.inner.ns_view as *mut AnyObject; + let window: *mut AnyObject = msg_send![view, window]; + if window.is_null() { return false; }; - let first_responder: id = msg_send![window, firstResponder]; - let is_key_window: BOOL = msg_send![window, isKeyWindow]; - let is_focused: BOOL = msg_send![view, isEqual: first_responder]; - is_key_window == YES && is_focused == YES + let first_responder: *mut AnyObject = msg_send![window, firstResponder]; + let is_key_window: ObjcBool = msg_send![window, isKeyWindow]; + let is_focused: ObjcBool = msg_send![view, isEqual: first_responder]; + is_key_window.as_bool() && is_focused.as_bool() } } pub fn focus(&mut self) { unsafe { - let view = self.inner.ns_view.as_mut().unwrap(); - let window: id = msg_send![view, window]; - if window != nil { - msg_send![window, makeFirstResponder:view] + let view = self.inner.ns_view as *mut AnyObject; + let window: *mut AnyObject = msg_send![view, window]; + if !window.is_null() { + let _: ObjcBool = msg_send![window, makeFirstResponder: view]; } } } @@ -317,7 +329,7 @@ impl<'a> Window<'a> { unsafe { NSView::setFrameSize(self.inner.ns_view, size) }; unsafe { - let _: () = msg_send![self.inner.ns_view, setNeedsDisplay: YES]; + let _: () = msg_send![self.inner.ns_view as *mut AnyObject, setNeedsDisplay: YES]; } // When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because @@ -372,7 +384,8 @@ impl WindowState { /// This method returns a cloned `Rc` rather than just a `&WindowState`, since the /// original `Rc` owned by the `NSView` can be dropped at any time /// (including during an event handler). - pub(super) unsafe fn from_view(view: &Object) -> Rc { + pub(super) unsafe fn from_view(view: &AnyObject) -> Rc { + #[allow(deprecated)] let state_ptr: *const c_void = *view.get_ivar(BASEVIEW_STATE_IVAR); let state_rc = Rc::from_raw(state_ptr as *const WindowState); @@ -416,8 +429,8 @@ impl WindowState { &self.keyboard_state } - pub(super) fn process_native_key_event(&self, event: *mut Object) -> Option { - self.keyboard_state.process_native_event(event) + pub(super) fn process_native_key_event(&self, event: *mut AnyObject) -> Option { + self.keyboard_state.process_native_event(event as id) } unsafe fn setup_timer(window_state_ptr: *const WindowState) {