diff --git a/Cargo.toml b/Cargo.toml index 8d8a46ae17..6992e90975 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,27 @@ features = [ "Foundation_NSProcessInfo", "Foundation_NSThread", "Foundation_NSNumber", + "AppKit", + "AppKit_NSAppearance", + "AppKit_NSApplication", + "AppKit_NSBitmapImageRep", + "AppKit_NSButton", + "AppKit_NSColor", + "AppKit_NSControl", + "AppKit_NSCursor", + "AppKit_NSEvent", + "AppKit_NSGraphicsContext", + "AppKit_NSImage", + "AppKit_NSImageRep", + "AppKit_NSMenu", + "AppKit_NSMenuItem", + "AppKit_NSPasteboard", + "AppKit_NSResponder", + "AppKit_NSScreen", + "AppKit_NSTextInputContext", + "AppKit_NSView", + "AppKit_NSWindow", + "AppKit_NSWindowTabGroup", ] [target.'cfg(target_os = "ios")'.dependencies.icrate] diff --git a/src/platform/macos.rs b/src/platform/macos.rs index d543c299d8..65cfbe8992 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -1,5 +1,6 @@ use std::os::raw::c_void; +use icrate::Foundation::MainThreadMarker; use objc2::rc::Id; use crate::{ @@ -365,7 +366,9 @@ impl MonitorHandleExtMacOS for MonitorHandle { } fn ns_screen(&self) -> Option<*mut c_void> { - self.inner.ns_screen().map(|s| Id::as_ptr(&s) as _) + // SAFETY: We only use the marker to get a pointer + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + self.inner.ns_screen(mtm).map(|s| Id::as_ptr(&s) as _) } } diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index b1e1fea33d..a360bfa334 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -1,9 +1,16 @@ #![allow(clippy::unnecessary_cast)] +use icrate::AppKit::{ + NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown, + NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved, + NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp, + NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder, +}; use icrate::Foundation::NSObject; use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass}; -use super::appkit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder}; +use super::appkit::NSApplication; +use super::event::flags_contains; use super::{app_state::AppState, DEVICE_ID}; use crate::event::{DeviceEvent, ElementState, Event}; @@ -13,7 +20,7 @@ declare_class!( unsafe impl ClassType for WinitApplication { #[inherits(NSResponder, NSObject)] type Super = NSApplication; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "WinitApplication"; } @@ -28,10 +35,10 @@ declare_class!( // 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.type_(); - let modifier_flags = event.modifierFlags(); - if event_type == NSEventType::NSKeyUp - && modifier_flags.contains(NSEventModifierFlags::NSCommandKeyMask) + let event_type = unsafe { event.r#type() }; + let modifier_flags = unsafe { event.modifierFlags() }; + if event_type == NSEventTypeKeyUp + && flags_contains(modifier_flags, NSEventModifierFlagCommand) { if let Some(key_window) = self.keyWindow() { unsafe { key_window.sendEvent(event) }; @@ -45,14 +52,15 @@ declare_class!( ); fn maybe_dispatch_device_event(event: &NSEvent) { - let event_type = event.type_(); + let event_type = unsafe { event.r#type() }; + #[allow(non_upper_case_globals)] match event_type { - NSEventType::NSMouseMoved - | NSEventType::NSLeftMouseDragged - | NSEventType::NSOtherMouseDragged - | NSEventType::NSRightMouseDragged => { - let delta_x = event.deltaX() as f64; - let delta_y = event.deltaY() as f64; + NSEventTypeMouseMoved + | NSEventTypeLeftMouseDragged + | NSEventTypeOtherMouseDragged + | NSEventTypeRightMouseDragged => { + let delta_x = unsafe { event.deltaX() } as f64; + let delta_y = unsafe { event.deltaY() } as f64; if delta_x != 0.0 { queue_device_event(DeviceEvent::Motion { @@ -74,17 +82,15 @@ fn maybe_dispatch_device_event(event: &NSEvent) { }); } } - NSEventType::NSLeftMouseDown - | NSEventType::NSRightMouseDown - | NSEventType::NSOtherMouseDown => { + NSEventTypeLeftMouseDown | NSEventTypeRightMouseDown | NSEventTypeOtherMouseDown => { queue_device_event(DeviceEvent::Button { - button: event.buttonNumber() as u32, + button: unsafe { event.buttonNumber() } as u32, state: ElementState::Pressed, }); } - NSEventType::NSLeftMouseUp | NSEventType::NSRightMouseUp | NSEventType::NSOtherMouseUp => { + NSEventTypeLeftMouseUp | NSEventTypeRightMouseUp | NSEventTypeOtherMouseUp => { queue_device_event(DeviceEvent::Button { - button: event.buttonNumber() as u32, + button: unsafe { event.buttonNumber() } as u32, state: ElementState::Released, }); } diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 33cc1d88e6..9b8aae05e2 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -12,13 +12,14 @@ use std::{ }; use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp}; -use icrate::Foundation::{is_main_thread, NSSize}; +use icrate::Foundation::{is_main_thread, MainThreadMarker, NSSize}; use objc2::rc::{autoreleasepool, Id}; use once_cell::sync::Lazy; -use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent}; +use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy}; use super::{ - event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow, + event::dummy_event, event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, + window::WinitWindow, }; use crate::{ dpi::PhysicalSize, @@ -459,6 +460,7 @@ impl AppState { create_default_menu: bool, activate_ignoring_other_apps: bool, ) { + let mtm = MainThreadMarker::new().unwrap(); let app = NSApp(); // We need to delay setting the activation policy and activating the app // until `applicationDidFinishLaunching` has been called. Otherwise the @@ -473,7 +475,7 @@ impl AppState { if create_default_menu { // The menubar initialization should be before the `NewEvents` event, to allow // overriding of the default menu even if it's created - menu::initialize(); + menu::initialize(mtm); } Self::start_running(); @@ -593,7 +595,7 @@ impl AppState { autoreleasepool(|_| { app.stop(None); // To stop event loop immediately, we need to post some event here. - app.postEvent_atStart(&NSEvent::dummy(), true); + app.postEvent_atStart(&dummy_event().unwrap(), true); }); } diff --git a/src/platform_impl/macos/appkit/appearance.rs b/src/platform_impl/macos/appkit/appearance.rs deleted file mode 100644 index 6db7c6d039..0000000000 --- a/src/platform_impl/macos/appkit/appearance.rs +++ /dev/null @@ -1,28 +0,0 @@ -use icrate::Foundation::{NSArray, NSObject, NSString}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSAppearance; - - unsafe impl ClassType for NSAppearance { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -type NSAppearanceName = NSString; - -extern_methods!( - unsafe impl NSAppearance { - #[method_id(appearanceNamed:)] - pub fn appearanceNamed(name: &NSAppearanceName) -> Id; - - #[method_id(bestMatchFromAppearancesWithNames:)] - pub fn bestMatchFromAppearancesWithNames( - &self, - appearances: &NSArray, - ) -> Id; - } -); diff --git a/src/platform_impl/macos/appkit/application.rs b/src/platform_impl/macos/appkit/application.rs index 22d166f261..241e606a30 100644 --- a/src/platform_impl/macos/appkit/application.rs +++ b/src/platform_impl/macos/appkit/application.rs @@ -1,10 +1,11 @@ +use icrate::AppKit::{NSAppearance, NSEvent, NSMenu, NSResponder}; use icrate::Foundation::{MainThreadMarker, NSArray, NSInteger, NSObject, NSUInteger}; use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType}; use objc2::{Encode, Encoding}; -use super::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow}; +use super::NSWindow; extern_class!( #[derive(Debug, PartialEq, Eq, Hash)] @@ -13,7 +14,7 @@ extern_class!( unsafe impl ClassType for NSApplication { #[inherits(NSObject)] type Super = NSResponder; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; } ); diff --git a/src/platform_impl/macos/appkit/bitmap_image_rep.rs b/src/platform_impl/macos/appkit/bitmap_image_rep.rs deleted file mode 100644 index aa6e48486e..0000000000 --- a/src/platform_impl/macos/appkit/bitmap_image_rep.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::ffi::c_uchar; - -use icrate::Foundation::{NSInteger, NSObject, NSString}; -use objc2::rc::Id; -use objc2::runtime::Bool; -use objc2::{extern_class, extern_methods, msg_send, msg_send_id, mutability, ClassType}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub struct NSImageRep; - - unsafe impl ClassType for NSImageRep { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern "C" { - static NSDeviceRGBColorSpace: &'static NSString; -} - -extern_class!( - // - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSBitmapImageRep; - - unsafe impl ClassType for NSBitmapImageRep { - type Super = NSImageRep; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSBitmapImageRep { - pub fn init_rgba(width: NSInteger, height: NSInteger) -> Id { - unsafe { - msg_send_id![Self::alloc(), - initWithBitmapDataPlanes: std::ptr::null_mut::<*mut c_uchar>(), - pixelsWide: width, - pixelsHigh: height, - bitsPerSample: 8 as NSInteger, - samplesPerPixel: 4 as NSInteger, - hasAlpha: Bool::new(true), - isPlanar: Bool::new(false), - colorSpaceName: NSDeviceRGBColorSpace, - bytesPerRow: width * 4, - bitsPerPixel: 32 as NSInteger, - ] - } - } - - pub fn bitmap_data(&self) -> *mut u8 { - unsafe { msg_send![self, bitmapData] } - } - } -); diff --git a/src/platform_impl/macos/appkit/button.rs b/src/platform_impl/macos/appkit/button.rs deleted file mode 100644 index eb3c57a543..0000000000 --- a/src/platform_impl/macos/appkit/button.rs +++ /dev/null @@ -1,15 +0,0 @@ -use icrate::Foundation::NSObject; -use objc2::{extern_class, mutability, ClassType}; - -use super::{NSControl, NSResponder, NSView}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSButton; - - unsafe impl ClassType for NSButton { - #[inherits(NSView, NSResponder, NSObject)] - type Super = NSControl; - type Mutability = mutability::InteriorMutable; - } -); diff --git a/src/platform_impl/macos/appkit/color.rs b/src/platform_impl/macos/appkit/color.rs deleted file mode 100644 index b5477a1eb9..0000000000 --- a/src/platform_impl/macos/appkit/color.rs +++ /dev/null @@ -1,28 +0,0 @@ -use icrate::Foundation::NSObject; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -extern_class!( - /// An object that stores color data and sometimes opacity (alpha value). - /// - /// - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSColor; - - unsafe impl ClassType for NSColor { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// SAFETY: Documentation clearly states: -// > Color objects are immutable and thread-safe -unsafe impl Send for NSColor {} -unsafe impl Sync for NSColor {} - -extern_methods!( - unsafe impl NSColor { - #[method_id(clearColor)] - pub fn clear() -> Id; - } -); diff --git a/src/platform_impl/macos/appkit/control.rs b/src/platform_impl/macos/appkit/control.rs deleted file mode 100644 index d38f4b1096..0000000000 --- a/src/platform_impl/macos/appkit/control.rs +++ /dev/null @@ -1,25 +0,0 @@ -use icrate::Foundation::NSObject; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::{NSResponder, NSView}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSControl; - - unsafe impl ClassType for NSControl { - #[inherits(NSResponder, NSObject)] - type Super = NSView; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSControl { - #[method(setEnabled:)] - pub fn setEnabled(&self, enabled: bool); - - #[method(isEnabled)] - pub fn isEnabled(&self) -> bool; - } -); diff --git a/src/platform_impl/macos/appkit/cursor.rs b/src/platform_impl/macos/appkit/cursor.rs deleted file mode 100644 index 0a2a5e3260..0000000000 --- a/src/platform_impl/macos/appkit/cursor.rs +++ /dev/null @@ -1,259 +0,0 @@ -use once_cell::sync::Lazy; - -use icrate::Foundation::{ - ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize, - NSString, -}; -use objc2::rc::{DefaultId, Id}; -use objc2::runtime::Sel; -use objc2::{extern_class, extern_methods, msg_send_id, mutability, sel, ClassType}; - -use super::{NSBitmapImageRep, NSImage}; -use crate::cursor::CursorImage; -use crate::window::CursorIcon; - -extern_class!( - /// - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSCursor; - - unsafe impl ClassType for NSCursor { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// SAFETY: NSCursor is immutable, stated here: -// https://developer.apple.com/documentation/appkit/nscursor/1527062-image?language=objc -unsafe impl Send for NSCursor {} -unsafe impl Sync for NSCursor {} - -macro_rules! def_cursor { - {$( - $(#[$($m:meta)*])* - pub fn $name:ident(); - )*} => {$( - $(#[$($m)*])* - pub fn $name() -> Id { - unsafe { msg_send_id![Self::class(), $name] } - } - )*}; -} - -macro_rules! def_undocumented_cursor { - {$( - $(#[$($m:meta)*])* - pub fn $name:ident(); - )*} => {$( - $(#[$($m)*])* - pub fn $name() -> Id { - unsafe { Self::from_selector(sel!($name)).unwrap_or_else(|| Default::default()) } - } - )*}; -} - -extern_methods!( - /// Documented cursors - unsafe impl NSCursor { - def_cursor!( - pub fn arrowCursor(); - pub fn pointingHandCursor(); - pub fn openHandCursor(); - pub fn closedHandCursor(); - pub fn IBeamCursor(); - pub fn IBeamCursorForVerticalLayout(); - pub fn dragCopyCursor(); - pub fn dragLinkCursor(); - pub fn operationNotAllowedCursor(); - pub fn contextualMenuCursor(); - pub fn crosshairCursor(); - pub fn resizeRightCursor(); - pub fn resizeUpCursor(); - pub fn resizeLeftCursor(); - pub fn resizeDownCursor(); - pub fn resizeLeftRightCursor(); - pub fn resizeUpDownCursor(); - ); - - // Creating cursors should be thread-safe, though using them for anything probably isn't. - pub fn new(image: &NSImage, hotSpot: NSPoint) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithImage: image, hotSpot: hotSpot] } - } - - pub fn invisible() -> Id { - // 16x16 GIF data for invisible cursor - // You can reproduce this via ImageMagick. - // $ convert -size 16x16 xc:none cursor.gif - static CURSOR_BYTES: &[u8] = &[ - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, - 0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B, - ]; - - static CURSOR: Lazy> = Lazy::new(|| { - // TODO: Consider using `dataWithBytesNoCopy:` - let data = NSData::with_bytes(CURSOR_BYTES); - let image = NSImage::new_with_data(&data); - NSCursor::new(&image, NSPoint::new(0.0, 0.0)) - }); - - CURSOR.clone() - } - } - - /// Undocumented cursors - unsafe impl NSCursor { - #[method(respondsToSelector:)] - fn class_responds_to(sel: Sel) -> bool; - - #[method_id(performSelector:)] - unsafe fn from_selector_unchecked(sel: Sel) -> Id; - - unsafe fn from_selector(sel: Sel) -> Option> { - if Self::class_responds_to(sel) { - Some(unsafe { Self::from_selector_unchecked(sel) }) - } else { - warn!("Cursor `{:?}` appears to be invalid", sel); - None - } - } - - def_undocumented_cursor!( - // Undocumented cursors: https://stackoverflow.com/a/46635398/5435443 - pub fn _helpCursor(); - pub fn _zoomInCursor(); - pub fn _zoomOutCursor(); - pub fn _windowResizeNorthEastCursor(); - pub fn _windowResizeNorthWestCursor(); - pub fn _windowResizeSouthEastCursor(); - pub fn _windowResizeSouthWestCursor(); - pub fn _windowResizeNorthEastSouthWestCursor(); - pub fn _windowResizeNorthWestSouthEastCursor(); - - // While these two are available, the former just loads a white arrow, - // and the latter loads an ugly deflated beachball! - // pub fn _moveCursor(); - // pub fn _waitCursor(); - - // An even more undocumented cursor... - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349 - pub fn busyButClickableCursor(); - ); - } - - /// Webkit cursors - unsafe impl NSCursor { - // Note that loading `busybutclickable` with this code won't animate - // the frames; instead you'll just get them all in a column. - unsafe fn load_webkit_cursor(name: &NSString) -> Id { - // Snatch a cursor from WebKit; They fit the style of the native - // cursors, and will seem completely standard to macOS users. - // - // https://stackoverflow.com/a/21786835/5435443 - let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors"); - let cursor_path = root.stringByAppendingPathComponent(name); - - let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf")); - let image = NSImage::new_by_referencing_file(&pdf_path); - - // TODO: Handle PLists better - let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist")); - let info: Id> = unsafe { - msg_send_id![ - >::class(), - dictionaryWithContentsOfFile: &*info_path, - ] - }; - let mut x = 0.0; - if let Some(n) = info.get(&*ns_string!("hotx")) { - if n.is_kind_of::() { - let ptr: *const NSObject = n; - let ptr: *const NSNumber = ptr.cast(); - x = unsafe { &*ptr }.as_cgfloat() - } - } - let mut y = 0.0; - if let Some(n) = info.get(&*ns_string!("hotx")) { - if n.is_kind_of::() { - let ptr: *const NSObject = n; - let ptr: *const NSNumber = ptr.cast(); - y = unsafe { &*ptr }.as_cgfloat() - } - } - - let hotspot = NSPoint::new(x, y); - Self::new(&image, hotspot) - } - - pub fn moveCursor() -> Id { - unsafe { Self::load_webkit_cursor(ns_string!("move")) } - } - - pub fn cellCursor() -> Id { - unsafe { Self::load_webkit_cursor(ns_string!("cell")) } - } - } -); - -impl NSCursor { - pub fn from_icon(icon: CursorIcon) -> Id { - match icon { - CursorIcon::Default => Default::default(), - CursorIcon::Pointer => Self::pointingHandCursor(), - CursorIcon::Grab => Self::openHandCursor(), - CursorIcon::Grabbing => Self::closedHandCursor(), - CursorIcon::Text => Self::IBeamCursor(), - CursorIcon::VerticalText => Self::IBeamCursorForVerticalLayout(), - CursorIcon::Copy => Self::dragCopyCursor(), - CursorIcon::Alias => Self::dragLinkCursor(), - CursorIcon::NotAllowed | CursorIcon::NoDrop => Self::operationNotAllowedCursor(), - CursorIcon::ContextMenu => Self::contextualMenuCursor(), - CursorIcon::Crosshair => Self::crosshairCursor(), - CursorIcon::EResize => Self::resizeRightCursor(), - CursorIcon::NResize => Self::resizeUpCursor(), - CursorIcon::WResize => Self::resizeLeftCursor(), - CursorIcon::SResize => Self::resizeDownCursor(), - CursorIcon::EwResize | CursorIcon::ColResize => Self::resizeLeftRightCursor(), - CursorIcon::NsResize | CursorIcon::RowResize => Self::resizeUpDownCursor(), - CursorIcon::Help => Self::_helpCursor(), - CursorIcon::ZoomIn => Self::_zoomInCursor(), - CursorIcon::ZoomOut => Self::_zoomOutCursor(), - CursorIcon::NeResize => Self::_windowResizeNorthEastCursor(), - CursorIcon::NwResize => Self::_windowResizeNorthWestCursor(), - CursorIcon::SeResize => Self::_windowResizeSouthEastCursor(), - CursorIcon::SwResize => Self::_windowResizeSouthWestCursor(), - CursorIcon::NeswResize => Self::_windowResizeNorthEastSouthWestCursor(), - CursorIcon::NwseResize => Self::_windowResizeNorthWestSouthEastCursor(), - // This is the wrong semantics for `Wait`, but it's the same as - // what's used in Safari and Chrome. - CursorIcon::Wait | CursorIcon::Progress => Self::busyButClickableCursor(), - CursorIcon::Move | CursorIcon::AllScroll => Self::moveCursor(), - CursorIcon::Cell => Self::cellCursor(), - _ => Default::default(), - } - } - - pub fn from_image(cursor: &CursorImage) -> Id { - let width = cursor.width; - let height = cursor.height; - - let bitmap = NSBitmapImageRep::init_rgba(width as isize, height as isize); - let bitmap_data = - unsafe { std::slice::from_raw_parts_mut(bitmap.bitmap_data(), cursor.rgba.len()) }; - bitmap_data.copy_from_slice(&cursor.rgba); - - let image = NSImage::init_with_size(NSSize::new(width.into(), height.into())); - image.add_representation(&bitmap); - - let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64); - - NSCursor::new(&image, hotspot) - } -} - -impl DefaultId for NSCursor { - fn default_id() -> Id { - Self::arrowCursor() - } -} diff --git a/src/platform_impl/macos/appkit/event.rs b/src/platform_impl/macos/appkit/event.rs deleted file mode 100644 index f749639904..0000000000 --- a/src/platform_impl/macos/appkit/event.rs +++ /dev/null @@ -1,308 +0,0 @@ -use std::os::raw::c_ushort; - -use icrate::Foundation::{ - CGFloat, NSCopying, NSInteger, NSObject, NSPoint, NSString, NSTimeInterval, NSUInteger, -}; -use objc2::encode::{Encode, Encoding}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSEvent; - - unsafe impl ClassType for NSEvent { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// > Safely handled only on the same thread, whether that be the main thread -// > or a secondary thread; otherwise you run the risk of having events get -// > out of sequence. -// -// - -extern_methods!( - unsafe impl NSEvent { - #[method_id( - otherEventWithType: - location: - modifierFlags: - timestamp: - windowNumber: - context: - subtype: - data1: - data2: - )] - unsafe fn otherEventWithType( - type_: NSEventType, - location: NSPoint, - flags: NSEventModifierFlags, - time: NSTimeInterval, - window_num: NSInteger, - context: Option<&NSObject>, // NSGraphicsContext - subtype: NSEventSubtype, - data1: NSInteger, - data2: NSInteger, - ) -> Id; - - pub fn dummy() -> Id { - unsafe { - Self::otherEventWithType( - NSEventType::NSApplicationDefined, - NSPoint::new(0.0, 0.0), - NSEventModifierFlags::empty(), - 0.0, - 0, - None, - NSEventSubtype::NSWindowExposedEventType, - 0, - 0, - ) - } - } - - #[method_id( - keyEventWithType: - location: - modifierFlags: - timestamp: - windowNumber: - context: - characters: - charactersIgnoringModifiers: - isARepeat: - keyCode: - )] - pub fn keyEventWithType( - type_: NSEventType, - location: NSPoint, - modifier_flags: NSEventModifierFlags, - timestamp: NSTimeInterval, - window_num: NSInteger, - context: Option<&NSObject>, - characters: &NSString, - characters_ignoring_modifiers: &NSString, - is_a_repeat: bool, - scancode: c_ushort, - ) -> Id; - - #[method(locationInWindow)] - pub fn locationInWindow(&self) -> NSPoint; - - // TODO: MainThreadMarker - #[method(pressedMouseButtons)] - pub fn pressedMouseButtons() -> NSUInteger; - - #[method(modifierFlags)] - pub fn modifierFlags(&self) -> NSEventModifierFlags; - - #[method(type)] - pub fn type_(&self) -> NSEventType; - - #[method(keyCode)] - pub fn key_code(&self) -> c_ushort; - - #[method(magnification)] - pub fn magnification(&self) -> CGFloat; - - #[method(phase)] - pub fn phase(&self) -> NSEventPhase; - - #[method(momentumPhase)] - pub fn momentumPhase(&self) -> NSEventPhase; - - #[method(deltaX)] - pub fn deltaX(&self) -> CGFloat; - - #[method(deltaY)] - pub fn deltaY(&self) -> CGFloat; - - #[method(buttonNumber)] - pub fn buttonNumber(&self) -> NSInteger; - - #[method(scrollingDeltaX)] - pub fn scrollingDeltaX(&self) -> CGFloat; - - #[method(scrollingDeltaY)] - pub fn scrollingDeltaY(&self) -> CGFloat; - - #[method(hasPreciseScrollingDeltas)] - pub fn hasPreciseScrollingDeltas(&self) -> bool; - - #[method(rotation)] - pub fn rotation(&self) -> f32; - - #[method(pressure)] - pub fn pressure(&self) -> f32; - - #[method(stage)] - pub fn stage(&self) -> NSInteger; - - #[method(isARepeat)] - pub fn is_a_repeat(&self) -> bool; - - #[method(windowNumber)] - pub fn window_number(&self) -> NSInteger; - - #[method(timestamp)] - pub fn timestamp(&self) -> NSTimeInterval; - - #[method_id(characters)] - pub fn characters(&self) -> Option>; - - #[method_id(charactersIgnoringModifiers)] - pub fn charactersIgnoringModifiers(&self) -> Option>; - - pub fn lshift_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICELSHIFTKEYMASK != 0 - } - - pub fn rshift_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICERSHIFTKEYMASK != 0 - } - - pub fn lctrl_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICELCTLKEYMASK != 0 - } - - pub fn rctrl_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICERCTLKEYMASK != 0 - } - - pub fn lalt_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICELALTKEYMASK != 0 - } - - pub fn ralt_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICERALTKEYMASK != 0 - } - - pub fn lcmd_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICELCMDKEYMASK != 0 - } - - pub fn rcmd_pressed(&self) -> bool { - let raw_modifiers = self.modifierFlags().bits() as u32; - raw_modifiers & NX_DEVICERCMDKEYMASK != 0 - } - } -); - -unsafe impl NSCopying for NSEvent {} - -// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259 -const NX_DEVICELCTLKEYMASK: u32 = 0x00000001; -const NX_DEVICELSHIFTKEYMASK: u32 = 0x00000002; -const NX_DEVICERSHIFTKEYMASK: u32 = 0x00000004; -const NX_DEVICELCMDKEYMASK: u32 = 0x00000008; -const NX_DEVICERCMDKEYMASK: u32 = 0x00000010; -const NX_DEVICELALTKEYMASK: u32 = 0x00000020; -const NX_DEVICERALTKEYMASK: u32 = 0x00000040; -const NX_DEVICERCTLKEYMASK: u32 = 0x00002000; - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct NSEventModifierFlags: NSUInteger { - const NSAlphaShiftKeyMask = 1 << 16; - const NSShiftKeyMask = 1 << 17; - const NSControlKeyMask = 1 << 18; - const NSAlternateKeyMask = 1 << 19; - const NSCommandKeyMask = 1 << 20; - const NSNumericPadKeyMask = 1 << 21; - const NSHelpKeyMask = 1 << 22; - const NSFunctionKeyMask = 1 << 23; - const NSDeviceIndependentModifierFlagsMask = 0xffff0000; - } -} - -unsafe impl Encode for NSEventModifierFlags { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct NSEventPhase: NSUInteger { - const NSEventPhaseNone = 0; - const NSEventPhaseBegan = 0x1 << 0; - const NSEventPhaseStationary = 0x1 << 1; - const NSEventPhaseChanged = 0x1 << 2; - const NSEventPhaseEnded = 0x1 << 3; - const NSEventPhaseCancelled = 0x1 << 4; - const NSEventPhaseMayBegin = 0x1 << 5; - } -} - -unsafe impl Encode for NSEventPhase { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(i16)] // short -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSEventSubtype { - // TODO: Not sure what these values are - // NSMouseEventSubtype = NX_SUBTYPE_DEFAULT, - // NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT, - // NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY - // NSTouchEventSubtype = NX_SUBTYPE_MOUSE_TOUCH, - NSWindowExposedEventType = 0, - NSApplicationActivatedEventType = 1, - NSApplicationDeactivatedEventType = 2, - NSWindowMovedEventType = 4, - NSScreenChangedEventType = 8, - NSAWTEventType = 16, -} - -unsafe impl Encode for NSEventSubtype { - const ENCODING: Encoding = i16::ENCODING; -} - -#[allow(dead_code)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(usize)] // NSUInteger -pub enum NSEventType { - NSLeftMouseDown = 1, - NSLeftMouseUp = 2, - NSRightMouseDown = 3, - NSRightMouseUp = 4, - NSMouseMoved = 5, - NSLeftMouseDragged = 6, - NSRightMouseDragged = 7, - NSMouseEntered = 8, - NSMouseExited = 9, - NSKeyDown = 10, - NSKeyUp = 11, - NSFlagsChanged = 12, - NSAppKitDefined = 13, - NSSystemDefined = 14, - NSApplicationDefined = 15, - NSPeriodic = 16, - NSCursorUpdate = 17, - NSScrollWheel = 22, - NSTabletPoint = 23, - NSTabletProximity = 24, - NSOtherMouseDown = 25, - NSOtherMouseUp = 26, - NSOtherMouseDragged = 27, - NSEventTypeGesture = 29, - NSEventTypeMagnify = 30, - NSEventTypeSwipe = 31, - NSEventTypeRotate = 18, - NSEventTypeBeginGesture = 19, - NSEventTypeEndGesture = 20, - NSEventTypePressure = 34, -} - -unsafe impl Encode for NSEventType { - const ENCODING: Encoding = NSUInteger::ENCODING; -} diff --git a/src/platform_impl/macos/appkit/image.rs b/src/platform_impl/macos/appkit/image.rs deleted file mode 100644 index d108eb1f09..0000000000 --- a/src/platform_impl/macos/appkit/image.rs +++ /dev/null @@ -1,46 +0,0 @@ -use icrate::Foundation::{NSData, NSObject, NSSize, NSString}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, msg_send, msg_send_id, mutability, ClassType}; - -use super::NSBitmapImageRep; - -extern_class!( - // TODO: Can this be mutable? - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSImage; - - unsafe impl ClassType for NSImage { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// Documented Thread-Unsafe, but: -// > One thread can create an NSImage object, draw to the image buffer, -// > and pass it off to the main thread for drawing. The underlying image -// > cache is shared among all threads. -// -// -// So really only unsafe to mutate on several threads. -unsafe impl Send for NSImage {} -unsafe impl Sync for NSImage {} - -extern_methods!( - unsafe impl NSImage { - pub fn new_by_referencing_file(path: &NSString) -> Id { - unsafe { msg_send_id![Self::alloc(), initByReferencingFile: path] } - } - - pub fn new_with_data(data: &NSData) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithData: data] } - } - - pub fn init_with_size(size: NSSize) -> Id { - unsafe { msg_send_id![Self::alloc(), initWithSize: size] } - } - - pub fn add_representation(&self, representation: &NSBitmapImageRep) { - unsafe { msg_send![self, addRepresentation: representation] } - } - } -); diff --git a/src/platform_impl/macos/appkit/menu.rs b/src/platform_impl/macos/appkit/menu.rs deleted file mode 100644 index e03ec95945..0000000000 --- a/src/platform_impl/macos/appkit/menu.rs +++ /dev/null @@ -1,25 +0,0 @@ -use icrate::Foundation::NSObject; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::NSMenuItem; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSMenu; - - unsafe impl ClassType for NSMenu { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSMenu { - #[method_id(new)] - pub fn new() -> Id; - - #[method(addItem:)] - pub fn addItem(&self, item: &NSMenuItem); - } -); diff --git a/src/platform_impl/macos/appkit/menu_item.rs b/src/platform_impl/macos/appkit/menu_item.rs deleted file mode 100644 index c406529139..0000000000 --- a/src/platform_impl/macos/appkit/menu_item.rs +++ /dev/null @@ -1,47 +0,0 @@ -use icrate::Foundation::{NSObject, NSString}; -use objc2::rc::Id; -use objc2::runtime::Sel; -use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType}; - -use super::{NSEventModifierFlags, NSMenu}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSMenuItem; - - unsafe impl ClassType for NSMenuItem { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSMenuItem { - #[method_id(new)] - pub fn new() -> Id; - - pub fn newWithTitle( - title: &NSString, - action: Option, - key_equivalent: &NSString, - ) -> Id { - unsafe { - msg_send_id![ - Self::alloc(), - initWithTitle: title, - action: action, - keyEquivalent: key_equivalent, - ] - } - } - - #[method_id(separatorItem)] - pub fn separatorItem() -> Id; - - #[method(setKeyEquivalentModifierMask:)] - pub fn setKeyEquivalentModifierMask(&self, mask: NSEventModifierFlags); - - #[method(setSubmenu:)] - pub fn setSubmenu(&self, submenu: &NSMenu); - } -); diff --git a/src/platform_impl/macos/appkit/mod.rs b/src/platform_impl/macos/appkit/mod.rs index 8c6eb12ada..715bfa98f6 100644 --- a/src/platform_impl/macos/appkit/mod.rs +++ b/src/platform_impl/macos/appkit/mod.rs @@ -11,57 +11,18 @@ #![allow(clippy::enum_variant_names)] #![allow(non_upper_case_globals)] -mod appearance; mod application; -mod bitmap_image_rep; -mod button; -mod color; -mod control; -mod cursor; -mod event; -mod image; -mod menu; -mod menu_item; -mod pasteboard; -mod responder; -mod screen; mod tab_group; -mod text_input_client; -mod text_input_context; -mod version; -mod view; mod window; -pub(crate) use self::appearance::NSAppearance; pub(crate) use self::application::{ NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions, NSRequestUserAttentionType, }; -pub(crate) use self::bitmap_image_rep::NSBitmapImageRep; -pub(crate) use self::button::NSButton; -pub(crate) use self::color::NSColor; -pub(crate) use self::control::NSControl; -pub(crate) use self::cursor::NSCursor; -#[allow(unused_imports)] -pub(crate) use self::event::{ - NSEvent, NSEventModifierFlags, NSEventPhase, NSEventSubtype, NSEventType, -}; -pub(crate) use self::image::NSImage; -pub(crate) use self::menu::NSMenu; -pub(crate) use self::menu_item::NSMenuItem; -pub(crate) use self::pasteboard::{NSFilenamesPboardType, NSPasteboard, NSPasteboardType}; -pub(crate) use self::responder::NSResponder; -#[allow(unused_imports)] -pub(crate) use self::screen::{NSDeviceDescriptionKey, NSScreen}; pub(crate) use self::tab_group::NSWindowTabGroup; -pub(crate) use self::text_input_client::NSTextInputClient; -pub(crate) use self::text_input_context::NSTextInputContext; -pub(crate) use self::version::NSAppKitVersion; -pub(crate) use self::view::{NSTrackingRectTag, NSView}; pub(crate) use self::window::{ NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState, - NSWindowOrderingMode, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, - NSWindowTitleVisibility, + NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, }; #[link(name = "AppKit", kind = "framework")] diff --git a/src/platform_impl/macos/appkit/pasteboard.rs b/src/platform_impl/macos/appkit/pasteboard.rs deleted file mode 100644 index 01a9408983..0000000000 --- a/src/platform_impl/macos/appkit/pasteboard.rs +++ /dev/null @@ -1,26 +0,0 @@ -use icrate::Foundation::{NSObject, NSString}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSPasteboard; - - unsafe impl ClassType for NSPasteboard { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSPasteboard { - #[method_id(propertyListForType:)] - pub fn propertyListForType(&self, type_: &NSPasteboardType) -> Id; - } -); - -pub type NSPasteboardType = NSString; - -extern "C" { - pub static NSFilenamesPboardType: &'static NSPasteboardType; -} diff --git a/src/platform_impl/macos/appkit/responder.rs b/src/platform_impl/macos/appkit/responder.rs deleted file mode 100644 index 11f3bddf06..0000000000 --- a/src/platform_impl/macos/appkit/responder.rs +++ /dev/null @@ -1,23 +0,0 @@ -use icrate::Foundation::{NSArray, NSObject}; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::NSEvent; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub struct NSResponder; - - unsafe impl ClassType for NSResponder { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// Documented as "Thread-Unsafe". - -extern_methods!( - unsafe impl NSResponder { - #[method(interpretKeyEvents:)] - pub(crate) unsafe fn interpretKeyEvents(&self, events: &NSArray); - } -); diff --git a/src/platform_impl/macos/appkit/screen.rs b/src/platform_impl/macos/appkit/screen.rs deleted file mode 100644 index 732f06e35d..0000000000 --- a/src/platform_impl/macos/appkit/screen.rs +++ /dev/null @@ -1,66 +0,0 @@ -use icrate::Foundation::{ - ns_string, CGFloat, NSArray, NSDictionary, NSNumber, NSObject, NSRect, NSString, -}; -use objc2::rc::Id; -use objc2::runtime::AnyObject; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSScreen; - - unsafe impl ClassType for NSScreen { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -// TODO: Main thread marker! - -extern_methods!( - unsafe impl NSScreen { - /// The application object must have been created. - #[method_id(mainScreen)] - pub fn main() -> Option>; - - /// The application object must have been created. - #[method_id(screens)] - pub fn screens() -> Id>; - - #[method(frame)] - pub fn frame(&self) -> NSRect; - - #[method(visibleFrame)] - pub fn visibleFrame(&self) -> NSRect; - - #[method_id(deviceDescription)] - pub fn deviceDescription(&self) -> Id>; - - pub fn display_id(&self) -> u32 { - let key = ns_string!("NSScreenNumber"); - - objc2::rc::autoreleasepool(|_| { - let device_description = self.deviceDescription(); - - // Retrieve the CGDirectDisplayID associated with this screen - // - // SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed - // to be an NSNumber. See documentation for `deviceDescription` for details: - // - let obj = device_description - .get(key) - .expect("failed getting screen display id from device description"); - let obj: *const AnyObject = obj; - let obj: *const NSNumber = obj.cast(); - let obj: &NSNumber = unsafe { &*obj }; - - obj.as_u32() - }) - } - - #[method(backingScaleFactor)] - pub fn backingScaleFactor(&self) -> CGFloat; - } -); - -pub type NSDeviceDescriptionKey = NSString; diff --git a/src/platform_impl/macos/appkit/text_input_client.rs b/src/platform_impl/macos/appkit/text_input_client.rs deleted file mode 100644 index 12d03fcb5c..0000000000 --- a/src/platform_impl/macos/appkit/text_input_client.rs +++ /dev/null @@ -1,9 +0,0 @@ -use objc2::{extern_protocol, ProtocolType}; - -extern_protocol!( - pub(crate) unsafe trait NSTextInputClient { - // TODO: Methods - } - - unsafe impl ProtocolType for dyn NSTextInputClient {} -); diff --git a/src/platform_impl/macos/appkit/text_input_context.rs b/src/platform_impl/macos/appkit/text_input_context.rs deleted file mode 100644 index ee72079872..0000000000 --- a/src/platform_impl/macos/appkit/text_input_context.rs +++ /dev/null @@ -1,29 +0,0 @@ -use icrate::Foundation::{NSObject, NSString}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -type NSTextInputSourceIdentifier = NSString; - -extern_class!( - /// Main-Thread-Only! - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSTextInputContext; - - unsafe impl ClassType for NSTextInputContext { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSTextInputContext { - #[method(invalidateCharacterCoordinates)] - pub fn invalidateCharacterCoordinates(&self); - - #[method(discardMarkedText)] - pub fn discardMarkedText(&self); - - #[method_id(selectedKeyboardInputSource)] - pub fn selectedKeyboardInputSource(&self) -> Option>; - } -); diff --git a/src/platform_impl/macos/appkit/version.rs b/src/platform_impl/macos/appkit/version.rs deleted file mode 100644 index c121e0dc9d..0000000000 --- a/src/platform_impl/macos/appkit/version.rs +++ /dev/null @@ -1,62 +0,0 @@ -#[repr(transparent)] -#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)] -pub struct NSAppKitVersion(f64); - -#[allow(dead_code)] -#[allow(non_upper_case_globals)] -impl NSAppKitVersion { - pub fn current() -> Self { - extern "C" { - static NSAppKitVersionNumber: NSAppKitVersion; - } - - unsafe { NSAppKitVersionNumber } - } - - pub fn floor(self) -> Self { - Self(self.0.floor()) - } - - pub const NSAppKitVersionNumber10_0: Self = Self(577.0); - pub const NSAppKitVersionNumber10_1: Self = Self(620.0); - pub const NSAppKitVersionNumber10_2: Self = Self(663.0); - pub const NSAppKitVersionNumber10_2_3: Self = Self(663.6); - pub const NSAppKitVersionNumber10_3: Self = Self(743.0); - pub const NSAppKitVersionNumber10_3_2: Self = Self(743.14); - pub const NSAppKitVersionNumber10_3_3: Self = Self(743.2); - pub const NSAppKitVersionNumber10_3_5: Self = Self(743.24); - pub const NSAppKitVersionNumber10_3_7: Self = Self(743.33); - pub const NSAppKitVersionNumber10_3_9: Self = Self(743.36); - pub const NSAppKitVersionNumber10_4: Self = Self(824.0); - pub const NSAppKitVersionNumber10_4_1: Self = Self(824.1); - pub const NSAppKitVersionNumber10_4_3: Self = Self(824.23); - pub const NSAppKitVersionNumber10_4_4: Self = Self(824.33); - pub const NSAppKitVersionNumber10_4_7: Self = Self(824.41); - pub const NSAppKitVersionNumber10_5: Self = Self(949.0); - pub const NSAppKitVersionNumber10_5_2: Self = Self(949.27); - pub const NSAppKitVersionNumber10_5_3: Self = Self(949.33); - pub const NSAppKitVersionNumber10_6: Self = Self(1038.0); - pub const NSAppKitVersionNumber10_7: Self = Self(1138.0); - pub const NSAppKitVersionNumber10_7_2: Self = Self(1138.23); - pub const NSAppKitVersionNumber10_7_3: Self = Self(1138.32); - pub const NSAppKitVersionNumber10_7_4: Self = Self(1138.47); - pub const NSAppKitVersionNumber10_8: Self = Self(1187.0); - pub const NSAppKitVersionNumber10_9: Self = Self(1265.0); - pub const NSAppKitVersionNumber10_10: Self = Self(1343.0); - pub const NSAppKitVersionNumber10_10_2: Self = Self(1344.0); - pub const NSAppKitVersionNumber10_10_3: Self = Self(1347.0); - pub const NSAppKitVersionNumber10_10_4: Self = Self(1348.0); - pub const NSAppKitVersionNumber10_10_5: Self = Self(1348.0); - pub const NSAppKitVersionNumber10_10_Max: Self = Self(1349.0); - pub const NSAppKitVersionNumber10_11: Self = Self(1404.0); - pub const NSAppKitVersionNumber10_11_1: Self = Self(1404.13); - pub const NSAppKitVersionNumber10_11_2: Self = Self(1404.34); - pub const NSAppKitVersionNumber10_11_3: Self = Self(1404.34); - pub const NSAppKitVersionNumber10_12: Self = Self(1504.0); - pub const NSAppKitVersionNumber10_12_1: Self = Self(1504.60); - pub const NSAppKitVersionNumber10_12_2: Self = Self(1504.76); - pub const NSAppKitVersionNumber10_13: Self = Self(1561.0); - pub const NSAppKitVersionNumber10_13_1: Self = Self(1561.1); - pub const NSAppKitVersionNumber10_13_2: Self = Self(1561.2); - pub const NSAppKitVersionNumber10_13_4: Self = Self(1561.4); -} diff --git a/src/platform_impl/macos/appkit/view.rs b/src/platform_impl/macos/appkit/view.rs deleted file mode 100644 index d6720ec529..0000000000 --- a/src/platform_impl/macos/appkit/view.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::ffi::c_void; -use std::num::NonZeroIsize; -use std::ptr; - -use icrate::Foundation::{NSObject, NSPoint, NSRect}; -use objc2::rc::Id; -use objc2::runtime::AnyObject; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::{NSCursor, NSResponder, NSTextInputContext, NSWindow}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSView; - - unsafe impl ClassType for NSView { - #[inherits(NSObject)] - type Super = NSResponder; - type Mutability = mutability::InteriorMutable; - } -); - -// Documented as "Main Thread Only". -// > generally thread safe, although operations on views such as creating, -// > resizing, and moving should happen on the main thread. -// -// -// > If you want to use a thread to draw to a view, bracket all drawing code -// > between the lockFocusIfCanDraw and unlockFocus methods of NSView. -// - -extern_methods!( - /// Getter methods - unsafe impl NSView { - #[method(frame)] - pub fn frame(&self) -> NSRect; - - #[method(bounds)] - pub fn bounds(&self) -> NSRect; - - #[method_id(inputContext)] - pub fn inputContext( - &self, - // _mtm: MainThreadMarker, - ) -> Option>; - - #[method(hasMarkedText)] - pub fn hasMarkedText(&self) -> bool; - - #[method(convertPoint:fromView:)] - pub fn convertPoint_fromView(&self, point: NSPoint, view: Option<&NSView>) -> NSPoint; - - #[method_id(window)] - pub fn window(&self) -> Option>; - } - - unsafe impl NSView { - #[method(setWantsBestResolutionOpenGLSurface:)] - pub fn setWantsBestResolutionOpenGLSurface(&self, value: bool); - - #[method(setWantsLayer:)] - pub fn setWantsLayer(&self, wants_layer: bool); - - #[method(setPostsFrameChangedNotifications:)] - pub fn setPostsFrameChangedNotifications(&self, value: bool); - - #[method(removeTrackingRect:)] - pub fn removeTrackingRect(&self, tag: NSTrackingRectTag); - - #[method(addTrackingRect:owner:userData:assumeInside:)] - unsafe fn inner_addTrackingRect( - &self, - rect: NSRect, - owner: &AnyObject, - user_data: *mut c_void, - assume_inside: bool, - ) -> Option; - - pub fn add_tracking_rect(&self, rect: NSRect, assume_inside: bool) -> NSTrackingRectTag { - // SAFETY: The user data is NULL, so it is valid - unsafe { self.inner_addTrackingRect(rect, self, ptr::null_mut(), assume_inside) } - .expect("failed creating tracking rect") - } - - #[method(addCursorRect:cursor:)] - // NSCursor safe to take by shared reference since it is already immutable - pub fn addCursorRect(&self, rect: NSRect, cursor: &NSCursor); - - #[method(setHidden:)] - pub fn setHidden(&self, hidden: bool); - } -); - -/// -pub type NSTrackingRectTag = NonZeroIsize; // NSInteger, but non-zero! diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs index 5f5d8f179f..748665fc5f 100644 --- a/src/platform_impl/macos/appkit/window.rs +++ b/src/platform_impl/macos/appkit/window.rs @@ -1,3 +1,4 @@ +use icrate::AppKit::{NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView}; use icrate::Foundation::{ CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger, }; @@ -6,9 +7,7 @@ use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{extern_class, extern_methods, mutability, ClassType}; -use super::{ - NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowTabGroup, -}; +use super::NSWindowTabGroup; extern_class!( /// Main-Thread-Only! @@ -18,7 +17,7 @@ extern_class!( unsafe impl ClassType for NSWindow { #[inherits(NSObject)] type Super = NSResponder; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; } ); diff --git a/src/platform_impl/macos/cursor.rs b/src/platform_impl/macos/cursor.rs index 4512b76e34..8f84b5e88d 100644 --- a/src/platform_impl/macos/cursor.rs +++ b/src/platform_impl/macos/cursor.rs @@ -1,17 +1,228 @@ +use icrate::AppKit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage}; +use icrate::Foundation::{ + ns_string, NSData, NSDictionary, NSNumber, NSObject, NSObjectProtocol, NSPoint, NSSize, + NSString, +}; use objc2::rc::Id; +use objc2::runtime::Sel; +use objc2::{msg_send_id, sel, ClassType}; +use once_cell::sync::Lazy; +use std::ffi::c_uchar; +use std::slice; -use super::appkit::NSCursor; use super::EventLoopWindowTarget; +use crate::cursor::CursorImage; use crate::cursor::OnlyCursorImageBuilder; +use crate::window::CursorIcon; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct CustomCursor(pub(crate) Id); +// SAFETY: NSCursor is immutable and thread-safe +// TODO(madsmtm): Put this logic in icrate itself +unsafe impl Send for CustomCursor {} +unsafe impl Sync for CustomCursor {} + impl CustomCursor { pub(crate) fn build( cursor: OnlyCursorImageBuilder, _: &EventLoopWindowTarget, ) -> CustomCursor { - Self(NSCursor::from_image(&cursor.0)) + Self(cursor_from_image(&cursor.0)) + } +} + +pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id { + let width = cursor.width; + let height = cursor.height; + + let bitmap = unsafe { + NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel( + NSBitmapImageRep::alloc(), + std::ptr::null_mut::<*mut c_uchar>(), + width as isize, + height as isize, + 8, + 4, + true, + false, + NSDeviceRGBColorSpace, + width as isize * 4, + 32, + ).unwrap() + }; + let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) }; + bitmap_data.copy_from_slice(&cursor.rgba); + + let image = unsafe { + NSImage::initWithSize(NSImage::alloc(), NSSize::new(width.into(), height.into())) + }; + unsafe { image.addRepresentation(&bitmap) }; + + let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64); + + NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot) +} + +pub(crate) fn default_cursor() -> Id { + NSCursor::arrowCursor() +} + +unsafe fn try_cursor_from_selector(sel: Sel) -> Option> { + let cls = NSCursor::class(); + if cls.responds_to(sel) { + let cursor: Id = unsafe { msg_send_id![cls, performSelector: sel] }; + Some(cursor) + } else { + warn!("cursor `{sel}` appears to be invalid"); + None + } +} + +macro_rules! def_undocumented_cursor { + {$( + $(#[$($m:meta)*])* + fn $name:ident(); + )*} => {$( + $(#[$($m)*])* + #[allow(non_snake_case)] + fn $name() -> Id { + unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) } + } + )*}; +} + +def_undocumented_cursor!( + // Undocumented cursors: https://stackoverflow.com/a/46635398/5435443 + fn _helpCursor(); + fn _zoomInCursor(); + fn _zoomOutCursor(); + fn _windowResizeNorthEastCursor(); + fn _windowResizeNorthWestCursor(); + fn _windowResizeSouthEastCursor(); + fn _windowResizeSouthWestCursor(); + fn _windowResizeNorthEastSouthWestCursor(); + fn _windowResizeNorthWestSouthEastCursor(); + + // While these two are available, the former just loads a white arrow, + // and the latter loads an ugly deflated beachball! + // pub fn _moveCursor(); + // pub fn _waitCursor(); + + // An even more undocumented cursor... + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349 + fn busyButClickableCursor(); +); + +// Note that loading `busybutclickable` with this code won't animate +// the frames; instead you'll just get them all in a column. +unsafe fn load_webkit_cursor(name: &NSString) -> Id { + // Snatch a cursor from WebKit; They fit the style of the native + // cursors, and will seem completely standard to macOS users. + // + // https://stackoverflow.com/a/21786835/5435443 + let root = ns_string!("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors"); + let cursor_path = root.stringByAppendingPathComponent(name); + + let pdf_path = cursor_path.stringByAppendingPathComponent(ns_string!("cursor.pdf")); + let image = NSImage::initByReferencingFile(NSImage::alloc(), &pdf_path).unwrap(); + + // TODO: Handle PLists better + let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist")); + let info: Id> = unsafe { + msg_send_id![ + >::class(), + dictionaryWithContentsOfFile: &*info_path, + ] + }; + let mut x = 0.0; + if let Some(n) = info.get(&*ns_string!("hotx")) { + if n.is_kind_of::() { + let ptr: *const NSObject = n; + let ptr: *const NSNumber = ptr.cast(); + x = unsafe { &*ptr }.as_cgfloat() + } + } + let mut y = 0.0; + if let Some(n) = info.get(&*ns_string!("hotx")) { + if n.is_kind_of::() { + let ptr: *const NSObject = n; + let ptr: *const NSNumber = ptr.cast(); + y = unsafe { &*ptr }.as_cgfloat() + } + } + + let hotspot = NSPoint::new(x, y); + NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot) +} + +fn webkit_move() -> Id { + unsafe { load_webkit_cursor(ns_string!("move")) } +} + +fn webkit_cell() -> Id { + unsafe { load_webkit_cursor(ns_string!("cell")) } +} + +pub(crate) fn invisible_cursor() -> Id { + // 16x16 GIF data for invisible cursor + // You can reproduce this via ImageMagick. + // $ convert -size 16x16 xc:none cursor.gif + static CURSOR_BYTES: &[u8] = &[ + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, + 0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B, + ]; + + static CURSOR: Lazy = Lazy::new(|| { + // TODO: Consider using `dataWithBytesNoCopy:` + let data = NSData::with_bytes(CURSOR_BYTES); + let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap(); + let hotspot = NSPoint::new(0.0, 0.0); + CustomCursor(NSCursor::initWithImage_hotSpot( + NSCursor::alloc(), + &image, + hotspot, + )) + }); + + CURSOR.0.clone() +} + +pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Id { + match icon { + CursorIcon::Default => default_cursor(), + CursorIcon::Pointer => NSCursor::pointingHandCursor(), + CursorIcon::Grab => NSCursor::openHandCursor(), + CursorIcon::Grabbing => NSCursor::closedHandCursor(), + CursorIcon::Text => NSCursor::IBeamCursor(), + CursorIcon::VerticalText => NSCursor::IBeamCursorForVerticalLayout(), + CursorIcon::Copy => NSCursor::dragCopyCursor(), + CursorIcon::Alias => NSCursor::dragLinkCursor(), + CursorIcon::NotAllowed | CursorIcon::NoDrop => NSCursor::operationNotAllowedCursor(), + CursorIcon::ContextMenu => NSCursor::contextualMenuCursor(), + CursorIcon::Crosshair => NSCursor::crosshairCursor(), + CursorIcon::EResize => NSCursor::resizeRightCursor(), + CursorIcon::NResize => NSCursor::resizeUpCursor(), + CursorIcon::WResize => NSCursor::resizeLeftCursor(), + CursorIcon::SResize => NSCursor::resizeDownCursor(), + CursorIcon::EwResize | CursorIcon::ColResize => NSCursor::resizeLeftRightCursor(), + CursorIcon::NsResize | CursorIcon::RowResize => NSCursor::resizeUpDownCursor(), + CursorIcon::Help => _helpCursor(), + CursorIcon::ZoomIn => _zoomInCursor(), + CursorIcon::ZoomOut => _zoomOutCursor(), + CursorIcon::NeResize => _windowResizeNorthEastCursor(), + CursorIcon::NwResize => _windowResizeNorthWestCursor(), + CursorIcon::SeResize => _windowResizeSouthEastCursor(), + CursorIcon::SwResize => _windowResizeSouthWestCursor(), + CursorIcon::NeswResize => _windowResizeNorthEastSouthWestCursor(), + CursorIcon::NwseResize => _windowResizeNorthWestSouthEastCursor(), + // This is the wrong semantics for `Wait`, but it's the same as + // what's used in Safari and Chrome. + CursorIcon::Wait | CursorIcon::Progress => busyButClickableCursor(), + CursorIcon::Move | CursorIcon::AllScroll => webkit_move(), + CursorIcon::Cell => webkit_cell(), + _ => default_cursor(), } } diff --git a/src/platform_impl/macos/event.rs b/src/platform_impl/macos/event.rs index cdd79297ec..e2c1838525 100644 --- a/src/platform_impl/macos/event.rs +++ b/src/platform_impl/macos/event.rs @@ -4,10 +4,15 @@ use core_foundation::{ base::CFRelease, data::{CFDataGetBytePtr, CFDataRef}, }; -use icrate::Foundation::MainThreadMarker; +use icrate::AppKit::{ + NSEvent, NSEventModifierFlagCommand, NSEventModifierFlagControl, NSEventModifierFlagOption, + NSEventModifierFlagShift, NSEventModifierFlags, NSEventSubtypeWindowExposed, + NSEventTypeApplicationDefined, +}; +use icrate::Foundation::{MainThreadMarker, NSPoint}; +use objc2::rc::Id; use smol_str::SmolStr; -use super::appkit::{NSEvent, NSEventModifierFlags}; use crate::{ event::{ElementState, KeyEvent, Modifiers}, keyboard::{ @@ -98,8 +103,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key { } fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key { - let string = ns_event - .charactersIgnoringModifiers() + let string = unsafe { ns_event.charactersIgnoringModifiers() } .map(|s| s.to_string()) .unwrap_or_default(); if string.is_empty() { @@ -122,15 +126,14 @@ pub(crate) fn create_key_event( use ElementState::{Pressed, Released}; let state = if is_press { Pressed } else { Released }; - let scancode = ns_event.key_code(); + let scancode = unsafe { ns_event.keyCode() }; let mut physical_key = key_override.unwrap_or_else(|| PhysicalKey::from_scancode(scancode as u32)); let text_with_all_modifiers: Option = if key_override.is_some() { None } else { - let characters = ns_event - .characters() + let characters = unsafe { ns_event.characters() } .map(|s| s.to_string()) .unwrap_or_default(); if characters.is_empty() { @@ -148,8 +151,8 @@ pub(crate) fn create_key_event( let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) { let key_without_modifiers = get_modifierless_char(scancode); - let modifiers = NSEvent::modifierFlags(ns_event); - let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask); + let modifiers = unsafe { ns_event.modifierFlags() }; + let has_ctrl = flags_contains(modifiers, NSEventModifierFlagControl); let logical_key = match text_with_all_modifiers.as_ref() { // Only checking for ctrl here, not checking for alt because we DO want to @@ -311,42 +314,84 @@ pub fn extra_function_key_to_code(scancode: u16, string: &str) -> PhysicalKey { } } +// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259 +const NX_DEVICELCTLKEYMASK: NSEventModifierFlags = 0x00000001; +const NX_DEVICELSHIFTKEYMASK: NSEventModifierFlags = 0x00000002; +const NX_DEVICERSHIFTKEYMASK: NSEventModifierFlags = 0x00000004; +const NX_DEVICELCMDKEYMASK: NSEventModifierFlags = 0x00000008; +const NX_DEVICERCMDKEYMASK: NSEventModifierFlags = 0x00000010; +const NX_DEVICELALTKEYMASK: NSEventModifierFlags = 0x00000020; +const NX_DEVICERALTKEYMASK: NSEventModifierFlags = 0x00000040; +const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = 0x00002000; + +pub(super) fn flags_contains(flags: NSEventModifierFlags, value: NSEventModifierFlags) -> bool { + flags & value == value +} + +pub(super) fn lalt_pressed(event: &NSEvent) -> bool { + flags_contains(unsafe { event.modifierFlags() }, NX_DEVICELALTKEYMASK) +} + +pub(super) fn ralt_pressed(event: &NSEvent) -> bool { + flags_contains(unsafe { event.modifierFlags() }, NX_DEVICERALTKEYMASK) +} + pub(super) fn event_mods(event: &NSEvent) -> Modifiers { - let flags = event.modifierFlags(); + let flags = unsafe { event.modifierFlags() }; let mut state = ModifiersState::empty(); let mut pressed_mods = ModifiersKeys::empty(); state.set( ModifiersState::SHIFT, - flags.contains(NSEventModifierFlags::NSShiftKeyMask), + flags_contains(flags, NSEventModifierFlagShift), + ); + pressed_mods.set( + ModifiersKeys::LSHIFT, + flags_contains(flags, NX_DEVICELSHIFTKEYMASK), + ); + pressed_mods.set( + ModifiersKeys::RSHIFT, + flags_contains(flags, NX_DEVICERSHIFTKEYMASK), ); - - pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed()); - pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed()); state.set( ModifiersState::CONTROL, - flags.contains(NSEventModifierFlags::NSControlKeyMask), + flags_contains(flags, NSEventModifierFlagControl), + ); + pressed_mods.set( + ModifiersKeys::LCONTROL, + flags_contains(flags, NX_DEVICELCTLKEYMASK), + ); + pressed_mods.set( + ModifiersKeys::RCONTROL, + flags_contains(flags, NX_DEVICERCTLKEYMASK), ); - - pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed()); - pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed()); state.set( ModifiersState::ALT, - flags.contains(NSEventModifierFlags::NSAlternateKeyMask), + flags_contains(flags, NSEventModifierFlagOption), + ); + pressed_mods.set( + ModifiersKeys::LALT, + flags_contains(flags, NX_DEVICELALTKEYMASK), + ); + pressed_mods.set( + ModifiersKeys::RALT, + flags_contains(flags, NX_DEVICERALTKEYMASK), ); - - pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed()); - pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed()); state.set( ModifiersState::SUPER, - flags.contains(NSEventModifierFlags::NSCommandKeyMask), + flags_contains(flags, NSEventModifierFlagCommand), + ); + pressed_mods.set( + ModifiersKeys::LSUPER, + flags_contains(flags, NX_DEVICELCMDKEYMASK), + ); + pressed_mods.set( + ModifiersKeys::RSUPER, + flags_contains(flags, NX_DEVICERCMDKEYMASK), ); - - pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed()); - pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed()); Modifiers { state, @@ -354,6 +399,22 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers { } } +pub(super) fn dummy_event() -> Option> { + unsafe { + NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2( + NSEventTypeApplicationDefined, + NSPoint::new(0.0, 0.0), + 0, // Empty NSEventModifierFlags + 0.0, + 0, + None, + NSEventSubtypeWindowExposed, + 0, + 0, + ) + } +} + impl PhysicalKeyExtScancode for PhysicalKey { fn to_scancode(self) -> Option { let code = match self { diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index fc56b11d37..f73c4a9c4d 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -22,7 +22,10 @@ use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::NSObjectProtocol; use objc2::{msg_send_id, ClassType}; -use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent, NSWindow}; +use super::{ + appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow}, + event::dummy_event, +}; use crate::{ error::EventLoopError, event::Event, @@ -477,7 +480,7 @@ pub fn stop_app_on_panic R + UnwindSafe, R>( app.stop(None); // 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 - app.postEvent_atStart(&NSEvent::dummy(), true); + app.postEvent_atStart(&dummy_event().unwrap(), true); None } } diff --git a/src/platform_impl/macos/menu.rs b/src/platform_impl/macos/menu.rs index 5f03e8ed80..04c5442fe8 100644 --- a/src/platform_impl/macos/menu.rs +++ b/src/platform_impl/macos/menu.rs @@ -1,42 +1,47 @@ -use icrate::Foundation::{ns_string, NSProcessInfo, NSString}; +use icrate::AppKit::{ + NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags, NSMenu, NSMenuItem, +}; +use icrate::Foundation::{ns_string, MainThreadMarker, NSProcessInfo, NSString}; use objc2::rc::Id; use objc2::runtime::Sel; use objc2::sel; -use super::appkit::{NSApp, NSEventModifierFlags, NSMenu, NSMenuItem}; +use super::appkit::NSApp; struct KeyEquivalent<'a> { key: &'a NSString, masks: Option, } -pub fn initialize() { - let menubar = NSMenu::new(); - let app_menu_item = NSMenuItem::new(); +pub fn initialize(mtm: MainThreadMarker) { + let menubar = NSMenu::new(mtm); + let app_menu_item = NSMenuItem::new(mtm); menubar.addItem(&app_menu_item); - let app_menu = NSMenu::new(); + let app_menu = NSMenu::new(mtm); let process_name = NSProcessInfo::processInfo().processName(); // About menu item let about_item_title = ns_string!("About ").stringByAppendingString(&process_name); let about_item = menu_item( + mtm, &about_item_title, Some(sel!(orderFrontStandardAboutPanel:)), None, ); // Services menu item - let services_menu = NSMenu::new(); - let services_item = menu_item(ns_string!("Services"), None, None); - services_item.setSubmenu(&services_menu); + let services_menu = NSMenu::new(mtm); + let services_item = menu_item(mtm, ns_string!("Services"), None, None); + services_item.setSubmenu(Some(&services_menu)); // Seperator menu item - let sep_first = NSMenuItem::separatorItem(); + let sep_first = NSMenuItem::separatorItem(mtm); // Hide application menu item let hide_item_title = ns_string!("Hide ").stringByAppendingString(&process_name); let hide_item = menu_item( + mtm, &hide_item_title, Some(sel!(hide:)), Some(KeyEquivalent { @@ -48,30 +53,31 @@ pub fn initialize() { // Hide other applications menu item let hide_others_item_title = ns_string!("Hide Others"); let hide_others_item = menu_item( + mtm, hide_others_item_title, Some(sel!(hideOtherApplications:)), Some(KeyEquivalent { key: ns_string!("h"), - masks: Some( - NSEventModifierFlags::NSAlternateKeyMask | NSEventModifierFlags::NSCommandKeyMask, - ), + masks: Some(NSEventModifierFlagOption | NSEventModifierFlagCommand), }), ); // Show applications menu item let show_all_item_title = ns_string!("Show All"); let show_all_item = menu_item( + mtm, show_all_item_title, Some(sel!(unhideAllApplications:)), None, ); // Seperator menu item - let sep = NSMenuItem::separatorItem(); + let sep = NSMenuItem::separatorItem(mtm); // Quit application menu item let quit_item_title = ns_string!("Quit ").stringByAppendingString(&process_name); let quit_item = menu_item( + mtm, &quit_item_title, Some(sel!(terminate:)), Some(KeyEquivalent { @@ -88,7 +94,7 @@ pub fn initialize() { app_menu.addItem(&show_all_item); app_menu.addItem(&sep); app_menu.addItem(&quit_item); - app_menu_item.setSubmenu(&app_menu); + app_menu_item.setSubmenu(Some(&app_menu)); let app = NSApp(); app.setServicesMenu(&services_menu); @@ -96,6 +102,7 @@ pub fn initialize() { } fn menu_item( + mtm: MainThreadMarker, title: &NSString, selector: Option, key_equivalent: Option>, @@ -104,7 +111,9 @@ fn menu_item( Some(ke) => (ke.key, ke.masks), None => (ns_string!(""), None), }; - let item = NSMenuItem::newWithTitle(title, selector, key); + let item = unsafe { + NSMenuItem::initWithTitle_action_keyEquivalent(mtm.alloc(), title, selector, key) + }; if let Some(masks) = masks { item.setKeyEquivalentModifierMask(masks) } diff --git a/src/platform_impl/macos/monitor.rs b/src/platform_impl/macos/monitor.rs index 16e92f3b1f..ec4c33039c 100644 --- a/src/platform_impl/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -10,9 +10,10 @@ use core_foundation::{ use core_graphics::display::{ CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode, }; -use objc2::rc::Id; +use icrate::AppKit::NSScreen; +use icrate::Foundation::{ns_string, MainThreadMarker, NSNumber}; +use objc2::{rc::Id, runtime::AnyObject}; -use super::appkit::NSScreen; use super::ffi; use crate::dpi::{PhysicalPosition, PhysicalSize}; @@ -210,10 +211,12 @@ impl MonitorHandle { } pub fn scale_factor(&self) -> f64 { - match self.ns_screen() { - Some(screen) => screen.backingScaleFactor() as f64, - None => 1.0, // default to 1.0 when we can't find the screen - } + MainThreadMarker::run_on_main(|mtm| { + match self.ns_screen(mtm) { + Some(screen) => screen.backingScaleFactor() as f64, + None => 1.0, // default to 1.0 when we can't find the screen + } + }) } pub fn refresh_rate_millihertz(&self) -> Option { @@ -303,10 +306,10 @@ impl MonitorHandle { } } - pub(crate) fn ns_screen(&self) -> Option> { + pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option> { let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) }; - NSScreen::screens().into_iter().find(|screen| { - let other_native_id = screen.display_id(); + NSScreen::screens(mtm).into_iter().find(|screen| { + let other_native_id = get_display_id(screen); let other_uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(other_native_id as CGDirectDisplayID) }; @@ -314,3 +317,25 @@ impl MonitorHandle { }) } } + +pub(crate) fn get_display_id(screen: &NSScreen) -> u32 { + let key = ns_string!("NSScreenNumber"); + + objc2::rc::autoreleasepool(|_| { + let device_description = screen.deviceDescription(); + + // Retrieve the CGDirectDisplayID associated with this screen + // + // SAFETY: The value from @"NSScreenNumber" in deviceDescription is guaranteed + // to be an NSNumber. See documentation for `deviceDescription` for details: + // + let obj = device_description + .get(key) + .expect("failed getting screen display id from device description"); + let obj: *const AnyObject = obj; + let obj: *const NSNumber = obj.cast(); + let obj: &NSNumber = unsafe { &*obj }; + + obj.as_u32() + }) +} diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 68f9a0328c..ac2dce9e01 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -1,10 +1,17 @@ #![allow(clippy::unnecessary_cast)] use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; +use std::ptr; +use icrate::AppKit::{ + NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled, NSEventPhaseChanged, + NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient, NSTrackingRectTag, + NSView, +}; use icrate::Foundation::{ - NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString, - NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger, + MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, + NSMutableAttributedString, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize, + NSString, NSUInteger, }; use objc2::rc::{Id, WeakId}; use objc2::runtime::{AnyObject, Sel}; @@ -12,11 +19,10 @@ use objc2::{ class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, }; +use super::cursor::{default_cursor, invisible_cursor}; +use super::event::{lalt_pressed, ralt_pressed}; use super::{ - appkit::{ - NSApp, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingRectTag, - NSView, - }, + appkit::NSApp, event::{code_to_key, code_to_location}, }; use crate::{ @@ -48,7 +54,7 @@ impl Default for CursorState { fn default() -> Self { Self { visible: true, - cursor: Default::default(), + cursor: default_cursor(), } } } @@ -150,7 +156,7 @@ declare_class!( unsafe impl ClassType for WinitView { #[inherits(NSResponder, NSObject)] type Super = NSView; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "WinitView"; } @@ -167,7 +173,8 @@ declare_class!( } let rect = self.frame(); - let tracking_rect = self.add_tracking_rect(rect, false); + let tracking_rect = unsafe { self.addTrackingRect_owner_userData_assumeInside(rect, self, ptr::null_mut(), false) }; + assert_ne!(tracking_rect, 0, "failed adding tracking rect"); self.ivars().tracking_rect.set(Some(tracking_rect)); } @@ -179,7 +186,8 @@ declare_class!( } let rect = self.frame(); - let tracking_rect = self.add_tracking_rect(rect, false); + let tracking_rect = unsafe { self.addTrackingRect_owner_userData_assumeInside(rect, self, ptr::null_mut(), false) }; + assert_ne!(tracking_rect, 0, "failed adding tracking rect"); self.ivars().tracking_rect.set(Some(tracking_rect)); // Emit resize event here rather than from windowDidResize because: @@ -227,9 +235,9 @@ declare_class!( let cursor_state = self.ivars().cursor_state.borrow(); // We correctly invoke `addCursorRect` only from inside `resetCursorRects` if cursor_state.visible { - self.addCursorRect(bounds, &cursor_state.cursor); + self.addCursorRect_cursor(bounds, &cursor_state.cursor); } else { - self.addCursorRect(bounds, &NSCursor::invisible()); + self.addCursorRect_cursor(bounds, &invisible_cursor()); } } } @@ -295,7 +303,7 @@ declare_class!( self.queue_event(WindowEvent::Ime(Ime::Enabled)); } - if self.hasMarkedText() { + if unsafe { self.hasMarkedText() } { self.ivars().ime_state.set(ImeState::Preedit); } else { // In case the preedit was cleared, set IME into the Ground state. @@ -388,7 +396,7 @@ declare_class!( let is_control = string.chars().next().map_or(false, |c| c.is_control()); // Commit only if we have marked text. - if self.hasMarkedText() && self.is_ime_enabled() && !is_control { + if unsafe { self.hasMarkedText() } && self.is_ime_enabled() && !is_control { self.queue_event(WindowEvent::Ime(Ime::Preedit(String::new(), None))); self.queue_event(WindowEvent::Ime(Ime::Commit(string))); self.ivars().ime_state.set(ImeState::Commited); @@ -409,7 +417,7 @@ declare_class!( self.ivars().forward_key_to_app.set(true); - if self.hasMarkedText() && self.ivars().ime_state.get() == ImeState::Preedit { + if unsafe { self.hasMarkedText() } && self.ivars().ime_state.get() == ImeState::Preedit { // Leave preedit so that we also report the key-up for this key. self.ivars().ime_state.set(ImeState::Ground); } @@ -467,7 +475,7 @@ declare_class!( }; if !had_ime_input || self.ivars().forward_key_to_app.get() { - let key_event = create_key_event(&event, true, event.is_a_repeat(), None); + let key_event = create_key_event(&event, true, unsafe { event.isARepeat() }, None); self.queue_event(WindowEvent::KeyboardInput { device_id: DEVICE_ID, event: key_event, @@ -536,7 +544,7 @@ declare_class!( .expect("could not find current event"); self.update_modifiers(&event, false); - let event = create_key_event(&event, true, event.is_a_repeat(), None); + let event = create_key_event(&event, true, unsafe { event.isARepeat() }, None); self.queue_event(WindowEvent::KeyboardInput { device_id: DEVICE_ID, @@ -633,8 +641,8 @@ declare_class!( self.mouse_motion(event); let delta = { - let (x, y) = (event.scrollingDeltaX(), event.scrollingDeltaY()); - if event.hasPreciseScrollingDeltas() { + let (x, y) = unsafe { (event.scrollingDeltaX(), event.scrollingDeltaY()) }; + if unsafe { event.hasPreciseScrollingDeltas() } { let delta = LogicalPosition::new(x, y).to_physical(self.scale_factor()); MouseScrollDelta::PixelDelta(delta) } else { @@ -646,18 +654,19 @@ declare_class!( // be mutually exclusive anyhow, which is why the API is rather incoherent). If no momentum // phase is recorded (or rather, the started/ended cases of the momentum phase) then we // report the touch phase. - let phase = match event.momentumPhase() { - NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => { + #[allow(non_upper_case_globals)] + let phase = match unsafe { event.momentumPhase() } { + NSEventPhaseMayBegin | NSEventPhaseBegan => { TouchPhase::Started } - NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => { + NSEventPhaseEnded | NSEventPhaseCancelled => { TouchPhase::Ended } - _ => match event.phase() { - NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => { + _ => match unsafe { event.phase() } { + NSEventPhaseMayBegin | NSEventPhaseBegan => { TouchPhase::Started } - NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => { + NSEventPhaseEnded | NSEventPhaseCancelled => { TouchPhase::Ended } _ => TouchPhase::Moved, @@ -678,17 +687,18 @@ declare_class!( fn magnify_with_event(&self, event: &NSEvent) { trace_scope!("magnifyWithEvent:"); - let phase = match event.phase() { - NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, - NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved, - NSEventPhase::NSEventPhaseCancelled => TouchPhase::Cancelled, - NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended, + #[allow(non_upper_case_globals)] + let phase = match unsafe { event.phase() } { + NSEventPhaseBegan => TouchPhase::Started, + NSEventPhaseChanged => TouchPhase::Moved, + NSEventPhaseCancelled => TouchPhase::Cancelled, + NSEventPhaseEnded => TouchPhase::Ended, _ => return, }; self.queue_event(WindowEvent::TouchpadMagnify { device_id: DEVICE_ID, - delta: event.magnification(), + delta: unsafe { event.magnification() }, phase, }); } @@ -706,17 +716,18 @@ declare_class!( fn rotate_with_event(&self, event: &NSEvent) { trace_scope!("rotateWithEvent:"); - let phase = match event.phase() { - NSEventPhase::NSEventPhaseBegan => TouchPhase::Started, - NSEventPhase::NSEventPhaseChanged => TouchPhase::Moved, - NSEventPhase::NSEventPhaseCancelled => TouchPhase::Cancelled, - NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended, + #[allow(non_upper_case_globals)] + let phase = match unsafe { event.phase() } { + NSEventPhaseBegan => TouchPhase::Started, + NSEventPhaseChanged => TouchPhase::Moved, + NSEventPhaseCancelled => TouchPhase::Cancelled, + NSEventPhaseEnded => TouchPhase::Ended, _ => return, }; self.queue_event(WindowEvent::TouchpadRotate { device_id: DEVICE_ID, - delta: event.rotation(), + delta: unsafe { event.rotation() }, phase, }); } @@ -729,8 +740,8 @@ declare_class!( self.queue_event(WindowEvent::TouchpadPressure { device_id: DEVICE_ID, - pressure: event.pressure(), - stage: event.stage() as i64, + pressure: unsafe { event.pressure() }, + stage: unsafe { event.stage() } as i64, }); } @@ -753,7 +764,8 @@ declare_class!( impl WinitView { pub(super) fn new(window: &WinitWindow, accepts_first_mouse: bool) -> Id { - let this = Self::alloc().set_ivars(ViewState { + let mtm = MainThreadMarker::from(window); + let this = mtm.alloc().set_ivars(ViewState { accepts_first_mouse, _ns_window: WeakId::new(&window.retain()), ..Default::default() @@ -902,8 +914,8 @@ impl WinitView { // later will work though, since the flags are attached to the event and contain valid // information. 'send_event: { - if is_flags_changed_event && ns_event.key_code() != 0 { - let scancode = ns_event.key_code(); + if is_flags_changed_event && unsafe { ns_event.keyCode() } != 0 { + let scancode = unsafe { ns_event.keyCode() }; let physical_key = PhysicalKey::from_scancode(scancode as u32); // We'll correct the `is_press` later. @@ -1014,7 +1026,7 @@ impl WinitView { } fn mouse_motion(&self, event: &NSEvent) { - let window_point = event.locationInWindow(); + let window_point = unsafe { event.locationInWindow() }; let view_point = self.convertPoint_fromView(window_point, None); let view_rect = self.frame(); @@ -1023,7 +1035,7 @@ impl WinitView { || view_point.x > view_rect.size.width || view_point.y > view_rect.size.height { - let mouse_buttons_down = NSEvent::pressedMouseButtons(); + let mouse_buttons_down = unsafe { NSEvent::pressedMouseButtons() }; if mouse_buttons_down == 0 { // Point is outside of the client area (view) and no buttons are pressed return; @@ -1050,7 +1062,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton { // For the other events, it's always set to 0. // MacOS only defines the left, right and middle buttons, 3..=31 are left as generic buttons, // but 3 and 4 are very commonly used as Back and Forward by hardware vendors and applications. - match event.buttonNumber() { + match unsafe { event.buttonNumber() } { 0 => MouseButton::Left, 1 => MouseButton::Right, 2 => MouseButton::Middle, @@ -1066,30 +1078,35 @@ fn mouse_button(event: &NSEvent) -> MouseButton { fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id { let ev_mods = event_mods(event).state; let ignore_alt_characters = match option_as_alt { - OptionAsAlt::OnlyLeft if event.lalt_pressed() => true, - OptionAsAlt::OnlyRight if event.ralt_pressed() => true, + OptionAsAlt::OnlyLeft if lalt_pressed(event) => true, + OptionAsAlt::OnlyRight if ralt_pressed(event) => true, OptionAsAlt::Both if ev_mods.alt_key() => true, _ => false, } && !ev_mods.control_key() && !ev_mods.super_key(); if ignore_alt_characters { - let ns_chars = event - .charactersIgnoringModifiers() - .expect("expected characters to be non-null"); - - NSEvent::keyEventWithType( - event.type_(), - event.locationInWindow(), - event.modifierFlags(), - event.timestamp(), - event.window_number(), - None, - &ns_chars, - &ns_chars, - event.is_a_repeat(), - event.key_code(), - ) + let ns_chars = unsafe { + event + .charactersIgnoringModifiers() + .expect("expected characters to be non-null") + }; + + unsafe { + NSEvent::keyEventWithType_location_modifierFlags_timestamp_windowNumber_context_characters_charactersIgnoringModifiers_isARepeat_keyCode( + event.r#type(), + event.locationInWindow(), + event.modifierFlags(), + event.timestamp(), + event.windowNumber(), + None, + &ns_chars, + &ns_chars, + event.isARepeat(), + event.keyCode(), + ) + .unwrap() + } } else { event.copy() } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 06ba38bd34..98d0ca8a74 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -15,7 +15,6 @@ use crate::{ platform::macos::{OptionAsAlt, WindowExtMacOS}, platform_impl::platform::{ app_state::AppState, - appkit::NSWindowOrderingMode, event_loop::EventLoopWindowTarget, ffi, monitor::{self, MonitorHandle, VideoMode}, @@ -30,6 +29,11 @@ use crate::{ }, }; use core_graphics::display::{CGDisplay, CGPoint}; +use icrate::AppKit::NSWindowAbove; +use icrate::AppKit::{ + NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSColor, + NSFilenamesPboardType, NSResponder, NSScreen, NSView, +}; use icrate::Foundation::{ CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, @@ -38,13 +42,14 @@ use objc2::rc::{autoreleasepool, Id}; use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; use super::appkit::{ - NSApp, NSAppKitVersion, NSAppearance, NSApplicationPresentationOptions, NSBackingStoreType, - NSColor, NSCursor, NSFilenamesPboardType, NSRequestUserAttentionType, NSResponder, NSScreen, - NSView, NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask, + NSApp, NSApplicationPresentationOptions, NSBackingStoreType, NSRequestUserAttentionType, + NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, }; +use super::cursor::cursor_from_icon; use super::ffi::CGSMainConnectionID; use super::ffi::CGSSetWindowBackgroundBlurRadius; +use super::monitor::get_display_id; pub(crate) struct Window { window: MainThreadBound>, @@ -67,7 +72,8 @@ impl Window { ) -> Result { let mtm = MainThreadMarker::new() .expect("windows can only be created on the main thread on macOS"); - let (window, _delegate) = autoreleasepool(|_| WinitWindow::new(attributes, pl_attribs))?; + let (window, _delegate) = + autoreleasepool(|_| WinitWindow::new(attributes, pl_attribs, mtm))?; Ok(Window { window: MainThreadBound::new(window, mtm), _delegate: MainThreadBound::new(_delegate, mtm), @@ -161,7 +167,7 @@ declare_class!( unsafe impl ClassType for WinitWindow { #[inherits(NSResponder, NSObject)] type Super = NSWindow; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "WinitWindow"; } @@ -267,6 +273,7 @@ impl WinitWindow { fn new( attrs: WindowAttributes, pl_attrs: PlatformSpecificWindowBuilderAttributes, + mtm: MainThreadMarker, ) -> Result<(Id, Id), RootOsError> { trace_scope!("WinitWindow::new"); @@ -274,15 +281,15 @@ impl WinitWindow { let screen = match attrs.fullscreen.0.clone().map(Into::into) { Some(Fullscreen::Borderless(Some(monitor))) | Some(Fullscreen::Exclusive(VideoMode { monitor, .. })) => { - monitor.ns_screen().or_else(NSScreen::main) + monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm)) } - Some(Fullscreen::Borderless(None)) => NSScreen::main(), + Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm), None => None, }; let frame = match &screen { Some(screen) => screen.frame(), None => { - let scale_factor = NSScreen::main() + let scale_factor = NSScreen::mainScreen(mtm) .map(|screen| screen.backingScaleFactor() as f64) .unwrap_or(1.0); let (width, height) = match attrs.inner_size { @@ -346,7 +353,7 @@ impl WinitWindow { ..Default::default() }; - let this = WinitWindow::alloc().set_ivars(Mutex::new(state)); + let this = mtm.alloc().set_ivars(Mutex::new(state)); let this: Option> = unsafe { msg_send_id![ super(this), @@ -442,9 +449,11 @@ impl WinitWindow { )) })?; + // TODO(madsmtm): Remove this + let window: Id = unsafe { Id::cast(this.clone()) }; // SAFETY: We know that there are no parent -> child -> parent cycles since the only place in `winit` // where we allow making a window a child window is right here, just after it's been created. - unsafe { parent.addChildWindow(&this, NSWindowOrderingMode::NSWindowAbove) }; + unsafe { parent.addChildWindow_ordered(&window, NSWindowAbove) }; } Some(raw) => panic!("Invalid raw window handle {raw:?} on macOS"), None => (), @@ -455,6 +464,7 @@ impl WinitWindow { // The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until // macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid // always the default system value in favour of the user's code + #[allow(deprecated)] view.setWantsBestResolutionOpenGLSurface(!pl_attrs.disallow_hidpi); // On Mojave, views automatically become layer-backed shortly after being added to @@ -462,7 +472,7 @@ impl WinitWindow { // the view and its associated OpenGL context. To work around this, on Mojave we // explicitly make the view layer-backed up front so that AppKit doesn't do it // itself and break the association with its context. - if NSAppKitVersion::current().floor() > NSAppKitVersion::NSAppKitVersionNumber10_12 { + if unsafe { NSAppKitVersionNumber }.floor() > NSAppKitVersionNumber10_12 { view.setWantsLayer(true); } @@ -472,7 +482,7 @@ impl WinitWindow { if attrs.transparent { this.setOpaque(false); - this.setBackgroundColor(&NSColor::clear()); + this.setBackgroundColor(unsafe { &NSColor::clearColor() }); } if attrs.blur { @@ -809,7 +819,7 @@ impl WinitWindow { pub fn set_cursor_icon(&self, icon: CursorIcon) { let view = self.view(); - view.set_cursor_icon(NSCursor::from_icon(icon)); + view.set_cursor_icon(cursor_from_icon(icon)); self.invalidateCursorRectsForView(&view); } @@ -960,6 +970,7 @@ impl WinitWindow { #[inline] pub fn set_maximized(&self, maximized: bool) { + let mtm = MainThreadMarker::from(self); let is_zoomed = self.is_zoomed(); if is_zoomed == maximized { return; @@ -988,7 +999,7 @@ impl WinitWindow { } else { // if it's not resizable, we set the frame directly let new_rect = if maximized { - let screen = NSScreen::main().expect("no screen found"); + let screen = NSScreen::mainScreen(mtm).expect("no screen found"); screen.visibleFrame() } else { shared_state.saved_standard_frame() @@ -1011,6 +1022,7 @@ impl WinitWindow { #[inline] pub(crate) fn set_fullscreen(&self, fullscreen: Option) { + let mtm = MainThreadMarker::from(self); let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); if shared_state_lock.is_simple_fullscreen { return; @@ -1042,7 +1054,7 @@ impl WinitWindow { } Fullscreen::Exclusive(video_mode) => video_mode.monitor(), } - .ns_screen() + .ns_screen(mtm) .unwrap(); let old_screen = self.screen().unwrap(); @@ -1318,7 +1330,7 @@ impl WinitWindow { #[inline] // Allow directly accessing the current monitor internally without unwrapping. pub(crate) fn current_monitor_inner(&self) -> Option { - let display_id = self.screen()?.display_id(); + let display_id = get_display_id(&*self.screen()?); Some(MonitorHandle::new(display_id)) } @@ -1582,10 +1594,12 @@ pub(super) fn get_ns_theme() -> Theme { return Theme::Light; } let appearance = app.effectiveAppearance(); - let name = appearance.bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[ - NSString::from_str("NSAppearanceNameAqua"), - NSString::from_str("NSAppearanceNameDarkAqua"), - ])); + let name = appearance + .bestMatchFromAppearancesWithNames(&NSArray::from_id_slice(&[ + NSString::from_str("NSAppearanceNameAqua"), + NSString::from_str("NSAppearanceNameDarkAqua"), + ])) + .unwrap(); match &*name.to_string() { "NSAppearanceNameDarkAqua" => Theme::Dark, _ => Theme::Light, @@ -1601,7 +1615,7 @@ fn set_ns_theme(theme: Option) { Theme::Dark => NSString::from_str("NSAppearanceNameDarkAqua"), Theme::Light => NSString::from_str("NSAppearanceNameAqua"), }; - NSAppearance::appearanceNamed(&name) + NSAppearance::appearanceNamed(&name).unwrap() }); app.setAppearance(appearance.as_ref().map(|a| a.as_ref())); } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index e5a62deced..0bc982e4fa 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -2,6 +2,7 @@ use std::cell::Cell; use std::ptr; +use icrate::AppKit::{NSFilenamesPboardType, NSPasteboard}; use icrate::Foundation::{NSArray, NSObject, NSSize, NSString}; use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::AnyObject; @@ -9,9 +10,7 @@ use objc2::{ class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, }; -use super::appkit::{ - NSApplicationPresentationOptions, NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionState, -}; +use super::appkit::{NSApplicationPresentationOptions, NSWindowOcclusionState}; use super::{ app_state::AppState, util, @@ -144,7 +143,7 @@ declare_class!( use std::path::PathBuf; let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); + let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Id> = unsafe { Id::cast(filenames) }; filenames.into_iter().for_each(|file| { @@ -170,7 +169,7 @@ declare_class!( use std::path::PathBuf; let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }); + let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); let filenames: Id> = unsafe { Id::cast(filenames) }; filenames.into_iter().for_each(|file| {