Skip to main content

winit_appkit/
lib.rs

1//! # macOS / AppKit
2//!
3//! Winit has [the same macOS version requirements as `rustc`][rustc-macos-version], and is tested
4//! once in a while on as low as macOS 10.14.
5//!
6//! [rustc-macos-version]: https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version
7//!
8//! ## Custom `NSApplicationDelegate`
9//!
10//! Winit usually handles everything related to the lifecycle events of the application. Sometimes,
11//! though, you might want to do more niche stuff, such as [handle when the user re-activates the
12//! application][reopen]. Such functionality is not exposed directly in Winit, since it would
13//! increase the API surface by quite a lot.
14//!
15//! [reopen]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc
16//!
17//! Instead, Winit guarantees that it will not register an application delegate, so the solution is
18//! to register your own application delegate, as outlined in the following example (see
19//! `objc2-app-kit` for more detailed information).
20//! ```
21//! use objc2::rc::Retained;
22//! use objc2::runtime::ProtocolObject;
23//! use objc2::{DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
24//! use objc2_app_kit::{NSApplication, NSApplicationDelegate};
25//! use objc2_foundation::{NSArray, NSObject, NSObjectProtocol, NSURL};
26//! use winit::event_loop::EventLoop;
27//!
28//! define_class!(
29//!     #[unsafe(super(NSObject))]
30//!     #[thread_kind = MainThreadOnly]
31//!     #[name = "AppDelegate"]
32//!     struct AppDelegate;
33//!
34//!     unsafe impl NSObjectProtocol for AppDelegate {}
35//!
36//!     unsafe impl NSApplicationDelegate for AppDelegate {
37//!         #[unsafe(method(application:openURLs:))]
38//!         fn application_openURLs(&self, application: &NSApplication, urls: &NSArray<NSURL>) {
39//!             // Note: To specifically get `application:openURLs:` to work, you _might_
40//!             // have to bundle your application. This is not done in this example.
41//!             println!("open urls: {application:?}, {urls:?}");
42//!         }
43//!     }
44//! );
45//!
46//! impl AppDelegate {
47//!     fn new(mtm: MainThreadMarker) -> Retained<Self> {
48//!         unsafe { msg_send![super(Self::alloc(mtm).set_ivars(())), init] }
49//!     }
50//! }
51//!
52//! fn main() -> Result<(), Box<dyn std::error::Error>> {
53//!     let event_loop = EventLoop::new()?;
54//!
55//!     let mtm = MainThreadMarker::new().unwrap();
56//!     let delegate = AppDelegate::new(mtm);
57//!     // Important: Call `sharedApplication` after `EventLoop::new`,
58//!     // doing it before is not yet supported.
59//!     let app = NSApplication::sharedApplication(mtm);
60//!     app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
61//!
62//!     // event_loop.run_app(&mut my_app);
63//!     Ok(())
64//! }
65//! ```
66#![cfg(target_vendor = "apple")] // TODO: Remove once `objc2` allows compiling on all platforms
67#![warn(clippy::exhaustive_enums)]
68
69#[macro_use]
70mod util;
71
72mod app;
73mod app_state;
74mod cursor;
75mod dnd;
76mod event;
77mod event_loop;
78mod ffi;
79mod menu;
80mod monitor;
81mod observer;
82mod view;
83mod window;
84mod window_delegate;
85
86use std::os::raw::c_void;
87
88#[cfg(feature = "serde")]
89use serde::{Deserialize, Serialize};
90#[doc(inline)]
91pub use winit_core::application::macos::ApplicationHandlerExtMacOS;
92use winit_core::event_loop::ActiveEventLoop;
93use winit_core::monitor::MonitorHandle;
94use winit_core::window::{PlatformWindowAttributes, Window};
95
96pub use self::dnd::{Pasteboard, PasteboardType, PasteboardValue};
97pub use self::event::{physicalkey_to_scancode, scancode_to_physicalkey};
98use self::event_loop::ActiveEventLoop as AppKitActiveEventLoop;
99pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes};
100use self::monitor::MonitorHandle as AppKitMonitorHandle;
101use self::window::Window as AppKitWindow;
102
103/// Additional methods on [`Window`] that are specific to MacOS.
104pub trait WindowExtMacOS {
105    /// Returns whether or not the window is in simple fullscreen mode.
106    fn simple_fullscreen(&self) -> bool;
107
108    /// Toggles a fullscreen mode that doesn't require a new macOS space.
109    /// Returns a boolean indicating whether the transition was successful (this
110    /// won't work if the window was already in the native fullscreen).
111    ///
112    /// This is how fullscreen used to work on macOS in versions before Lion.
113    /// And allows the user to have a fullscreen window without using another
114    /// space or taking control over the entire monitor.
115    ///
116    /// Make sure you only draw your important content inside the safe area so that it does not
117    /// overlap with the notch on newer devices, see [`Window::safe_area`] for details.
118    fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
119
120    /// Returns whether or not the window has shadow.
121    fn has_shadow(&self) -> bool;
122
123    /// Sets whether or not the window has shadow.
124    fn set_has_shadow(&self, has_shadow: bool);
125
126    /// Group windows together by using the same tabbing identifier.
127    ///
128    /// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
129    fn set_tabbing_identifier(&self, identifier: &str);
130
131    /// Returns the window's tabbing identifier.
132    fn tabbing_identifier(&self) -> String;
133
134    /// Select next tab.
135    fn select_next_tab(&self);
136
137    /// Select previous tab.
138    fn select_previous_tab(&self);
139
140    /// Select the tab with the given index.
141    ///
142    /// Will no-op when the index is out of bounds.
143    fn select_tab_at_index(&self, index: usize);
144
145    /// Get the number of tabs in the window tab group.
146    fn num_tabs(&self) -> usize;
147
148    /// Get the window's edit state.
149    ///
150    /// # Examples
151    ///
152    /// ```ignore
153    /// WindowEvent::CloseRequested => {
154    ///     if window.is_document_edited() {
155    ///         // Show the user a save pop-up or similar
156    ///     } else {
157    ///         // Close the window
158    ///         drop(window);
159    ///     }
160    /// }
161    /// ```
162    fn is_document_edited(&self) -> bool;
163
164    /// Put the window in a state which indicates a file save is required.
165    fn set_document_edited(&self, edited: bool);
166
167    /// Set option as alt behavior as described in [`OptionAsAlt`].
168    ///
169    /// This will ignore diacritical marks and accent characters from
170    /// being processed as received characters. Instead, the input
171    /// device's raw character will be placed in event queues with the
172    /// Alt modifier set.
173    fn set_option_as_alt(&self, option_as_alt: OptionAsAlt);
174
175    /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
176    fn option_as_alt(&self) -> OptionAsAlt;
177
178    /// Disable the Menu Bar and Dock in Simple or Borderless Fullscreen mode. Useful for games.
179    /// The effect is applied when [`WindowExtMacOS::set_simple_fullscreen`] or
180    /// [`Window::set_fullscreen`] is called.
181    fn set_borderless_game(&self, borderless_game: bool);
182
183    /// Getter for the [`WindowExtMacOS::set_borderless_game`].
184    fn is_borderless_game(&self) -> bool;
185
186    /// Makes the titlebar bigger, effectively adding more space around the
187    /// window controls if the titlebar is invisible.
188    fn set_unified_titlebar(&self, unified_titlebar: bool);
189
190    /// Getter for the [`WindowExtMacOS::set_unified_titlebar`].
191    fn unified_titlebar(&self) -> bool;
192
193    /// Sets whether the window can be shown on the same Space as a fullscreen window.
194    ///
195    /// This corresponds to [`NSWindowCollectionBehaviorFullScreenAuxiliary`], and is useful
196    /// for floating palettes, inspectors and other secondary windows accompanying a fullscreen
197    /// window. Without it, ordering a new window on screen while another window of the
198    /// application is fullscreen on the active Space makes macOS switch Spaces or attempt
199    /// Split View tiling.
200    ///
201    /// A window marked as fullscreen auxiliary cannot itself enter (native) fullscreen;
202    /// [`Window::set_fullscreen`] will warn and do nothing. Call
203    /// `set_fullscreen_auxiliary(false)` first if you want to make the window fullscreen.
204    ///
205    /// [`NSWindowCollectionBehaviorFullScreenAuxiliary`]: https://developer.apple.com/documentation/appkit/nswindow/collectionbehavior-swift.struct/fullscreenauxiliary?language=objc
206    /// [`Window::set_fullscreen`]: winit_core::window::Window::set_fullscreen
207    fn set_fullscreen_auxiliary(&self, fullscreen_auxiliary: bool);
208
209    /// Getter for the [`WindowExtMacOS::set_fullscreen_auxiliary`].
210    fn fullscreen_auxiliary(&self) -> bool;
211
212    /// Sets the material drawn behind the window when it is blurred.
213    ///
214    /// Takes effect the next time the window is blurred, and immediately if it already is. See
215    /// [`Window::set_blur`].
216    ///
217    /// [`Window::set_blur`]: winit_core::window::Window::set_blur
218    fn set_blur_material(&self, blur_material: BlurMaterial);
219
220    /// Getter for the [`WindowExtMacOS::set_blur_material`].
221    fn blur_material(&self) -> BlurMaterial;
222}
223
224impl WindowExtMacOS for dyn Window + '_ {
225    #[inline]
226    fn simple_fullscreen(&self) -> bool {
227        let window = self.cast_ref::<AppKitWindow>().unwrap();
228        window.maybe_wait_on_main(|w| w.simple_fullscreen())
229    }
230
231    #[inline]
232    fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
233        let window = self.cast_ref::<AppKitWindow>().unwrap();
234        window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen))
235    }
236
237    #[inline]
238    fn has_shadow(&self) -> bool {
239        let window = self.cast_ref::<AppKitWindow>().unwrap();
240        window.maybe_wait_on_main(|w| w.has_shadow())
241    }
242
243    #[inline]
244    fn set_has_shadow(&self, has_shadow: bool) {
245        let window = self.cast_ref::<AppKitWindow>().unwrap();
246        window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow));
247    }
248
249    #[inline]
250    fn set_tabbing_identifier(&self, identifier: &str) {
251        let window = self.cast_ref::<AppKitWindow>().unwrap();
252        window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier))
253    }
254
255    #[inline]
256    fn tabbing_identifier(&self) -> String {
257        let window = self.cast_ref::<AppKitWindow>().unwrap();
258        window.maybe_wait_on_main(|w| w.tabbing_identifier())
259    }
260
261    #[inline]
262    fn select_next_tab(&self) {
263        let window = self.cast_ref::<AppKitWindow>().unwrap();
264        window.maybe_wait_on_main(|w| w.select_next_tab());
265    }
266
267    #[inline]
268    fn select_previous_tab(&self) {
269        let window = self.cast_ref::<AppKitWindow>().unwrap();
270        window.maybe_wait_on_main(|w| w.select_previous_tab());
271    }
272
273    #[inline]
274    fn select_tab_at_index(&self, index: usize) {
275        let window = self.cast_ref::<AppKitWindow>().unwrap();
276        window.maybe_wait_on_main(move |w| w.select_tab_at_index(index));
277    }
278
279    #[inline]
280    fn num_tabs(&self) -> usize {
281        let window = self.cast_ref::<AppKitWindow>().unwrap();
282        window.maybe_wait_on_main(|w| w.num_tabs())
283    }
284
285    #[inline]
286    fn is_document_edited(&self) -> bool {
287        let window = self.cast_ref::<AppKitWindow>().unwrap();
288        window.maybe_wait_on_main(|w| w.is_document_edited())
289    }
290
291    #[inline]
292    fn set_document_edited(&self, edited: bool) {
293        let window = self.cast_ref::<AppKitWindow>().unwrap();
294        window.maybe_wait_on_main(move |w| w.set_document_edited(edited));
295    }
296
297    #[inline]
298    fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
299        let window = self.cast_ref::<AppKitWindow>().unwrap();
300        window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt));
301    }
302
303    #[inline]
304    fn option_as_alt(&self) -> OptionAsAlt {
305        let window = self.cast_ref::<AppKitWindow>().unwrap();
306        window.maybe_wait_on_main(|w| w.option_as_alt())
307    }
308
309    #[inline]
310    fn set_borderless_game(&self, borderless_game: bool) {
311        let window = self.cast_ref::<AppKitWindow>().unwrap();
312        window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game))
313    }
314
315    #[inline]
316    fn is_borderless_game(&self) -> bool {
317        let window = self.cast_ref::<AppKitWindow>().unwrap();
318        window.maybe_wait_on_main(|w| w.is_borderless_game())
319    }
320
321    #[inline]
322    fn set_unified_titlebar(&self, unified_titlebar: bool) {
323        let window = self.cast_ref::<AppKitWindow>().unwrap();
324        window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar))
325    }
326
327    #[inline]
328    fn unified_titlebar(&self) -> bool {
329        let window = self.cast_ref::<AppKitWindow>().unwrap();
330        window.maybe_wait_on_main(|w| w.unified_titlebar())
331    }
332
333    #[inline]
334    fn set_fullscreen_auxiliary(&self, fullscreen_auxiliary: bool) {
335        let window = self.cast_ref::<AppKitWindow>().unwrap();
336        window.maybe_wait_on_main(move |w| w.set_fullscreen_auxiliary(fullscreen_auxiliary))
337    }
338
339    #[inline]
340    fn fullscreen_auxiliary(&self) -> bool {
341        let window = self.cast_ref::<AppKitWindow>().unwrap();
342        window.maybe_wait_on_main(|w| w.fullscreen_auxiliary())
343    }
344
345    #[inline]
346    fn set_blur_material(&self, blur_material: BlurMaterial) {
347        let window = self.cast_ref::<AppKitWindow>().unwrap();
348        window.maybe_wait_on_main(move |w| w.set_blur_material(blur_material))
349    }
350
351    #[inline]
352    fn blur_material(&self) -> BlurMaterial {
353        let window = self.cast_ref::<AppKitWindow>().unwrap();
354        window.maybe_wait_on_main(|w| w.blur_material())
355    }
356}
357
358/// Corresponds to `NSApplicationActivationPolicy`.
359#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
360#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
361#[non_exhaustive]
362pub enum ActivationPolicy {
363    /// Corresponds to `NSApplicationActivationPolicyRegular`.
364    #[default]
365    Regular,
366
367    /// Corresponds to `NSApplicationActivationPolicyAccessory`.
368    Accessory,
369
370    /// Corresponds to `NSApplicationActivationPolicyProhibited`.
371    Prohibited,
372}
373
374/// Window attributes that are specific to MacOS.
375///
376/// **Note:** Properties dealing with the titlebar will be overwritten by the
377/// [`WindowAttributes::with_decorations`] method:
378/// - `with_titlebar_transparent`
379/// - `with_title_hidden`
380/// - `with_titlebar_hidden`
381/// - `with_titlebar_buttons_hidden`
382/// - `with_fullsize_content_view`
383///
384/// [`WindowAttributes::with_decorations`]: crate::window::WindowAttributes::with_decorations
385#[derive(Clone, Debug, PartialEq)]
386pub struct WindowAttributesMacOS {
387    pub(crate) movable_by_window_background: bool,
388    pub(crate) titlebar_transparent: bool,
389    pub(crate) title_hidden: bool,
390    pub(crate) titlebar_hidden: bool,
391    pub(crate) titlebar_buttons_hidden: bool,
392    pub(crate) fullsize_content_view: bool,
393    pub(crate) disallow_hidpi: bool,
394    pub(crate) has_shadow: bool,
395    pub(crate) tabbing_identifier: Option<String>,
396    pub(crate) option_as_alt: OptionAsAlt,
397    pub(crate) borderless_game: bool,
398    pub(crate) unified_titlebar: bool,
399    pub(crate) panel: bool,
400    pub(crate) fullscreen_auxiliary: bool,
401    pub(crate) blur_material: BlurMaterial,
402}
403
404impl WindowAttributesMacOS {
405    /// Enables click-and-drag behavior for the entire window, not just the titlebar.
406    #[inline]
407    pub fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self {
408        self.movable_by_window_background = movable_by_window_background;
409        self
410    }
411
412    /// Sets the material drawn behind the window when it is blurred.
413    ///
414    /// Has no effect unless the window is blurred, see [`Window::set_blur`].
415    ///
416    /// [`Window::set_blur`]: winit_core::window::Window::set_blur
417    #[inline]
418    pub fn with_blur_material(mut self, blur_material: BlurMaterial) -> Self {
419        self.blur_material = blur_material;
420        self
421    }
422
423    /// Makes the titlebar transparent and allows the content to appear behind it.
424    #[inline]
425    pub fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self {
426        self.titlebar_transparent = titlebar_transparent;
427        self
428    }
429
430    /// Hides the window titlebar.
431    #[inline]
432    pub fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
433        self.titlebar_hidden = titlebar_hidden;
434        self
435    }
436
437    /// Hides the window titlebar buttons.
438    #[inline]
439    pub fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self {
440        self.titlebar_buttons_hidden = titlebar_buttons_hidden;
441        self
442    }
443
444    /// Hides the window title.
445    #[inline]
446    pub fn with_title_hidden(mut self, title_hidden: bool) -> Self {
447        self.title_hidden = title_hidden;
448        self
449    }
450
451    /// Makes the window content appear behind the titlebar.
452    #[inline]
453    pub fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self {
454        self.fullsize_content_view = fullsize_content_view;
455        self
456    }
457
458    #[inline]
459    pub fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self {
460        self.disallow_hidpi = disallow_hidpi;
461        self
462    }
463
464    #[inline]
465    pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
466        self.has_shadow = has_shadow;
467        self
468    }
469
470    /// Defines the window tabbing identifier.
471    ///
472    /// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
473    #[inline]
474    pub fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self {
475        self.tabbing_identifier.replace(tabbing_identifier.to_string());
476        self
477    }
478
479    /// Set how the <kbd>Option</kbd> keys are interpreted.
480    ///
481    /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
482    #[inline]
483    pub fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self {
484        self.option_as_alt = option_as_alt;
485        self
486    }
487
488    /// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set.
489    #[inline]
490    pub fn with_borderless_game(mut self, borderless_game: bool) -> Self {
491        self.borderless_game = borderless_game;
492        self
493    }
494
495    /// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set.
496    #[inline]
497    pub fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self {
498        self.unified_titlebar = unified_titlebar;
499        self
500    }
501
502    /// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of
503    /// [`NSWindow`].
504    ///
505    /// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc
506    /// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc
507    /// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc
508    #[inline]
509    pub fn with_panel(mut self, panel: bool) -> Self {
510        self.panel = panel;
511        self
512    }
513
514    /// See [`WindowExtMacOS::set_fullscreen_auxiliary`] for details on what this means if set.
515    ///
516    /// Contrary to the runtime setter, setting this attribute guarantees that the collection
517    /// behavior is already in place when the window is first ordered on screen, which is
518    /// required to avoid disturbing an active fullscreen Space.
519    #[inline]
520    pub fn with_fullscreen_auxiliary(mut self, fullscreen_auxiliary: bool) -> Self {
521        self.fullscreen_auxiliary = fullscreen_auxiliary;
522        self
523    }
524}
525
526impl Default for WindowAttributesMacOS {
527    #[inline]
528    fn default() -> Self {
529        Self {
530            movable_by_window_background: false,
531            titlebar_transparent: false,
532            title_hidden: false,
533            titlebar_hidden: false,
534            titlebar_buttons_hidden: false,
535            fullsize_content_view: false,
536            disallow_hidpi: false,
537            has_shadow: true,
538            tabbing_identifier: None,
539            option_as_alt: Default::default(),
540            borderless_game: false,
541            unified_titlebar: false,
542            panel: false,
543            fullscreen_auxiliary: false,
544            blur_material: Default::default(),
545        }
546    }
547}
548
549impl PlatformWindowAttributes for WindowAttributesMacOS {
550    fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
551        Box::from(self.clone())
552    }
553}
554
555pub trait EventLoopBuilderExtMacOS {
556    /// Sets the activation policy for the application. If used, this will override
557    /// any relevant settings provided in the package manifest.
558    /// For instance, `with_activation_policy(ActivationPolicy::Regular)` will prevent
559    /// the application from running as an "agent", even if LSUIElement is set to true.
560    ///
561    /// If unused, the Winit will honor the package manifest.
562    ///
563    /// # Example
564    ///
565    /// Set the activation policy to "accessory".
566    ///
567    /// ```
568    /// use winit::event_loop::EventLoop;
569    /// #[cfg(target_os = "macos")]
570    /// use winit::platform::macos::{ActivationPolicy, EventLoopBuilderExtMacOS};
571    ///
572    /// let mut builder = EventLoop::builder();
573    /// #[cfg(target_os = "macos")]
574    /// builder.with_activation_policy(ActivationPolicy::Accessory);
575    /// # if false { // We can't test this part
576    /// let event_loop = builder.build();
577    /// # }
578    /// ```
579    fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self;
580
581    /// Used to control whether a default menubar menu is created.
582    ///
583    /// Menu creation is enabled by default.
584    ///
585    /// # Example
586    ///
587    /// Disable creating a default menubar.
588    ///
589    /// ```
590    /// use winit::event_loop::EventLoop;
591    /// #[cfg(target_os = "macos")]
592    /// use winit::platform::macos::EventLoopBuilderExtMacOS;
593    ///
594    /// let mut builder = EventLoop::builder();
595    /// #[cfg(target_os = "macos")]
596    /// builder.with_default_menu(false);
597    /// # if false { // We can't test this part
598    /// let event_loop = builder.build();
599    /// # }
600    /// ```
601    fn with_default_menu(&mut self, enable: bool) -> &mut Self;
602
603    /// Used to prevent the application from automatically activating when launched if
604    /// another application is already active.
605    ///
606    /// The default behavior is to ignore other applications and activate when launched.
607    fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
608}
609
610/// Additional methods on [`MonitorHandle`] that are specific to MacOS.
611pub trait MonitorHandleExtMacOS {
612    /// Returns a pointer to the NSScreen representing this monitor.
613    fn ns_screen(&self) -> Option<*mut c_void>;
614}
615
616impl MonitorHandleExtMacOS for MonitorHandle {
617    fn ns_screen(&self) -> Option<*mut c_void> {
618        let monitor = self.cast_ref::<AppKitMonitorHandle>().unwrap();
619        // SAFETY: We only use the marker to get a pointer
620        let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() };
621        monitor.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
622    }
623}
624
625/// Additional methods on [`ActiveEventLoop`] that are specific to macOS.
626pub trait ActiveEventLoopExtMacOS {
627    /// Hide the entire application. In most applications this is typically triggered with
628    /// Command-H.
629    fn hide_application(&self);
630    /// Hide the other applications. In most applications this is typically triggered with
631    /// Command+Option-H.
632    fn hide_other_applications(&self);
633    /// Set whether the system can automatically organize windows into tabs.
634    ///
635    /// <https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing>
636    fn set_allows_automatic_window_tabbing(&self, enabled: bool);
637    /// Returns whether the system can automatically organize windows into tabs.
638    fn allows_automatic_window_tabbing(&self) -> bool;
639}
640
641impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ {
642    fn hide_application(&self) {
643        let event_loop =
644            self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
645        event_loop.hide_application()
646    }
647
648    fn hide_other_applications(&self) {
649        let event_loop =
650            self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
651        event_loop.hide_other_applications()
652    }
653
654    fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
655        let event_loop =
656            self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
657        event_loop.set_allows_automatic_window_tabbing(enabled);
658    }
659
660    fn allows_automatic_window_tabbing(&self) -> bool {
661        let event_loop =
662            self.cast_ref::<AppKitActiveEventLoop>().expect("non macOS event loop on macOS");
663        event_loop.allows_automatic_window_tabbing()
664    }
665}
666
667/// Option as alt behavior.
668///
669/// The default is `None`.
670#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
671#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
672#[allow(clippy::exhaustive_enums)]
673pub enum OptionAsAlt {
674    /// The left `Option` key is treated as `Alt`.
675    OnlyLeft,
676
677    /// The right `Option` key is treated as `Alt`.
678    OnlyRight,
679
680    /// Both `Option` keys are treated as `Alt`.
681    Both,
682
683    /// No special handling is applied for `Option` key.
684    #[default]
685    None,
686}
687
688/// The material drawn behind a blurred window, corresponding to `NSVisualEffectMaterial`.
689///
690/// Only the materials that AppKit renders behind the window are listed. The remaining
691/// `NSVisualEffectMaterial` values are meant for opaque backgrounds, and draw a placeholder
692/// rather than a blur when used this way.
693///
694/// The material determines the tint and translucency of the blur, so the result is not an
695/// untinted blur of a chosen radius; AppKit exposes no public API for that. Enable the
696/// `private-apple-apis` Cargo feature for the private one, which this setting then no longer
697/// affects.
698///
699/// See [`Window::set_blur`] for the blur itself.
700///
701/// [`Window::set_blur`]: winit_core::window::Window::set_blur
702#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
703#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
704#[non_exhaustive]
705pub enum BlurMaterial {
706    /// Corresponds to `NSVisualEffectMaterialFullScreenUI`.
707    ///
708    /// Tints the least of the available materials, making it the closest to a plain backdrop
709    /// blur. This is the default.
710    #[default]
711    FullScreenUI,
712
713    /// Corresponds to `NSVisualEffectMaterialHUDWindow`.
714    HudWindow,
715
716    /// Corresponds to `NSVisualEffectMaterialMenu`.
717    Menu,
718
719    /// Corresponds to `NSVisualEffectMaterialPopover`.
720    Popover,
721
722    /// Corresponds to `NSVisualEffectMaterialSidebar`.
723    Sidebar,
724
725    /// Corresponds to `NSVisualEffectMaterialSelection`.
726    Selection,
727
728    /// Corresponds to `NSVisualEffectMaterialTitlebar`.
729    Titlebar,
730
731    /// Corresponds to `NSVisualEffectMaterialHeaderView`.
732    HeaderView,
733
734    /// Corresponds to `NSVisualEffectMaterialToolTip`.
735    ToolTip,
736
737    /// Corresponds to `NSVisualEffectMaterialUnderWindowBackground`.
738    ///
739    /// Tints heavily towards the window's background colour, which can leave the blur barely
740    /// visible over light content.
741    UnderWindowBackground,
742}