From d5109dd9f5239c0029e05ed8ae3293728c9422d9 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 23 Dec 2023 21:22:23 +0100 Subject: [PATCH 1/4] Use icrate's window structs and enums --- src/platform_impl/macos/appkit/mod.rs | 5 +- src/platform_impl/macos/appkit/window.rs | 201 +-------------------- src/platform_impl/macos/ffi.rs | 42 +++++ src/platform_impl/macos/window.rs | 136 +++++++------- src/platform_impl/macos/window_delegate.rs | 22 +-- 5 files changed, 128 insertions(+), 278 deletions(-) diff --git a/src/platform_impl/macos/appkit/mod.rs b/src/platform_impl/macos/appkit/mod.rs index 715bfa98f6..c636da481e 100644 --- a/src/platform_impl/macos/appkit/mod.rs +++ b/src/platform_impl/macos/appkit/mod.rs @@ -20,10 +20,7 @@ pub(crate) use self::application::{ NSRequestUserAttentionType, }; pub(crate) use self::tab_group::NSWindowTabGroup; -pub(crate) use self::window::{ - NSBackingStoreType, NSWindow, NSWindowButton, NSWindowLevel, NSWindowOcclusionState, - NSWindowSharingType, NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, -}; +pub(crate) use self::window::NSWindow; #[link(name = "AppKit", kind = "framework")] extern "C" {} diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs index 748665fc5f..5ebcf21c8d 100644 --- a/src/platform_impl/macos/appkit/window.rs +++ b/src/platform_impl/macos/appkit/window.rs @@ -1,8 +1,11 @@ -use icrate::AppKit::{NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView}; +use icrate::AppKit::{ + NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowButton, + NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, + NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, +}; use icrate::Foundation::{ - CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, NSUInteger, + CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, }; -use objc2::encode::{Encode, Encoding}; use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{extern_class, extern_methods, mutability, ClassType}; @@ -245,195 +248,3 @@ extern_methods!( pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode); } ); - -#[allow(dead_code)] -#[repr(isize)] // NSInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSWindowTitleVisibility { - #[doc(alias = "NSWindowTitleVisible")] - Visible = 0, - #[doc(alias = "NSWindowTitleHidden")] - Hidden = 1, -} - -unsafe impl Encode for NSWindowTitleVisibility { - const ENCODING: Encoding = NSInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(usize)] // NSUInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSWindowButton { - #[doc(alias = "NSWindowCloseButton")] - Close = 0, - #[doc(alias = "NSWindowMiniaturizeButton")] - Miniaturize = 1, - #[doc(alias = "NSWindowZoomButton")] - Zoom = 2, - #[doc(alias = "NSWindowToolbarButton")] - Toolbar = 3, - #[doc(alias = "NSWindowDocumentIconButton")] - DocumentIcon = 4, - #[doc(alias = "NSWindowDocumentVersionsButton")] - DocumentVersions = 6, - #[doc(alias = "NSWindowFullScreenButton")] - #[deprecated = "Deprecated since macOS 10.12"] - FullScreen = 7, -} - -unsafe impl Encode for NSWindowButton { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -// CGWindowLevel.h -// -// Note: There are two different things at play in this header: -// `CGWindowLevel` and `CGWindowLevelKey`. -// -// It seems like there was a push towards using "key" values instead of the -// raw window level values, and then you were supposed to use -// `CGWindowLevelForKey` to get the actual level. -// -// But the values that `NSWindowLevel` has are compiled in, and as such has -// to remain ABI compatible, so they're safe for us to hardcode as well. -#[allow(dead_code)] -mod window_level { - const kCGNumReservedWindowLevels: i32 = 16; - const kCGNumReservedBaseWindowLevels: i32 = 5; - - pub const kCGBaseWindowLevel: i32 = i32::MIN; - pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels; - pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels; - - pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20; - pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20; - pub const kCGBackstopMenuLevel: i32 = -20; - pub const kCGNormalWindowLevel: i32 = 0; - pub const kCGFloatingWindowLevel: i32 = 3; - pub const kCGTornOffMenuWindowLevel: i32 = 3; - pub const kCGModalPanelWindowLevel: i32 = 8; - pub const kCGUtilityWindowLevel: i32 = 19; - pub const kCGDockWindowLevel: i32 = 20; - pub const kCGMainMenuWindowLevel: i32 = 24; - pub const kCGStatusWindowLevel: i32 = 25; - pub const kCGPopUpMenuWindowLevel: i32 = 101; - pub const kCGOverlayWindowLevel: i32 = 102; - pub const kCGHelpWindowLevel: i32 = 200; - pub const kCGDraggingWindowLevel: i32 = 500; - pub const kCGScreenSaverWindowLevel: i32 = 1000; - pub const kCGAssistiveTechHighWindowLevel: i32 = 1500; - pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1; -} -use window_level::*; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[repr(transparent)] -pub struct NSWindowLevel(pub NSInteger); - -#[allow(dead_code)] -impl NSWindowLevel { - #[doc(alias = "BelowNormalWindowLevel")] - pub const BELOW_NORMAL: Self = Self((kCGNormalWindowLevel - 1) as _); - #[doc(alias = "NSNormalWindowLevel")] - pub const Normal: Self = Self(kCGNormalWindowLevel as _); - #[doc(alias = "NSFloatingWindowLevel")] - pub const Floating: Self = Self(kCGFloatingWindowLevel as _); - #[doc(alias = "NSTornOffMenuWindowLevel")] - pub const TornOffMenu: Self = Self(kCGTornOffMenuWindowLevel as _); - #[doc(alias = "NSModalPanelWindowLevel")] - pub const ModalPanel: Self = Self(kCGModalPanelWindowLevel as _); - #[doc(alias = "NSMainMenuWindowLevel")] - pub const MainMenu: Self = Self(kCGMainMenuWindowLevel as _); - #[doc(alias = "NSStatusWindowLevel")] - pub const Status: Self = Self(kCGStatusWindowLevel as _); - #[doc(alias = "NSPopUpMenuWindowLevel")] - pub const PopUpMenu: Self = Self(kCGPopUpMenuWindowLevel as _); - #[doc(alias = "NSScreenSaverWindowLevel")] - pub const ScreenSaver: Self = Self(kCGScreenSaverWindowLevel as _); -} - -unsafe impl Encode for NSWindowLevel { - const ENCODING: Encoding = NSInteger::ENCODING; -} - -bitflags! { - #[derive(Clone, Copy)] - pub struct NSWindowOcclusionState: NSUInteger { - const NSWindowOcclusionStateVisible = 1 << 1; - } -} - -unsafe impl Encode for NSWindowOcclusionState { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -bitflags! { - #[derive(Debug, Clone, Copy)] - pub struct NSWindowStyleMask: NSUInteger { - const NSBorderlessWindowMask = 0; - const NSTitledWindowMask = 1 << 0; - const NSClosableWindowMask = 1 << 1; - const NSMiniaturizableWindowMask = 1 << 2; - const NSResizableWindowMask = 1 << 3; - const NSTexturedBackgroundWindowMask = 1 << 8; - const NSUnifiedTitleAndToolbarWindowMask = 1 << 12; - const NSFullScreenWindowMask = 1 << 14; - const NSFullSizeContentViewWindowMask = 1 << 15; - } -} - -unsafe impl Encode for NSWindowStyleMask { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(usize)] // NSUInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSBackingStoreType { - NSBackingStoreRetained = 0, - NSBackingStoreNonretained = 1, - NSBackingStoreBuffered = 2, -} - -unsafe impl Encode for NSBackingStoreType { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(usize)] // NSUInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSWindowSharingType { - NSWindowSharingNone = 0, - NSWindowSharingReadOnly = 1, - NSWindowSharingReadWrite = 2, -} - -unsafe impl Encode for NSWindowSharingType { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(isize)] // NSInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSWindowOrderingMode { - NSWindowAbove = 1, - NSWindowBelow = -1, - NSWindowOut = 0, -} - -unsafe impl Encode for NSWindowOrderingMode { - const ENCODING: Encoding = NSInteger::ENCODING; -} - -#[allow(dead_code)] -#[repr(isize)] // NSInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSWindowTabbingMode { - NSWindowTabbingModeAutomatic = 0, - NSWindowTabbingModeDisallowed = 2, - NSWindowTabbingModePreferred = 1, -} - -unsafe impl Encode for NSWindowTabbingMode { - const ENCODING: Encoding = NSInteger::ENCODING; -} diff --git a/src/platform_impl/macos/ffi.rs b/src/platform_impl/macos/ffi.rs index 5396d32584..fed212ca4e 100644 --- a/src/platform_impl/macos/ffi.rs +++ b/src/platform_impl/macos/ffi.rs @@ -210,3 +210,45 @@ extern "C" { unicodeString: *mut UniChar, ) -> OSStatus; } + +// CGWindowLevel.h +// +// Note: There are two different things at play in this header: +// `CGWindowLevel` and `CGWindowLevelKey`. +// +// It seems like there was a push towards using "key" values instead of the +// raw window level values, and then you were supposed to use +// `CGWindowLevelForKey` to get the actual level. +// +// But the values that `NSWindowLevel` has are compiled in, and as such has +// to remain ABI compatible, so they're safe for us to hardcode as well. +#[allow(dead_code, non_upper_case_globals)] +mod window_level { + const kCGNumReservedWindowLevels: i32 = 16; + const kCGNumReservedBaseWindowLevels: i32 = 5; + + pub const kCGBaseWindowLevel: i32 = i32::MIN; + pub const kCGMinimumWindowLevel: i32 = kCGBaseWindowLevel + kCGNumReservedBaseWindowLevels; + pub const kCGMaximumWindowLevel: i32 = i32::MAX - kCGNumReservedWindowLevels; + + pub const kCGDesktopWindowLevel: i32 = kCGMinimumWindowLevel + 20; + pub const kCGDesktopIconWindowLevel: i32 = kCGDesktopWindowLevel + 20; + pub const kCGBackstopMenuLevel: i32 = -20; + pub const kCGNormalWindowLevel: i32 = 0; + pub const kCGFloatingWindowLevel: i32 = 3; + pub const kCGTornOffMenuWindowLevel: i32 = 3; + pub const kCGModalPanelWindowLevel: i32 = 8; + pub const kCGUtilityWindowLevel: i32 = 19; + pub const kCGDockWindowLevel: i32 = 20; + pub const kCGMainMenuWindowLevel: i32 = 24; + pub const kCGStatusWindowLevel: i32 = 25; + pub const kCGPopUpMenuWindowLevel: i32 = 101; + pub const kCGOverlayWindowLevel: i32 = 102; + pub const kCGHelpWindowLevel: i32 = 200; + pub const kCGDraggingWindowLevel: i32 = 500; + pub const kCGScreenSaverWindowLevel: i32 = 1000; + pub const kCGAssistiveTechHighWindowLevel: i32 = 1500; + pub const kCGCursorWindowLevel: i32 = kCGMaximumWindowLevel - 1; +} + +pub use window_level::*; diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 98d0ca8a74..746b38c817 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -29,26 +29,28 @@ use crate::{ }, }; use core_graphics::display::{CGDisplay, CGPoint}; -use icrate::AppKit::NSWindowAbove; use icrate::AppKit::{ - NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSColor, - NSFilenamesPboardType, NSResponder, NSScreen, NSView, + NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSBackingStoreBuffered, + NSColor, NSFilenamesPboardType, NSResponder, NSScreen, NSView, NSWindowAbove, + NSWindowCloseButton, NSWindowFullScreenButton, NSWindowLevel, NSWindowMiniaturizeButton, + NSWindowSharingNone, NSWindowSharingReadOnly, NSWindowStyleMask, NSWindowStyleMaskBorderless, + NSWindowStyleMaskClosable, NSWindowStyleMaskFullSizeContentView, + NSWindowStyleMaskMiniaturizable, NSWindowStyleMaskResizable, NSWindowStyleMaskTitled, + NSWindowTabbingModePreferred, NSWindowTitleHidden, NSWindowZoomButton, }; use icrate::Foundation::{ - CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSInteger, NSObject, NSPoint, - NSRect, NSSize, NSString, + CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSObject, NSPoint, NSRect, + NSSize, NSString, }; use objc2::rc::{autoreleasepool, Id}; use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; use super::appkit::{ - NSApp, NSApplicationPresentationOptions, NSBackingStoreType, NSRequestUserAttentionType, - NSWindow, NSWindowButton, NSWindowLevel, NSWindowSharingType, NSWindowStyleMask, - NSWindowTabbingMode, NSWindowTitleVisibility, + NSApp, NSApplicationPresentationOptions, NSRequestUserAttentionType, NSWindow, }; use super::cursor::cursor_from_icon; -use super::ffi::CGSMainConnectionID; -use super::ffi::CGSSetWindowBackgroundBlurRadius; +use super::ffi::kCGFloatingWindowLevel; +use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius}; use super::monitor::get_display_id; pub(crate) struct Window { @@ -319,31 +321,31 @@ impl WinitWindow { // if decorations is set to false, ignore pl_attrs // // if the titlebar is hidden, ignore other pl_attrs - NSWindowStyleMask::NSBorderlessWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask + NSWindowStyleMaskBorderless + | NSWindowStyleMaskResizable + | NSWindowStyleMaskMiniaturizable } else { // default case, resizable window with titlebar and titlebar buttons - NSWindowStyleMask::NSClosableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSTitledWindowMask + NSWindowStyleMaskClosable + | NSWindowStyleMaskMiniaturizable + | NSWindowStyleMaskResizable + | NSWindowStyleMaskTitled }; if !attrs.resizable { - masks &= !NSWindowStyleMask::NSResizableWindowMask; + masks &= !NSWindowStyleMaskResizable; } if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) { - masks &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + masks &= !NSWindowStyleMaskMiniaturizable; } if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) { - masks &= !NSWindowStyleMask::NSClosableWindowMask; + masks &= !NSWindowStyleMaskClosable; } if pl_attrs.fullsize_content_view { - masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask; + masks |= NSWindowStyleMaskFullSizeContentView; } let state = SharedState { @@ -359,7 +361,7 @@ impl WinitWindow { super(this), initWithContentRect: frame, styleMask: masks, - backing: NSBackingStoreType::NSBackingStoreBuffered, + backing: NSBackingStoreBuffered, defer: false, ] }; @@ -387,26 +389,26 @@ impl WinitWindow { if let Some(identifier) = pl_attrs.tabbing_identifier { this.setTabbingIdentifier(&NSString::from_str(&identifier)); - this.setTabbingMode(NSWindowTabbingMode::NSWindowTabbingModePreferred); + this.setTabbingMode(NSWindowTabbingModePreferred); } if attrs.content_protected { - this.setSharingType(NSWindowSharingType::NSWindowSharingNone); + this.setSharingType(NSWindowSharingNone); } if pl_attrs.titlebar_transparent { this.setTitlebarAppearsTransparent(true); } if pl_attrs.title_hidden { - this.setTitleVisibility(NSWindowTitleVisibility::Hidden); + this.setTitleVisibility(NSWindowTitleHidden); } if pl_attrs.titlebar_buttons_hidden { for titlebar_button in &[ #[allow(deprecated)] - NSWindowButton::FullScreen, - NSWindowButton::Miniaturize, - NSWindowButton::Close, - NSWindowButton::Zoom, + NSWindowFullScreenButton, + NSWindowMiniaturizeButton, + NSWindowCloseButton, + NSWindowZoomButton, ] { if let Some(button) = this.standardWindowButton(*titlebar_button) { button.setHidden(true); @@ -418,7 +420,7 @@ impl WinitWindow { } if !attrs.enabled_buttons.contains(WindowButtons::MAXIMIZE) { - if let Some(button) = this.standardWindowButton(NSWindowButton::Zoom) { + if let Some(button) = this.standardWindowButton(NSWindowZoomButton) { button.setEnabled(false); } } @@ -756,9 +758,9 @@ impl WinitWindow { if !fullscreen { let mut mask = self.styleMask(); if resizable { - mask |= NSWindowStyleMask::NSResizableWindowMask; + mask |= NSWindowStyleMaskResizable; } else { - mask &= !NSWindowStyleMask::NSResizableWindowMask; + mask &= !NSWindowStyleMaskResizable; } self.set_style_mask(mask); } @@ -775,15 +777,15 @@ impl WinitWindow { let mut mask = self.styleMask(); if buttons.contains(WindowButtons::CLOSE) { - mask |= NSWindowStyleMask::NSClosableWindowMask; + mask |= NSWindowStyleMaskClosable; } else { - mask &= !NSWindowStyleMask::NSClosableWindowMask; + mask &= !NSWindowStyleMaskClosable; } if buttons.contains(WindowButtons::MINIMIZE) { - mask |= NSWindowStyleMask::NSMiniaturizableWindowMask; + mask |= NSWindowStyleMaskMiniaturizable; } else { - mask &= !NSWindowStyleMask::NSMiniaturizableWindowMask; + mask &= !NSWindowStyleMaskMiniaturizable; } // This must happen before the button's "enabled" status has been set, @@ -793,7 +795,7 @@ impl WinitWindow { // We edit the button directly instead of using `NSResizableWindowMask`, // since that mask also affect the resizability of the window (which is // controllable by other means in `winit`). - if let Some(button) = self.standardWindowButton(NSWindowButton::Zoom) { + if let Some(button) = self.standardWindowButton(NSWindowZoomButton) { button.setEnabled(buttons.contains(WindowButtons::MAXIMIZE)); } } @@ -805,7 +807,7 @@ impl WinitWindow { buttons |= WindowButtons::MINIMIZE; } if self - .standardWindowButton(NSWindowButton::Zoom) + .standardWindowButton(NSWindowZoomButton) .map(|b| b.isEnabled()) .unwrap_or(true) { @@ -903,9 +905,8 @@ impl WinitWindow { // we make it resizable temporalily. let curr_mask = self.styleMask(); - let required = - NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask; - let needs_temp_mask = !curr_mask.contains(required); + let required = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable; + let needs_temp_mask = !mask_contains(curr_mask, required); if needs_temp_mask { self.set_style_mask(required); } @@ -926,9 +927,9 @@ impl WinitWindow { .take() .unwrap_or_else(|| self.styleMask()); if shared_state.resizable { - base_mask | NSWindowStyleMask::NSResizableWindowMask + base_mask | NSWindowStyleMaskResizable } else { - base_mask & !NSWindowStyleMask::NSResizableWindowMask + base_mask & !NSWindowStyleMaskResizable } } @@ -989,10 +990,7 @@ impl WinitWindow { return; } - if self - .styleMask() - .contains(NSWindowStyleMask::NSResizableWindowMask) - { + if mask_contains(self.styleMask(), NSWindowStyleMaskResizable) { drop(shared_state); // Just use the native zoom if resizable self.zoom(None); @@ -1143,7 +1141,7 @@ impl WinitWindow { // Window level must be restored from `CGShieldingWindowLevel() // + 1` back to normal in order for `toggleFullScreen` to do // anything - window.setLevel(NSWindowLevel::Normal); + window.setLevel(kCGNormalWindowLevel as NSWindowLevel); window.toggleFullScreen(None); } @@ -1153,9 +1151,8 @@ impl WinitWindow { // set a normal style temporarily. The previous state will be // restored in `WindowDelegate::window_did_exit_fullscreen`. let curr_mask = self.styleMask(); - let required = NSWindowStyleMask::NSTitledWindowMask - | NSWindowStyleMask::NSResizableWindowMask; - if !curr_mask.contains(required) { + let required = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable; + if !mask_contains(curr_mask, required) { self.set_style_mask(required); self.lock_shared_state("set_fullscreen").saved_style = Some(curr_mask); } @@ -1194,8 +1191,7 @@ impl WinitWindow { | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; app.setPresentationOptions(presentation_options); - let window_level = - NSWindowLevel(unsafe { ffi::CGShieldingWindowLevel() } as NSInteger + 1); + let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1; self.setLevel(window_level); } (Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => { @@ -1219,7 +1215,7 @@ impl WinitWindow { // Restore the normal window level following the Borderless fullscreen // `CGShieldingWindowLevel() + 1` hack. - self.setLevel(NSWindowLevel::Normal); + self.setLevel(kCGNormalWindowLevel as NSWindowLevel); } _ => {} }; @@ -1247,15 +1243,15 @@ impl WinitWindow { let new_mask = { let mut new_mask = if decorations { - NSWindowStyleMask::NSClosableWindowMask - | NSWindowStyleMask::NSMiniaturizableWindowMask - | NSWindowStyleMask::NSResizableWindowMask - | NSWindowStyleMask::NSTitledWindowMask + NSWindowStyleMaskClosable + | NSWindowStyleMaskMiniaturizable + | NSWindowStyleMaskResizable + | NSWindowStyleMaskTitled } else { - NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask + NSWindowStyleMaskBorderless | NSWindowStyleMaskResizable }; if !resizable { - new_mask &= !NSWindowStyleMask::NSResizableWindowMask; + new_mask &= !NSWindowStyleMaskResizable; } new_mask }; @@ -1270,9 +1266,9 @@ impl WinitWindow { #[inline] pub fn set_window_level(&self, level: WindowLevel) { let level = match level { - WindowLevel::AlwaysOnTop => NSWindowLevel::Floating, - WindowLevel::AlwaysOnBottom => NSWindowLevel::BELOW_NORMAL, - WindowLevel::Normal => NSWindowLevel::Normal, + WindowLevel::AlwaysOnTop => kCGFloatingWindowLevel as NSWindowLevel, + WindowLevel::AlwaysOnBottom => (kCGNormalWindowLevel - 1) as NSWindowLevel, + WindowLevel::Normal => kCGNormalWindowLevel as NSWindowLevel, }; self.setLevel(level); } @@ -1421,9 +1417,9 @@ impl WinitWindow { #[inline] pub fn set_content_protected(&self, protected: bool) { self.setSharingType(if protected { - NSWindowSharingType::NSWindowSharingNone + NSWindowSharingNone } else { - NSWindowSharingType::NSWindowSharingReadOnly + NSWindowSharingReadOnly }) } @@ -1480,15 +1476,15 @@ impl WindowExtMacOS for WinitWindow { app.setPresentationOptions(presentation_options); // Hide the titlebar - self.toggle_style_mask(NSWindowStyleMask::NSTitledWindowMask, false); + self.toggle_style_mask(NSWindowStyleMaskTitled, false); // Set the window frame to the screen frame size let screen = self.screen().expect("expected screen to be available"); self.setFrame_display(screen.frame(), true); // Fullscreen windows can't be resized, minimized, or moved - self.toggle_style_mask(NSWindowStyleMask::NSMiniaturizableWindowMask, false); - self.toggle_style_mask(NSWindowStyleMask::NSResizableWindowMask, false); + self.toggle_style_mask(NSWindowStyleMaskMiniaturizable, false); + self.toggle_style_mask(NSWindowStyleMaskResizable, false); self.setMovable(false); true @@ -1587,6 +1583,10 @@ impl WindowExtMacOS for WinitWindow { } } +fn mask_contains(mask: NSWindowStyleMask, value: NSWindowStyleMask) -> bool { + mask & value == value +} + pub(super) fn get_ns_theme() -> Theme { let app = NSApp(); let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] }; diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 0bc982e4fa..97d82f00f3 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use std::ptr; -use icrate::AppKit::{NSFilenamesPboardType, NSPasteboard}; +use icrate::AppKit::{NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionStateVisible}; use icrate::Foundation::{NSArray, NSObject, NSSize, NSString}; use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::AnyObject; @@ -10,7 +10,7 @@ use objc2::{ class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, }; -use super::appkit::{NSApplicationPresentationOptions, NSWindowOcclusionState}; +use super::appkit::NSApplicationPresentationOptions; use super::{ app_state::AppState, util, @@ -143,7 +143,9 @@ declare_class!( use std::path::PathBuf; let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); + let filenames = pb + .propertyListForType(unsafe { NSFilenamesPboardType }) + .unwrap(); let filenames: Id> = unsafe { Id::cast(filenames) }; filenames.into_iter().for_each(|file| { @@ -169,7 +171,9 @@ declare_class!( use std::path::PathBuf; let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap(); + let filenames = pb + .propertyListForType(unsafe { NSFilenamesPboardType }) + .unwrap(); let filenames: Id> = unsafe { Id::cast(filenames) }; filenames.into_iter().for_each(|file| { @@ -343,13 +347,9 @@ declare_class!( #[method(windowDidChangeOcclusionState:)] fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) { trace_scope!("windowDidChangeOcclusionState:"); - self.queue_event(WindowEvent::Occluded( - !self - .ivars() - .window - .occlusionState() - .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible), - )) + let visible = self.ivars().window.occlusionState() & NSWindowOcclusionStateVisible + == NSWindowOcclusionStateVisible; + self.queue_event(WindowEvent::Occluded(!visible)); } // Observe theme change From 6a81fc91e76c413a7af00024f0cb99fa0895f91d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 23 Dec 2023 21:33:43 +0100 Subject: [PATCH 2/4] Properly implement protocols --- src/platform_impl/macos/app_delegate.rs | 12 +- src/platform_impl/macos/event_loop.rs | 1 + src/platform_impl/macos/window_delegate.rs | 175 +++++++++++---------- 3 files changed, 101 insertions(+), 87 deletions(-) diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 9ab35b4edd..3bee596b02 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -1,4 +1,5 @@ -use icrate::Foundation::NSObject; +use icrate::AppKit::NSApplicationDelegate; +use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol}; use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; @@ -18,7 +19,7 @@ declare_class!( unsafe impl ClassType for ApplicationDelegate { type Super = NSObject; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "WinitApplicationDelegate"; } @@ -26,7 +27,9 @@ declare_class!( type Ivars = State; } - unsafe impl ApplicationDelegate { + unsafe impl NSObjectProtocol for ApplicationDelegate {} + + unsafe impl NSApplicationDelegate for ApplicationDelegate { #[method(applicationDidFinishLaunching:)] fn did_finish_launching(&self, _sender: Option<&AnyObject>) { trace_scope!("applicationDidFinishLaunching:"); @@ -48,11 +51,12 @@ declare_class!( impl ApplicationDelegate { pub(super) fn new( + mtm: MainThreadMarker, activation_policy: NSApplicationActivationPolicy, default_menu: bool, activate_ignoring_other_apps: bool, ) -> Id { - let this = Self::alloc().set_ivars(State { + let this = mtm.alloc().set_ivars(State { activation_policy, default_menu, activate_ignoring_other_apps, diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index f73c4a9c4d..ce12ee0d6c 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -206,6 +206,7 @@ impl EventLoop { ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited, }; let delegate = ApplicationDelegate::new( + mtm, activation_policy, attributes.default_menu, attributes.activate_ignoring_other_apps, diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 97d82f00f3..44af539bf2 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -2,8 +2,11 @@ use std::cell::Cell; use std::ptr; -use icrate::AppKit::{NSFilenamesPboardType, NSPasteboard, NSWindowOcclusionStateVisible}; -use icrate::Foundation::{NSArray, NSObject, NSSize, NSString}; +use icrate::AppKit::{ + NSDraggingDestination, NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, + NSWindowOcclusionStateVisible, +}; +use icrate::Foundation::{MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSSize, NSString}; use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::AnyObject; use objc2::{ @@ -43,7 +46,7 @@ declare_class!( unsafe impl ClassType for WinitWindowDelegate { type Super = NSObject; - type Mutability = mutability::InteriorMutable; + type Mutability = mutability::MainThreadOnly; const NAME: &'static str = "WinitWindowDelegate"; } @@ -51,8 +54,9 @@ declare_class!( type Ivars = State; } - // NSWindowDelegate + NSDraggingDestination protocols - unsafe impl WinitWindowDelegate { + unsafe impl NSObjectProtocol for WinitWindowDelegate {} + + unsafe impl NSWindowDelegate for WinitWindowDelegate { #[method(windowShouldClose:)] fn window_should_close(&self, _: Option<&AnyObject>) -> bool { trace_scope!("windowShouldClose:"); @@ -135,68 +139,6 @@ declare_class!( self.queue_event(WindowEvent::Focused(false)); } - /// Invoked when the dragged image enters destination bounds or frame - #[method(draggingEntered:)] - fn dragging_entered(&self, sender: &NSObject) -> bool { - trace_scope!("draggingEntered:"); - - use std::path::PathBuf; - - let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb - .propertyListForType(unsafe { NSFilenamesPboardType }) - .unwrap(); - let filenames: Id> = unsafe { Id::cast(filenames) }; - - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::HoveredFile(path)); - }); - - true - } - - /// Invoked when the image is released - #[method(prepareForDragOperation:)] - fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool { - trace_scope!("prepareForDragOperation:"); - true - } - - /// Invoked after the released image has been removed from the screen - #[method(performDragOperation:)] - fn perform_drag_operation(&self, sender: &NSObject) -> bool { - trace_scope!("performDragOperation:"); - - use std::path::PathBuf; - - let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; - let filenames = pb - .propertyListForType(unsafe { NSFilenamesPboardType }) - .unwrap(); - let filenames: Id> = unsafe { Id::cast(filenames) }; - - filenames.into_iter().for_each(|file| { - let path = PathBuf::from(file.to_string()); - self.queue_event(WindowEvent::DroppedFile(path)); - }); - - true - } - - /// Invoked when the dragging operation is complete - #[method(concludeDragOperation:)] - fn conclude_drag_operation(&self, _sender: Option<&NSObject>) { - trace_scope!("concludeDragOperation:"); - } - - /// Invoked when the dragging operation is cancelled - #[method(draggingExited:)] - fn dragging_exited(&self, _sender: Option<&NSObject>) { - trace_scope!("draggingExited:"); - self.queue_event(WindowEvent::HoveredFileCancelled); - } - /// Invoked when before enter fullscreen #[method(windowWillEnterFullScreen:)] fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) { @@ -352,6 +294,87 @@ declare_class!( self.queue_event(WindowEvent::Occluded(!visible)); } + #[method(windowDidChangeScreen:)] + fn window_did_change_screen(&self, _: Option<&AnyObject>) { + trace_scope!("windowDidChangeScreen:"); + let is_simple_fullscreen = self + .ivars() + .window + .lock_shared_state("window_did_change_screen") + .is_simple_fullscreen; + if is_simple_fullscreen { + if let Some(screen) = self.ivars().window.screen() { + self.ivars().window.setFrame_display(screen.frame(), true); + } + } + } + } + + unsafe impl NSDraggingDestination for WinitWindowDelegate { + /// Invoked when the dragged image enters destination bounds or frame + #[method(draggingEntered:)] + fn dragging_entered(&self, sender: &NSObject) -> bool { + trace_scope!("draggingEntered:"); + + use std::path::PathBuf; + + let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; + let filenames = pb + .propertyListForType(unsafe { NSFilenamesPboardType }) + .unwrap(); + let filenames: Id> = unsafe { Id::cast(filenames) }; + + filenames.into_iter().for_each(|file| { + let path = PathBuf::from(file.to_string()); + self.queue_event(WindowEvent::HoveredFile(path)); + }); + + true + } + + /// Invoked when the image is released + #[method(prepareForDragOperation:)] + fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool { + trace_scope!("prepareForDragOperation:"); + true + } + + /// Invoked after the released image has been removed from the screen + #[method(performDragOperation:)] + fn perform_drag_operation(&self, sender: &NSObject) -> bool { + trace_scope!("performDragOperation:"); + + use std::path::PathBuf; + + let pb: Id = unsafe { msg_send_id![sender, draggingPasteboard] }; + let filenames = pb + .propertyListForType(unsafe { NSFilenamesPboardType }) + .unwrap(); + let filenames: Id> = unsafe { Id::cast(filenames) }; + + filenames.into_iter().for_each(|file| { + let path = PathBuf::from(file.to_string()); + self.queue_event(WindowEvent::DroppedFile(path)); + }); + + true + } + + /// Invoked when the dragging operation is complete + #[method(concludeDragOperation:)] + fn conclude_drag_operation(&self, _sender: Option<&NSObject>) { + trace_scope!("concludeDragOperation:"); + } + + /// Invoked when the dragging operation is cancelled + #[method(draggingExited:)] + fn dragging_exited(&self, _sender: Option<&NSObject>) { + trace_scope!("draggingExited:"); + self.queue_event(WindowEvent::HoveredFileCancelled); + } + } + + unsafe impl WinitWindowDelegate { // Observe theme change #[method(effectiveAppearanceDidChange:)] fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) { @@ -380,28 +403,14 @@ declare_class!( self.queue_event(WindowEvent::ThemeChanged(theme)); } } - - #[method(windowDidChangeScreen:)] - fn window_did_change_screen(&self, _: Option<&AnyObject>) { - trace_scope!("windowDidChangeScreen:"); - let is_simple_fullscreen = self - .ivars() - .window - .lock_shared_state("window_did_change_screen") - .is_simple_fullscreen; - if is_simple_fullscreen { - if let Some(screen) = self.ivars().window.screen() { - self.ivars().window.setFrame_display(screen.frame(), true); - } - } - } } ); impl WinitWindowDelegate { pub fn new(window: &WinitWindow, initial_fullscreen: bool) -> Id { + let mtm = MainThreadMarker::from(window); let scale_factor = window.scale_factor(); - let this = Self::alloc().set_ivars(State { + let this = mtm.alloc().set_ivars(State { window: window.retain(), initial_fullscreen: Cell::new(initial_fullscreen), previous_position: Cell::new(None), From 3b7c5e973fc780cfefa2ace72d4a3b7a2c0aa31b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 23 Dec 2023 22:28:28 +0100 Subject: [PATCH 3/4] Use icrate's NSWindow We were previously using undocumented methods on `NSWindowTabGroup` --- src/platform_impl/macos/app.rs | 2 +- src/platform_impl/macos/appkit/application.rs | 4 +- src/platform_impl/macos/appkit/mod.rs | 4 - src/platform_impl/macos/appkit/tab_group.rs | 31 --- src/platform_impl/macos/appkit/window.rs | 250 ------------------ src/platform_impl/macos/event_loop.rs | 7 +- src/platform_impl/macos/window.rs | 57 ++-- src/platform_impl/macos/window_delegate.rs | 6 +- 8 files changed, 34 insertions(+), 327 deletions(-) delete mode 100644 src/platform_impl/macos/appkit/tab_group.rs delete mode 100644 src/platform_impl/macos/appkit/window.rs diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index a360bfa334..c8078ca7da 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -41,7 +41,7 @@ declare_class!( && flags_contains(modifier_flags, NSEventModifierFlagCommand) { if let Some(key_window) = self.keyWindow() { - unsafe { key_window.sendEvent(event) }; + key_window.sendEvent(event); } } else { maybe_dispatch_device_event(event); diff --git a/src/platform_impl/macos/appkit/application.rs b/src/platform_impl/macos/appkit/application.rs index 241e606a30..ec6d2af0c3 100644 --- a/src/platform_impl/macos/appkit/application.rs +++ b/src/platform_impl/macos/appkit/application.rs @@ -1,12 +1,10 @@ -use icrate::AppKit::{NSAppearance, NSEvent, NSMenu, NSResponder}; +use icrate::AppKit::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow}; 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::NSWindow; - extern_class!( #[derive(Debug, PartialEq, Eq, Hash)] pub(crate) struct NSApplication; diff --git a/src/platform_impl/macos/appkit/mod.rs b/src/platform_impl/macos/appkit/mod.rs index c636da481e..669e9ea078 100644 --- a/src/platform_impl/macos/appkit/mod.rs +++ b/src/platform_impl/macos/appkit/mod.rs @@ -12,15 +12,11 @@ #![allow(non_upper_case_globals)] mod application; -mod tab_group; -mod window; pub(crate) use self::application::{ NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions, NSRequestUserAttentionType, }; -pub(crate) use self::tab_group::NSWindowTabGroup; -pub(crate) use self::window::NSWindow; #[link(name = "AppKit", kind = "framework")] extern "C" {} diff --git a/src/platform_impl/macos/appkit/tab_group.rs b/src/platform_impl/macos/appkit/tab_group.rs deleted file mode 100644 index e52bf572e6..0000000000 --- a/src/platform_impl/macos/appkit/tab_group.rs +++ /dev/null @@ -1,31 +0,0 @@ -use icrate::Foundation::{NSArray, NSObject}; -use objc2::rc::Id; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::NSWindow; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSWindowTabGroup; - - unsafe impl ClassType for NSWindowTabGroup { - type Super = NSObject; - type Mutability = mutability::InteriorMutable; - } -); - -extern_methods!( - unsafe impl NSWindowTabGroup { - #[method(selectNextTab)] - pub fn selectNextTab(&self); - - #[method(selectPreviousTab)] - pub fn selectPreviousTab(&self); - - #[method_id(windows)] - pub fn tabbedWindows(&self) -> Option>>; - - #[method(setSelectedWindow:)] - pub fn setSelectedWindow(&self, window: &NSWindow); - } -); diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs deleted file mode 100644 index 5ebcf21c8d..0000000000 --- a/src/platform_impl/macos/appkit/window.rs +++ /dev/null @@ -1,250 +0,0 @@ -use icrate::AppKit::{ - NSButton, NSColor, NSEvent, NSPasteboardType, NSResponder, NSScreen, NSView, NSWindowButton, - NSWindowLevel, NSWindowOcclusionState, NSWindowOrderingMode, NSWindowSharingType, - NSWindowStyleMask, NSWindowTabbingMode, NSWindowTitleVisibility, -}; -use icrate::Foundation::{ - CGFloat, NSArray, NSInteger, NSObject, NSPoint, NSRect, NSSize, NSString, -}; -use objc2::rc::Id; -use objc2::runtime::AnyObject; -use objc2::{extern_class, extern_methods, mutability, ClassType}; - -use super::NSWindowTabGroup; - -extern_class!( - /// Main-Thread-Only! - #[derive(Debug, PartialEq, Eq, Hash)] - pub struct NSWindow; - - unsafe impl ClassType for NSWindow { - #[inherits(NSObject)] - type Super = NSResponder; - type Mutability = mutability::MainThreadOnly; - } -); - -// Documented as "Main Thread Only", but: -// > Thread safe in that you can create and manage them on a secondary thread. -// -// -// -// So could in theory be `Send`, and perhaps also `Sync` - but we would like -// interior mutability on windows, since that's just much easier, and in that -// case, they can't be! - -extern_methods!( - unsafe impl NSWindow { - #[method(frame)] - pub(crate) fn frame(&self) -> NSRect; - - #[method(windowNumber)] - pub(crate) fn windowNumber(&self) -> NSInteger; - - #[method(backingScaleFactor)] - pub(crate) fn backingScaleFactor(&self) -> CGFloat; - - #[method_id(contentView)] - pub(crate) fn contentView(&self) -> Id; - - #[method(setContentView:)] - pub(crate) fn setContentView(&self, view: &NSView); - - #[method(setInitialFirstResponder:)] - pub(crate) fn setInitialFirstResponder(&self, view: &NSView); - - #[method(makeFirstResponder:)] - #[must_use] - pub(crate) fn makeFirstResponder(&self, responder: Option<&NSResponder>) -> bool; - - #[method(contentRectForFrameRect:)] - pub(crate) fn contentRectForFrameRect(&self, windowFrame: NSRect) -> NSRect; - - #[method_id(screen)] - pub(crate) fn screen(&self) -> Option>; - - #[method(setContentSize:)] - pub(crate) fn setContentSize(&self, contentSize: NSSize); - - #[method(setFrameTopLeftPoint:)] - pub(crate) fn setFrameTopLeftPoint(&self, point: NSPoint); - - #[method(setMinSize:)] - pub(crate) fn setMinSize(&self, minSize: NSSize); - - #[method(setMaxSize:)] - pub(crate) fn setMaxSize(&self, maxSize: NSSize); - - #[method(setResizeIncrements:)] - pub(crate) fn setResizeIncrements(&self, increments: NSSize); - - #[method(contentResizeIncrements)] - pub(crate) fn contentResizeIncrements(&self) -> NSSize; - - #[method(setContentResizeIncrements:)] - pub(crate) fn setContentResizeIncrements(&self, increments: NSSize); - - #[method(setFrame:display:)] - pub(crate) fn setFrame_display(&self, frameRect: NSRect, flag: bool); - - #[method(setMovable:)] - pub(crate) fn setMovable(&self, movable: bool); - - #[method(setSharingType:)] - pub(crate) fn setSharingType(&self, sharingType: NSWindowSharingType); - - #[method(setTabbingMode:)] - pub(crate) fn setTabbingMode(&self, tabbingMode: NSWindowTabbingMode); - - #[method(setOpaque:)] - pub(crate) fn setOpaque(&self, opaque: bool); - - #[method(hasShadow)] - pub(crate) fn hasShadow(&self) -> bool; - - #[method(setHasShadow:)] - pub(crate) fn setHasShadow(&self, has_shadow: bool); - - #[method(setIgnoresMouseEvents:)] - pub(crate) fn setIgnoresMouseEvents(&self, ignores: bool); - - #[method(setBackgroundColor:)] - pub(crate) fn setBackgroundColor(&self, color: &NSColor); - - #[method(styleMask)] - pub(crate) fn styleMask(&self) -> NSWindowStyleMask; - - #[method(setStyleMask:)] - pub(crate) fn setStyleMask(&self, mask: NSWindowStyleMask); - - #[method(registerForDraggedTypes:)] - pub(crate) fn registerForDraggedTypes(&self, types: &NSArray); - - #[method(makeKeyAndOrderFront:)] - pub(crate) fn makeKeyAndOrderFront(&self, sender: Option<&AnyObject>); - - #[method(orderFront:)] - pub(crate) fn orderFront(&self, sender: Option<&AnyObject>); - - #[method(miniaturize:)] - pub(crate) fn miniaturize(&self, sender: Option<&AnyObject>); - - #[method(deminiaturize:)] - pub(crate) fn deminiaturize(&self, sender: Option<&AnyObject>); - - #[method(toggleFullScreen:)] - pub(crate) fn toggleFullScreen(&self, sender: Option<&AnyObject>); - - #[method(orderOut:)] - pub(crate) fn orderOut(&self, sender: Option<&AnyObject>); - - #[method(zoom:)] - pub(crate) fn zoom(&self, sender: Option<&AnyObject>); - - #[method(selectNextKeyView:)] - pub(crate) fn selectNextKeyView(&self, sender: Option<&AnyObject>); - - #[method(selectPreviousKeyView:)] - pub(crate) fn selectPreviousKeyView(&self, sender: Option<&AnyObject>); - - #[method_id(firstResponder)] - pub(crate) fn firstResponder(&self) -> Option>; - - #[method_id(standardWindowButton:)] - pub(crate) fn standardWindowButton(&self, kind: NSWindowButton) -> Option>; - - #[method(setTitle:)] - pub(crate) fn setTitle(&self, title: &NSString); - - #[method_id(title)] - pub(crate) fn title_(&self) -> Id; - - #[method(setReleasedWhenClosed:)] - pub(crate) fn setReleasedWhenClosed(&self, val: bool); - - #[method(setAcceptsMouseMovedEvents:)] - pub(crate) fn setAcceptsMouseMovedEvents(&self, val: bool); - - #[method(setTitlebarAppearsTransparent:)] - pub(crate) fn setTitlebarAppearsTransparent(&self, val: bool); - - #[method(setTitleVisibility:)] - pub(crate) fn setTitleVisibility(&self, visibility: NSWindowTitleVisibility); - - #[method(setMovableByWindowBackground:)] - pub(crate) fn setMovableByWindowBackground(&self, val: bool); - - #[method(setLevel:)] - pub(crate) fn setLevel(&self, level: NSWindowLevel); - - #[method(setAllowsAutomaticWindowTabbing:)] - pub(crate) fn setAllowsAutomaticWindowTabbing(val: bool); - - #[method(setTabbingIdentifier:)] - pub(crate) fn setTabbingIdentifier(&self, identifier: &NSString); - - #[method(setDocumentEdited:)] - pub(crate) fn setDocumentEdited(&self, val: bool); - - #[method(occlusionState)] - pub(crate) fn occlusionState(&self) -> NSWindowOcclusionState; - - #[method(center)] - pub(crate) fn center(&self); - - #[method(isResizable)] - pub(crate) fn isResizable(&self) -> bool; - - #[method(isMiniaturizable)] - pub(crate) fn isMiniaturizable(&self) -> bool; - - #[method(hasCloseBox)] - pub(crate) fn hasCloseBox(&self) -> bool; - - #[method(isMiniaturized)] - pub(crate) fn isMiniaturized(&self) -> bool; - - #[method(isVisible)] - pub(crate) fn isVisible(&self) -> bool; - - #[method(isKeyWindow)] - pub(crate) fn isKeyWindow(&self) -> bool; - - #[method(isZoomed)] - pub(crate) fn isZoomed(&self) -> bool; - - #[method(allowsAutomaticWindowTabbing)] - pub(crate) fn allowsAutomaticWindowTabbing() -> bool; - - #[method(selectNextTab)] - pub(crate) fn selectNextTab(&self); - - #[method_id(tabbingIdentifier)] - pub(crate) fn tabbingIdentifier(&self) -> Id; - - #[method_id(tabGroup)] - pub(crate) fn tabGroup(&self) -> Option>; - - #[method(isDocumentEdited)] - pub(crate) fn isDocumentEdited(&self) -> bool; - - #[method(close)] - pub(crate) fn close(&self); - - #[method(performWindowDragWithEvent:)] - // TODO: Can this actually accept NULL? - pub(crate) fn performWindowDragWithEvent(&self, event: Option<&NSEvent>); - - #[method(invalidateCursorRectsForView:)] - pub(crate) fn invalidateCursorRectsForView(&self, view: &NSView); - - #[method(setDelegate:)] - pub(crate) fn setDelegate(&self, delegate: Option<&NSObject>); - - #[method(sendEvent:)] - pub(crate) unsafe fn sendEvent(&self, event: &NSEvent); - - #[method(addChildWindow:ordered:)] - pub(crate) unsafe fn addChildWindow(&self, child: &NSWindow, ordered: NSWindowOrderingMode); - } -); diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index ce12ee0d6c..43f86cf254 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -17,13 +17,14 @@ use core_foundation::runloop::{ kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp, }; +use icrate::AppKit::NSWindow; use icrate::Foundation::MainThreadMarker; use objc2::rc::{autoreleasepool, Id}; use objc2::runtime::NSObjectProtocol; use objc2::{msg_send_id, ClassType}; use super::{ - appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow}, + appkit::{NSApp, NSApplication, NSApplicationActivationPolicy}, event::dummy_event, }; use crate::{ @@ -134,11 +135,11 @@ impl EventLoopWindowTarget { } pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) { - NSWindow::setAllowsAutomaticWindowTabbing(enabled) + NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm) } pub(crate) fn allows_automatic_window_tabbing(&self) -> bool { - NSWindow::allowsAutomaticWindowTabbing() + NSWindow::allowsAutomaticWindowTabbing(self.mtm) } } diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 746b38c817..36884198b4 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -31,7 +31,7 @@ use crate::{ use core_graphics::display::{CGDisplay, CGPoint}; use icrate::AppKit::{ NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSBackingStoreBuffered, - NSColor, NSFilenamesPboardType, NSResponder, NSScreen, NSView, NSWindowAbove, + NSColor, NSFilenamesPboardType, NSResponder, NSScreen, NSView, NSWindow, NSWindowAbove, NSWindowCloseButton, NSWindowFullScreenButton, NSWindowLevel, NSWindowMiniaturizeButton, NSWindowSharingNone, NSWindowSharingReadOnly, NSWindowStyleMask, NSWindowStyleMaskBorderless, NSWindowStyleMaskClosable, NSWindowStyleMaskFullSizeContentView, @@ -45,9 +45,7 @@ use icrate::Foundation::{ use objc2::rc::{autoreleasepool, Id}; use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; -use super::appkit::{ - NSApp, NSApplicationPresentationOptions, NSRequestUserAttentionType, NSWindow, -}; +use super::appkit::{NSApp, NSApplicationPresentationOptions, NSRequestUserAttentionType}; use super::cursor::cursor_from_icon; use super::ffi::kCGFloatingWindowLevel; use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius}; @@ -370,7 +368,7 @@ impl WinitWindow { // It is very important for correct memory management that we // disable the extra release that would otherwise happen when // calling `close` on the window. - this.setReleasedWhenClosed(false); + unsafe { this.setReleasedWhenClosed(false) }; let resize_increments = match attrs .resize_increments @@ -451,11 +449,9 @@ 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_ordered(&window, NSWindowAbove) }; + unsafe { parent.addChildWindow_ordered(&this, NSWindowAbove) }; } Some(raw) => panic!("Invalid raw window handle {raw:?} on macOS"), None => (), @@ -479,12 +475,12 @@ impl WinitWindow { } // Configure the new view as the "key view" for the window - this.setContentView(&view); - this.setInitialFirstResponder(&view); + this.setContentView(Some(&view)); + this.setInitialFirstResponder(Some(&view)); if attrs.transparent { this.setOpaque(false); - this.setBackgroundColor(unsafe { &NSColor::clearColor() }); + this.setBackgroundColor(Some(unsafe { &NSColor::clearColor() })); } if attrs.blur { @@ -545,9 +541,10 @@ impl WinitWindow { Ok((this, delegate)) } + #[track_caller] pub(super) fn view(&self) -> Id { // SAFETY: The view inside WinitWindow is always `WinitView` - unsafe { Id::cast(self.contentView()) } + unsafe { Id::cast(self.contentView().unwrap()) } } #[track_caller] @@ -562,7 +559,7 @@ impl WinitWindow { self.setStyleMask(mask); // If we don't do this, key handling will break // (at least until the window is clicked again/etc.) - let _ = self.makeFirstResponder(Some(&self.contentView())); + let _ = self.makeFirstResponder(Some(&self.contentView().unwrap())); } } @@ -583,7 +580,7 @@ impl WinitWindow { // NOTE: in general we want to specify the blur radius, but the choice of 80 // should be a reasonable default. let radius = if blur { 80 } else { 0 }; - let window_number = self.windowNumber(); + let window_number = unsafe { self.windowNumber() }; unsafe { CGSSetWindowBackgroundBlurRadius(CGSMainConnectionID(), window_number, radius); } @@ -636,7 +633,7 @@ impl WinitWindow { #[inline] pub fn inner_size(&self) -> PhysicalSize { - let frame = self.contentView().frame(); + let frame = self.contentView().unwrap().frame(); let logical: LogicalSize = (frame.size.width as f64, frame.size.height as f64).into(); let scale_factor = self.scale_factor(); logical.to_physical(scale_factor) @@ -881,8 +878,8 @@ impl WinitWindow { #[inline] pub fn drag_window(&self) -> Result<(), ExternalError> { - let event = NSApp().currentEvent(); - self.performWindowDragWithEvent(event.as_deref()); + let event = NSApp().currentEvent().unwrap(); + self.performWindowDragWithEvent(&event); Ok(()) } @@ -960,7 +957,7 @@ impl WinitWindow { if minimized { self.miniaturize(Some(self)); } else { - self.deminiaturize(Some(self)); + unsafe { self.deminiaturize(Some(self)) }; } } @@ -1351,7 +1348,7 @@ impl WinitWindow { pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle { let mut window_handle = rwh_04::AppKitHandle::empty(); window_handle.ns_window = self as *const Self as *mut _; - window_handle.ns_view = Id::as_ptr(&self.contentView()) as *mut _; + window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _; rwh_04::RawWindowHandle::AppKit(window_handle) } @@ -1360,7 +1357,7 @@ impl WinitWindow { pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle { let mut window_handle = rwh_05::AppKitWindowHandle::empty(); window_handle.ns_window = self as *const Self as *mut _; - window_handle.ns_view = Id::as_ptr(&self.contentView()) as *mut _; + window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _; rwh_05::RawWindowHandle::AppKit(window_handle) } @@ -1374,7 +1371,7 @@ impl WinitWindow { #[inline] pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle { let window_handle = rwh_06::AppKitWindowHandle::new({ - let ptr = Id::as_ptr(&self.contentView()) as *mut _; + let ptr = Id::as_ptr(&self.contentView().unwrap()) as *mut _; std::ptr::NonNull::new(ptr).expect("Id should never be null") }); rwh_06::RawWindowHandle::AppKit(window_handle) @@ -1424,7 +1421,8 @@ impl WinitWindow { } pub fn title(&self) -> String { - self.title_().to_string() + let window: &NSWindow = self; + window.title().to_string() } pub fn reset_dead_keys(&self) { @@ -1533,24 +1531,20 @@ impl WindowExtMacOS for WinitWindow { #[inline] fn select_next_tab(&self) { - if let Some(group) = self.tabGroup() { - group.selectNextTab(); - } + self.selectNextTab(None) } #[inline] fn select_previous_tab(&self) { - if let Some(group) = self.tabGroup() { - group.selectPreviousTab() - } + unsafe { self.selectPreviousTab(None) } } #[inline] fn select_tab_at_index(&self, index: usize) { if let Some(group) = self.tabGroup() { - if let Some(windows) = group.tabbedWindows() { + if let Some(windows) = unsafe { self.tabbedWindows() } { if index < windows.len() { - group.setSelectedWindow(&windows[index]); + group.setSelectedWindow(Some(&windows[index])); } } } @@ -1558,8 +1552,7 @@ impl WindowExtMacOS for WinitWindow { #[inline] fn num_tabs(&self) -> usize { - self.tabGroup() - .and_then(|group| group.tabbedWindows()) + unsafe { self.tabbedWindows() } .map(|windows| windows.len()) .unwrap_or(1) } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 44af539bf2..5a6b238252 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -8,7 +8,7 @@ use icrate::AppKit::{ }; use icrate::Foundation::{MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSSize, NSString}; use objc2::rc::{autoreleasepool, Id}; -use objc2::runtime::AnyObject; +use objc2::runtime::{AnyObject, ProtocolObject}; use objc2::{ class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, }; @@ -421,7 +421,7 @@ impl WinitWindowDelegate { if scale_factor != 1.0 { this.queue_static_scale_factor_changed_event(); } - this.ivars().window.setDelegate(Some(&this)); + window.setDelegate(Some(ProtocolObject::from_ref(&*this))); // Enable theme change event let notification_center: Id = @@ -476,7 +476,7 @@ impl WinitWindowDelegate { } fn view_size(&self) -> LogicalSize { - let size = self.ivars().window.contentView().frame().size; + let size = self.ivars().window.contentView().unwrap().frame().size; LogicalSize::new(size.width as f64, size.height as f64) } } From 98676e88751091beb9f6553ed450eb141a1e2f92 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sat, 23 Dec 2023 22:01:56 +0100 Subject: [PATCH 4/4] Use icrate's NSApplication And clean up some doc comments regarding NSApplication --- src/platform/pump_events.rs | 10 +- src/platform_impl/macos/app.rs | 3 +- src/platform_impl/macos/app_delegate.rs | 3 +- src/platform_impl/macos/app_state.rs | 43 +++--- src/platform_impl/macos/appkit/application.rs | 142 ------------------ src/platform_impl/macos/appkit/mod.rs | 22 --- src/platform_impl/macos/event_loop.rs | 47 +++--- src/platform_impl/macos/menu.rs | 13 +- src/platform_impl/macos/mod.rs | 1 - src/platform_impl/macos/observer.rs | 4 +- src/platform_impl/macos/view.rs | 14 +- src/platform_impl/macos/window.rs | 82 +++++----- src/platform_impl/macos/window_delegate.rs | 15 +- 13 files changed, 122 insertions(+), 277 deletions(-) delete mode 100644 src/platform_impl/macos/appkit/application.rs delete mode 100644 src/platform_impl/macos/appkit/mod.rs diff --git a/src/platform/pump_events.rs b/src/platform/pump_events.rs index 1ea11af648..ccaf148586 100644 --- a/src/platform/pump_events.rs +++ b/src/platform/pump_events.rs @@ -155,19 +155,19 @@ pub trait EventLoopExtPumpEvents { /// - **Windows**: The implementation will use `PeekMessage` when checking for /// window messages to avoid blocking your external event loop. /// - /// - **MacOS**: The implementation works in terms of stopping the global `NSApp` + /// - **MacOS**: The implementation works in terms of stopping the global application /// whenever the application `RunLoop` indicates that it is preparing to block /// and wait for new events. /// /// This is very different to the polling APIs that are available on other /// platforms (the lower level polling primitives on MacOS are private - /// implementation details for `NSApp` which aren't accessible to application - /// developers) + /// implementation details for `NSApplication` which aren't accessible to + /// application developers) /// /// It's likely this will be less efficient than polling on other OSs and - /// it also means the `NSApp` is stopped while outside of the Winit + /// it also means the `NSApplication` is stopped while outside of the Winit /// event loop - and that's observable (for example to crates like `rfd`) - /// because the `NSApp` is global state. + /// because the `NSApplication` is global state. /// /// If you render outside of Winit you are likely to see window resizing artifacts /// since MacOS expects applications to render synchronously during any `drawRect` diff --git a/src/platform_impl/macos/app.rs b/src/platform_impl/macos/app.rs index c8078ca7da..2e2b3fccdf 100644 --- a/src/platform_impl/macos/app.rs +++ b/src/platform_impl/macos/app.rs @@ -1,7 +1,7 @@ #![allow(clippy::unnecessary_cast)] use icrate::AppKit::{ - NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown, + NSApplication, NSEvent, NSEventModifierFlagCommand, NSEventTypeKeyUp, NSEventTypeLeftMouseDown, NSEventTypeLeftMouseDragged, NSEventTypeLeftMouseUp, NSEventTypeMouseMoved, NSEventTypeOtherMouseDown, NSEventTypeOtherMouseDragged, NSEventTypeOtherMouseUp, NSEventTypeRightMouseDown, NSEventTypeRightMouseDragged, NSEventTypeRightMouseUp, NSResponder, @@ -9,7 +9,6 @@ use icrate::AppKit::{ use icrate::Foundation::NSObject; use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass}; -use super::appkit::NSApplication; use super::event::flags_contains; use super::{app_state::AppState, DEVICE_ID}; use crate::event::{DeviceEvent, ElementState, Event}; diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 3bee596b02..5439c0e9cc 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -1,11 +1,10 @@ -use icrate::AppKit::NSApplicationDelegate; +use icrate::AppKit::{NSApplicationActivationPolicy, NSApplicationDelegate}; use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol}; use objc2::rc::Id; use objc2::runtime::AnyObject; use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass}; use super::app_state::AppState; -use super::appkit::NSApplicationActivationPolicy; #[derive(Debug)] pub(super) struct State { diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 9b8aae05e2..3630b06b8c 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -12,11 +12,11 @@ use std::{ }; use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp}; +use icrate::AppKit::{NSApplication, NSApplicationActivationPolicy}; use icrate::Foundation::{is_main_thread, MainThreadMarker, NSSize}; use objc2::rc::{autoreleasepool, Id}; use once_cell::sync::Lazy; -use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy}; use super::{ event::dummy_event, event_loop::PanicInfo, menu, observer::EventLoopWaker, util::Never, window::WinitWindow, @@ -58,14 +58,14 @@ impl EventLoopHandler { where F: FnOnce(&mut EventLoopHandler, RefMut<'_, dyn FnMut(Event, &RootWindowTarget)>), { - // The `NSApp` and our `HANDLER` are global state and so it's possible that - // we could get a delegate callback after the application has exit an + // `NSApplication` and our `HANDLER` are global state and so it's possible + // that we could get a delegate callback after the application has exit an // `EventLoop`. If the loop has been exit then our weak `self.callback` // will fail to upgrade. // // We don't want to panic or output any verbose logging if we fail to // upgrade the weak reference since it might be valid that the application - // re-starts the `NSApp` after exiting a Winit `EventLoop` + // re-starts the `NSApplication` after exiting a Winit `EventLoop` if let Some(callback) = self.callback.upgrade() { let callback = callback.borrow_mut(); (f)(self, callback); @@ -145,9 +145,9 @@ impl Handler { /// `true` after `ApplicationDelegate::applicationDidFinishLaunching` called /// - /// NB: This is global / `NSApp` state and since the app will only be launched - /// once but an `EventLoop` may be run more than once then only the first - /// `EventLoop` will observe the `NSApp` before it is launched. + /// NB: This is global / `NSApplication` state and since the app will only + /// be launched once but an `EventLoop` may be run more than once then only + /// the first `EventLoop` will observe the application before it is launched. fn is_launched(&self) -> bool { self.launched.load(Ordering::Acquire) } @@ -159,8 +159,8 @@ impl Handler { /// `true` if an `EventLoop` is currently running /// - /// NB: This is global / `NSApp` state and may persist beyond the lifetime of - /// a running `EventLoop`. + /// NB: This is global / `NSApplication` state and may persist beyond the + /// lifetime of a running `EventLoop`. /// /// # Caveat /// This is only intended to be called from the main thread @@ -168,7 +168,7 @@ impl Handler { self.running.load(Ordering::Relaxed) } - /// Set when an `EventLoop` starts running, after the `NSApp` is launched + /// Set when an `EventLoop` starts running, after the `NSApplication` is launched /// /// # Caveat /// This is only intended to be called from the main thread @@ -181,8 +181,8 @@ impl Handler { /// Since an `EventLoop` may be run more than once we need make sure to reset the /// `control_flow` state back to `Poll` each time the loop exits. /// - /// Note: that if the `NSApp` has been launched then that state is preserved, and we won't - /// need to re-launch the app if subsequent EventLoops are run. + /// Note: that if the `NSApplication` has been launched then that state is preserved, + /// and we won't need to re-launch the app if subsequent EventLoops are run. /// /// # Caveat /// This is only intended to be called from the main thread @@ -393,7 +393,7 @@ impl AppState { } // If `pump_events` is called to progress the event loop then we bootstrap the event - // loop via `[NSApp run]` but will use `CFRunLoopRunInMode` for subsequent calls to + // loop via `-[NSAppplication run]` but will use `CFRunLoopRunInMode` for subsequent calls to // `pump_events` pub fn request_stop_on_launch() { HANDLER.request_stop_app_on_launch(); @@ -461,13 +461,14 @@ impl AppState { activate_ignoring_other_apps: bool, ) { let mtm = MainThreadMarker::new().unwrap(); - let app = NSApp(); + let app = NSApplication::sharedApplication(mtm); // We need to delay setting the activation policy and activating the app // until `applicationDidFinishLaunching` has been called. Otherwise the // menu bar is initially unresponsive on macOS 10.15. app.setActivationPolicy(activation_policy); window_activation_hack(&app); + #[allow(deprecated)] app.activateIgnoringOtherApps(activate_ignoring_other_apps); HANDLER.set_launched(); @@ -475,21 +476,22 @@ 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(mtm); + menu::initialize(&app); } Self::start_running(); - // If the `NSApp` is being launched via `EventLoop::pump_events()` then we'll + // If the application is being launched via `EventLoop::pump_events()` then we'll // want to stop the app once it is launched (and return to the external loop) // // In this case we still want to consider Winit's `EventLoop` to be "running", // so we call `start_running()` above. if HANDLER.should_stop_app_on_launch() { // Note: the original idea had been to only stop the underlying `RunLoop` - // for the app but that didn't work as expected (`[NSApp run]` effectively - // ignored the attempt to stop the RunLoop and re-started it.). So we - // return from `pump_events` by stopping the `NSApp` + // for the app but that didn't work as expected (`-[NSApplication run]` + // effectively ignored the attempt to stop the RunLoop and re-started it). + // + // So we return from `pump_events` by stopping the application. Self::stop(); } } @@ -591,7 +593,8 @@ impl AppState { } pub fn stop() { - let app = NSApp(); + let mtm = MainThreadMarker::new().unwrap(); + let app = NSApplication::sharedApplication(mtm); autoreleasepool(|_| { app.stop(None); // To stop event loop immediately, we need to post some event here. diff --git a/src/platform_impl/macos/appkit/application.rs b/src/platform_impl/macos/appkit/application.rs deleted file mode 100644 index ec6d2af0c3..0000000000 --- a/src/platform_impl/macos/appkit/application.rs +++ /dev/null @@ -1,142 +0,0 @@ -use icrate::AppKit::{NSAppearance, NSEvent, NSMenu, NSResponder, NSWindow}; -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}; - -extern_class!( - #[derive(Debug, PartialEq, Eq, Hash)] - pub(crate) struct NSApplication; - - unsafe impl ClassType for NSApplication { - #[inherits(NSObject)] - type Super = NSResponder; - type Mutability = mutability::MainThreadOnly; - } -); - -pub(crate) fn NSApp() -> Id { - // TODO: Only allow access from main thread - NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() }) -} - -extern_methods!( - unsafe impl NSApplication { - /// This can only be called on the main thread since it may initialize - /// the application and since it's parameters may be changed by the main - /// thread at any time (hence it is only safe to access on the main thread). - pub fn shared(_mtm: MainThreadMarker) -> Id { - let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] }; - // SAFETY: `sharedApplication` always initializes the app if it isn't already - unsafe { app.unwrap_unchecked() } - } - - #[method_id(currentEvent)] - pub fn currentEvent(&self) -> Option>; - - #[method(postEvent:atStart:)] - pub fn postEvent_atStart(&self, event: &NSEvent, front_of_queue: bool); - - #[method(presentationOptions)] - pub fn presentationOptions(&self) -> NSApplicationPresentationOptions; - - #[method_id(windows)] - pub fn windows(&self) -> Id>; - - #[method_id(keyWindow)] - pub fn keyWindow(&self) -> Option>; - - // TODO: NSApplicationDelegate - #[method(setDelegate:)] - pub fn setDelegate(&self, delegate: &AnyObject); - - #[method(setPresentationOptions:)] - pub fn setPresentationOptions(&self, options: NSApplicationPresentationOptions); - - #[method(hide:)] - pub fn hide(&self, sender: Option<&AnyObject>); - - #[method(orderFrontCharacterPalette:)] - #[allow(dead_code)] - pub fn orderFrontCharacterPalette(&self, sender: Option<&AnyObject>); - - #[method(hideOtherApplications:)] - pub fn hideOtherApplications(&self, sender: Option<&AnyObject>); - - #[method(stop:)] - pub fn stop(&self, sender: Option<&AnyObject>); - - #[method(activateIgnoringOtherApps:)] - pub fn activateIgnoringOtherApps(&self, ignore: bool); - - #[method(requestUserAttention:)] - pub fn requestUserAttention(&self, type_: NSRequestUserAttentionType) -> NSInteger; - - #[method(setActivationPolicy:)] - pub fn setActivationPolicy(&self, policy: NSApplicationActivationPolicy) -> bool; - - #[method(setMainMenu:)] - pub fn setMainMenu(&self, menu: &NSMenu); - - #[method(setServicesMenu:)] - pub fn setServicesMenu(&self, menu: &NSMenu); - - #[method_id(effectiveAppearance)] - pub fn effectiveAppearance(&self) -> Id; - - #[method(setAppearance:)] - pub fn setAppearance(&self, appearance: Option<&NSAppearance>); - - #[method(run)] - pub unsafe fn run(&self); - } -); - -#[allow(dead_code)] -#[repr(isize)] // NSInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSApplicationActivationPolicy { - NSApplicationActivationPolicyRegular = 0, - NSApplicationActivationPolicyAccessory = 1, - NSApplicationActivationPolicyProhibited = 2, - NSApplicationActivationPolicyERROR = -1, -} - -unsafe impl Encode for NSApplicationActivationPolicy { - const ENCODING: Encoding = NSInteger::ENCODING; -} - -bitflags! { - #[derive(Debug, Clone, Copy)] - pub struct NSApplicationPresentationOptions: NSUInteger { - const NSApplicationPresentationDefault = 0; - const NSApplicationPresentationAutoHideDock = 1 << 0; - const NSApplicationPresentationHideDock = 1 << 1; - const NSApplicationPresentationAutoHideMenuBar = 1 << 2; - const NSApplicationPresentationHideMenuBar = 1 << 3; - const NSApplicationPresentationDisableAppleMenu = 1 << 4; - const NSApplicationPresentationDisableProcessSwitching = 1 << 5; - const NSApplicationPresentationDisableForceQuit = 1 << 6; - const NSApplicationPresentationDisableSessionTermination = 1 << 7; - const NSApplicationPresentationDisableHideApplication = 1 << 8; - const NSApplicationPresentationDisableMenuBarTransparency = 1 << 9; - const NSApplicationPresentationFullScreen = 1 << 10; - const NSApplicationPresentationAutoHideToolbar = 1 << 11; - } -} - -unsafe impl Encode for NSApplicationPresentationOptions { - const ENCODING: Encoding = NSUInteger::ENCODING; -} - -#[repr(usize)] // NSUInteger -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NSRequestUserAttentionType { - NSCriticalRequest = 0, - NSInformationalRequest = 10, -} - -unsafe impl Encode for NSRequestUserAttentionType { - const ENCODING: Encoding = NSUInteger::ENCODING; -} diff --git a/src/platform_impl/macos/appkit/mod.rs b/src/platform_impl/macos/appkit/mod.rs deleted file mode 100644 index 669e9ea078..0000000000 --- a/src/platform_impl/macos/appkit/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Safe bindings for the AppKit framework. -//! -//! These are split out from the rest of `winit` to make safety easier to review. -//! In the future, these should probably live in another crate like `cacao`. -//! -//! TODO: Main thread safety. -// Objective-C methods have different conventions, and it's much easier to -// understand if we just use the same names -#![allow(non_snake_case)] -#![allow(clippy::too_many_arguments)] -#![allow(clippy::enum_variant_names)] -#![allow(non_upper_case_globals)] - -mod application; - -pub(crate) use self::application::{ - NSApp, NSApplication, NSApplicationActivationPolicy, NSApplicationPresentationOptions, - NSRequestUserAttentionType, -}; - -#[link(name = "AppKit", kind = "framework")] -extern "C" {} diff --git a/src/platform_impl/macos/event_loop.rs b/src/platform_impl/macos/event_loop.rs index 43f86cf254..7bce3aba05 100644 --- a/src/platform_impl/macos/event_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -17,16 +17,18 @@ use core_foundation::runloop::{ kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext, CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp, }; -use icrate::AppKit::NSWindow; -use icrate::Foundation::MainThreadMarker; -use objc2::rc::{autoreleasepool, Id}; -use objc2::runtime::NSObjectProtocol; +use icrate::AppKit::{ + NSApplication, NSApplicationActivationPolicyAccessory, NSApplicationActivationPolicyProhibited, + NSApplicationActivationPolicyRegular, NSWindow, +}; +use icrate::Foundation::{MainThreadMarker, NSObjectProtocol}; use objc2::{msg_send_id, ClassType}; - -use super::{ - appkit::{NSApp, NSApplication, NSApplicationActivationPolicy}, - event::dummy_event, +use objc2::{ + rc::{autoreleasepool, Id}, + runtime::ProtocolObject, }; + +use super::event::dummy_event; use crate::{ error::EventLoopError, event::Event, @@ -127,11 +129,11 @@ impl EventLoopWindowTarget { impl EventLoopWindowTarget { pub(crate) fn hide_application(&self) { - NSApplication::shared(self.mtm).hide(None) + NSApplication::sharedApplication(self.mtm).hide(None) } pub(crate) fn hide_other_applications(&self) { - NSApplication::shared(self.mtm).hideOtherApplications(None) + NSApplication::sharedApplication(self.mtm).hideOtherApplications(None) } pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) { @@ -200,7 +202,6 @@ impl EventLoop { panic!("`winit` requires control over the principal class. You must create the event loop before other parts of your application initialize NSApplication"); } - use NSApplicationActivationPolicy::*; let activation_policy = match attributes.activation_policy { ActivationPolicy::Regular => NSApplicationActivationPolicyRegular, ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory, @@ -214,7 +215,7 @@ impl EventLoop { ); autoreleasepool(|_| { - app.setDelegate(&delegate); + app.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); }); let panic_info: Rc = Default::default(); @@ -312,7 +313,7 @@ impl EventLoop { // While the app is running it's possible that we catch a panic // to avoid unwinding across an objective-c ffi boundary, which - // will lead to us stopping the `NSApp` and saving the + // will lead to us stopping the `NSApplication` and saving the // `PanicInfo` so that we can resume the unwind at a controlled, // safe point in time. if let Some(panic) = self.panic_info.take() { @@ -360,8 +361,6 @@ impl EventLoop { self._callback = Some(Rc::clone(&callback)); autoreleasepool(|_| { - let app = NSApp(); - // A bit of juggling with the callback references to make sure // that `self.callback` is the only owner of the callback. let weak_cb: Weak<_> = Rc::downgrade(&callback); @@ -382,25 +381,24 @@ impl EventLoop { // catch panics to make sure we can't unwind without clearing the set callback // (which would leave the global `AppState` in an undefined, unsafe state) let catch_result = catch_unwind(AssertUnwindSafe(|| { - // As a special case, if the `NSApp` hasn't been launched yet then we at least run + // As a special case, if the application hasn't been launched yet then we at least run // the loop until it has fully launched. if !AppState::is_launched() { debug_assert!(!AppState::is_running()); AppState::request_stop_on_launch(); unsafe { - app.run(); + self.app.run(); } - // Note: we dispatch `NewEvents(Init)` + `Resumed` events after the `NSApp` has launched + // Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application has launched } else if !AppState::is_running() { - // Even though the NSApp may have been launched, it's possible we aren't running + // Even though the application may have been launched, it's possible we aren't running // if the `EventLoop` was run before and has since exited. This indicates that // we just starting to re-run the same `EventLoop` again. AppState::start_running(); // Set is_running = true + dispatch `NewEvents(Init)` + `Resumed` } else { - // Only run the NSApp for as long as the given `Duration` allows so we - // don't block the external loop. + // Only run for as long as the given `Duration` allows so we don't block the external loop. match timeout { Some(Duration::ZERO) => { AppState::set_wait_timeout(None); @@ -420,13 +418,13 @@ impl EventLoop { } AppState::set_stop_app_on_redraw_requested(true); unsafe { - app.run(); + self.app.run(); } } // While the app is running it's possible that we catch a panic // to avoid unwinding across an objective-c ffi boundary, which - // will lead to us stopping the `NSApp` and saving the + // will lead to us stopping the application and saving the // `PanicInfo` so that we can resume the unwind at a controlled, // safe point in time. if let Some(panic) = self.panic_info.take() { @@ -464,6 +462,7 @@ impl EventLoop { /// happens, stops the `sharedApplication` #[inline] pub fn stop_app_on_panic R + UnwindSafe, R>( + mtm: MainThreadMarker, panic_info: Weak, f: F, ) -> Option { @@ -478,7 +477,7 @@ pub fn stop_app_on_panic R + UnwindSafe, R>( let panic_info = panic_info.upgrade().unwrap(); panic_info.set_panic(e); } - let app = NSApp(); + let app = NSApplication::sharedApplication(mtm); 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 diff --git a/src/platform_impl/macos/menu.rs b/src/platform_impl/macos/menu.rs index 04c5442fe8..2e65abc549 100644 --- a/src/platform_impl/macos/menu.rs +++ b/src/platform_impl/macos/menu.rs @@ -1,19 +1,19 @@ use icrate::AppKit::{ - NSEventModifierFlagCommand, NSEventModifierFlagOption, NSEventModifierFlags, NSMenu, NSMenuItem, + NSApplication, 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; - struct KeyEquivalent<'a> { key: &'a NSString, masks: Option, } -pub fn initialize(mtm: MainThreadMarker) { +pub fn initialize(app: &NSApplication) { + let mtm = MainThreadMarker::from(app); let menubar = NSMenu::new(mtm); let app_menu_item = NSMenuItem::new(mtm); menubar.addItem(&app_menu_item); @@ -96,9 +96,8 @@ pub fn initialize(mtm: MainThreadMarker) { app_menu.addItem(&quit_item); app_menu_item.setSubmenu(Some(&app_menu)); - let app = NSApp(); - app.setServicesMenu(&services_menu); - app.setMainMenu(&menubar); + unsafe { app.setServicesMenu(Some(&services_menu)) }; + app.setMainMenu(Some(&menubar)); } fn menu_item( diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index e5ac54b115..04aae45c69 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -4,7 +4,6 @@ mod util; mod app; mod app_delegate; mod app_state; -mod appkit; mod cursor; mod event; mod event_loop; diff --git a/src/platform_impl/macos/observer.rs b/src/platform_impl/macos/observer.rs index 70dff16dde..2b15ac5fd8 100644 --- a/src/platform_impl/macos/observer.rs +++ b/src/platform_impl/macos/observer.rs @@ -21,6 +21,7 @@ use core_foundation::runloop::{ CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, }; +use icrate::Foundation::MainThreadMarker; unsafe fn control_flow_handler(panic_info: *mut c_void, f: F) where @@ -36,7 +37,8 @@ where // However we want to keep that weak reference around after the function. std::mem::forget(info_from_raw); - stop_app_on_panic(Weak::clone(&panic_info), move || { + let mtm = MainThreadMarker::new().unwrap(); + stop_app_on_panic(mtm, Weak::clone(&panic_info), move || { let _ = &panic_info; f(panic_info.0) }); diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index ac2dce9e01..36baf1607e 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -4,9 +4,9 @@ use std::collections::{HashMap, VecDeque}; use std::ptr; use icrate::AppKit::{ - NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled, NSEventPhaseChanged, - NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient, NSTrackingRectTag, - NSView, + NSApplication, NSCursor, NSEvent, NSEventPhaseBegan, NSEventPhaseCancelled, + NSEventPhaseChanged, NSEventPhaseEnded, NSEventPhaseMayBegin, NSResponder, NSTextInputClient, + NSTrackingRectTag, NSView, }; use icrate::Foundation::{ MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, @@ -20,11 +20,8 @@ use objc2::{ }; use super::cursor::{default_cursor, invisible_cursor}; +use super::event::{code_to_key, code_to_location}; use super::event::{lalt_pressed, ralt_pressed}; -use super::{ - appkit::NSApp, - event::{code_to_key, code_to_location}, -}; use crate::{ dpi::{LogicalPosition, LogicalSize}, event::{ @@ -537,9 +534,10 @@ declare_class!( // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6 #[method(cancelOperation:)] fn cancel_operation(&self, _sender: Option<&AnyObject>) { + let mtm = MainThreadMarker::from(self); trace_scope!("cancelOperation:"); - let event = NSApp() + let event = NSApplication::sharedApplication(mtm) .currentEvent() .expect("could not find current event"); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 36884198b4..790a0ba196 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -30,13 +30,17 @@ use crate::{ }; use core_graphics::display::{CGDisplay, CGPoint}; use icrate::AppKit::{ - NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSBackingStoreBuffered, - NSColor, NSFilenamesPboardType, NSResponder, NSScreen, NSView, NSWindow, NSWindowAbove, - NSWindowCloseButton, NSWindowFullScreenButton, NSWindowLevel, NSWindowMiniaturizeButton, - NSWindowSharingNone, NSWindowSharingReadOnly, NSWindowStyleMask, NSWindowStyleMaskBorderless, - NSWindowStyleMaskClosable, NSWindowStyleMaskFullSizeContentView, - NSWindowStyleMaskMiniaturizable, NSWindowStyleMaskResizable, NSWindowStyleMaskTitled, - NSWindowTabbingModePreferred, NSWindowTitleHidden, NSWindowZoomButton, + NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication, + NSApplicationPresentationAutoHideDock, NSApplicationPresentationAutoHideMenuBar, + NSApplicationPresentationFullScreen, NSApplicationPresentationHideDock, + NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSBackingStoreBuffered, + NSColor, NSCriticalRequest, NSFilenamesPboardType, NSInformationalRequest, NSResponder, + NSScreen, NSView, NSWindow, NSWindowAbove, NSWindowCloseButton, NSWindowFullScreenButton, + NSWindowLevel, NSWindowMiniaturizeButton, NSWindowSharingNone, NSWindowSharingReadOnly, + NSWindowStyleMask, NSWindowStyleMaskBorderless, NSWindowStyleMaskClosable, + NSWindowStyleMaskFullSizeContentView, NSWindowStyleMaskMiniaturizable, + NSWindowStyleMaskResizable, NSWindowStyleMaskTitled, NSWindowTabbingModePreferred, + NSWindowTitleHidden, NSWindowZoomButton, }; use icrate::Foundation::{ CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSObject, NSPoint, NSRect, @@ -45,7 +49,6 @@ use icrate::Foundation::{ use objc2::rc::{autoreleasepool, Id}; use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass}; -use super::appkit::{NSApp, NSApplicationPresentationOptions, NSRequestUserAttentionType}; use super::cursor::cursor_from_icon; use super::ffi::kCGFloatingWindowLevel; use super::ffi::{kCGNormalWindowLevel, CGSMainConnectionID, CGSSetWindowBackgroundBlurRadius}; @@ -503,13 +506,13 @@ impl WinitWindow { match attrs.preferred_theme { Some(theme) => { - set_ns_theme(Some(theme)); + set_ns_theme(Some(theme), mtm); let mut state = this.lock_shared_state("WinitWindow::new"); state.current_theme = Some(theme); } None => { let mut state = this.lock_shared_state("WinitWindow::new"); - state.current_theme = Some(get_ns_theme()); + state.current_theme = Some(get_ns_theme(mtm)); } } @@ -878,7 +881,10 @@ impl WinitWindow { #[inline] pub fn drag_window(&self) -> Result<(), ExternalError> { - let event = NSApp().currentEvent().unwrap(); + let mtm = MainThreadMarker::from(self); + let event = NSApplication::sharedApplication(mtm) + .currentEvent() + .unwrap(); self.performWindowDragWithEvent(&event); Ok(()) } @@ -1018,6 +1024,8 @@ impl WinitWindow { #[inline] pub(crate) fn set_fullscreen(&self, fullscreen: Option) { let mtm = MainThreadMarker::from(self); + let app = NSApplication::sharedApplication(mtm); + let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); if shared_state_lock.is_simple_fullscreen { return; @@ -1080,7 +1088,6 @@ impl WinitWindow { let mut fade_token = ffi::kCGDisplayFadeReservationInvalidToken; if matches!(old_fullscreen, Some(Fullscreen::Borderless(_))) { - let app = NSApp(); let mut shared_state_lock = self.lock_shared_state("set_fullscreen"); shared_state_lock.save_presentation_opts = Some(app.presentationOptions()); } @@ -1178,14 +1185,12 @@ impl WinitWindow { // of the menu bar, and this looks broken, so we must make sure // that the menu bar is disabled. This is done in the window // delegate in `window:willUseFullScreenPresentationOptions:`. - let app = NSApp(); self.lock_shared_state("set_fullscreen") .save_presentation_opts = Some(app.presentationOptions()); - let presentation_options = - NSApplicationPresentationOptions::NSApplicationPresentationFullScreen - | NSApplicationPresentationOptions::NSApplicationPresentationHideDock - | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; + let presentation_options = NSApplicationPresentationFullScreen + | NSApplicationPresentationHideDock + | NSApplicationPresentationHideMenuBar; app.setPresentationOptions(presentation_options); let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1; @@ -1195,12 +1200,12 @@ impl WinitWindow { let presentation_options = self .lock_shared_state("set_fullscreen") .save_presentation_opts - .unwrap_or_else(|| { - NSApplicationPresentationOptions::NSApplicationPresentationFullScreen - | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock - | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar - }); - NSApp().setPresentationOptions(presentation_options); + .unwrap_or( + NSApplicationPresentationFullScreen + | NSApplicationPresentationAutoHideDock + | NSApplicationPresentationAutoHideMenuBar, + ); + app.setPresentationOptions(presentation_options); unsafe { ffi::CGRestorePermanentDisplayConfiguration(); @@ -1300,23 +1305,26 @@ impl WinitWindow { #[inline] pub fn focus_window(&self) { + let mtm = MainThreadMarker::from(self); let is_minimized = self.isMiniaturized(); let is_visible = self.isVisible(); if !is_minimized && is_visible { - NSApp().activateIgnoringOtherApps(true); + #[allow(deprecated)] + NSApplication::sharedApplication(mtm).activateIgnoringOtherApps(true); self.makeKeyAndOrderFront(None); } } #[inline] pub fn request_user_attention(&self, request_type: Option) { + let mtm = MainThreadMarker::from(self); let ns_request_type = request_type.map(|ty| match ty { - UserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest, - UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest, + UserAttentionType::Critical => NSCriticalRequest, + UserAttentionType::Informational => NSInformationalRequest, }); if let Some(ty) = ns_request_type { - NSApp().requestUserAttention(ty); + NSApplication::sharedApplication(mtm).requestUserAttention(ty); } } @@ -1407,8 +1415,10 @@ impl WinitWindow { } pub fn set_theme(&self, theme: Option) { - set_ns_theme(theme); - self.lock_shared_state("set_theme").current_theme = theme.or_else(|| Some(get_ns_theme())); + let mtm = MainThreadMarker::from(self); + set_ns_theme(theme, mtm); + self.lock_shared_state("set_theme").current_theme = + theme.or_else(|| Some(get_ns_theme(mtm))); } #[inline] @@ -1439,9 +1449,10 @@ impl WindowExtMacOS for WinitWindow { #[inline] fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { + let mtm = MainThreadMarker::from(self); let mut shared_state_lock = self.lock_shared_state("set_simple_fullscreen"); - let app = NSApp(); + let app = NSApplication::sharedApplication(mtm); let is_native_fullscreen = shared_state_lock.fullscreen.is_some(); let is_simple_fullscreen = shared_state_lock.is_simple_fullscreen; @@ -1469,8 +1480,7 @@ impl WindowExtMacOS for WinitWindow { // Simulate pre-Lion fullscreen by hiding the dock and menu bar let presentation_options = - NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock - | NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar; + NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar; app.setPresentationOptions(presentation_options); // Hide the titlebar @@ -1580,8 +1590,8 @@ fn mask_contains(mask: NSWindowStyleMask, value: NSWindowStyleMask) -> bool { mask & value == value } -pub(super) fn get_ns_theme() -> Theme { - let app = NSApp(); +pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme { + let app = NSApplication::sharedApplication(mtm); let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] }; if !has_theme { return Theme::Light; @@ -1599,8 +1609,8 @@ pub(super) fn get_ns_theme() -> Theme { } } -fn set_ns_theme(theme: Option) { - let app = NSApp(); +fn set_ns_theme(theme: Option, mtm: MainThreadMarker) { + let app = NSApplication::sharedApplication(mtm); let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] }; if has_theme { let appearance = theme.map(|t| { diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index 5a6b238252..5f06f557bf 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -3,8 +3,9 @@ use std::cell::Cell; use std::ptr; use icrate::AppKit::{ - NSDraggingDestination, NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, - NSWindowOcclusionStateVisible, + NSApplicationPresentationFullScreen, NSApplicationPresentationHideDock, + NSApplicationPresentationHideMenuBar, NSApplicationPresentationOptions, NSDraggingDestination, + NSFilenamesPboardType, NSPasteboard, NSWindowDelegate, NSWindowOcclusionStateVisible, }; use icrate::Foundation::{MainThreadMarker, NSArray, NSObject, NSObjectProtocol, NSSize, NSString}; use objc2::rc::{autoreleasepool, Id}; @@ -13,7 +14,6 @@ use objc2::{ class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass, }; -use super::appkit::NSApplicationPresentationOptions; use super::{ app_state::AppState, util, @@ -202,9 +202,9 @@ declare_class!( .window .lock_shared_state("window_will_use_fullscreen_presentation_options"); if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen { - options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen - | NSApplicationPresentationOptions::NSApplicationPresentationHideDock - | NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar; + options = NSApplicationPresentationFullScreen + | NSApplicationPresentationHideDock + | NSApplicationPresentationHideMenuBar; } options @@ -391,7 +391,8 @@ declare_class!( #[method(effectiveAppearanceDidChangedOnMainThread:)] fn effective_appearance_did_changed_on_main_thread(&self, _: Option<&AnyObject>) { - let theme = get_ns_theme(); + let mtm = MainThreadMarker::from(self); + let theme = get_ns_theme(mtm); let mut shared_state = self .ivars() .window