window.rs

   1use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
   2use crate::{
   3    display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, Executor, GlobalPixels,
   4    InputEvent, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
   5    MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
   6    PlatformInputHandler, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance,
   7    WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
   8};
   9use block::ConcreteBlock;
  10use cocoa::{
  11    appkit::{
  12        CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
  13        NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
  14        NSWindowStyleMask, NSWindowTitleVisibility,
  15    },
  16    base::{id, nil},
  17    foundation::{
  18        NSAutoreleasePool, NSDictionary, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger,
  19    },
  20};
  21use core_graphics::display::CGRect;
  22use ctor::ctor;
  23use foreign_types::ForeignTypeRef;
  24use futures::channel::oneshot;
  25use objc::{
  26    class,
  27    declare::ClassDecl,
  28    msg_send,
  29    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
  30    sel, sel_impl,
  31};
  32use parking_lot::Mutex;
  33use std::{
  34    any::Any,
  35    cell::{Cell, RefCell},
  36    ffi::{c_void, CStr},
  37    mem,
  38    ops::Range,
  39    os::raw::c_char,
  40    ptr,
  41    rc::Rc,
  42    sync::{Arc, Weak},
  43    time::Duration,
  44};
  45
  46const WINDOW_STATE_IVAR: &str = "windowState";
  47
  48static mut WINDOW_CLASS: *const Class = ptr::null();
  49static mut PANEL_CLASS: *const Class = ptr::null();
  50static mut VIEW_CLASS: *const Class = ptr::null();
  51
  52#[allow(non_upper_case_globals)]
  53const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =
  54    unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) };
  55#[allow(non_upper_case_globals)]
  56const NSNormalWindowLevel: NSInteger = 0;
  57#[allow(non_upper_case_globals)]
  58const NSPopUpWindowLevel: NSInteger = 101;
  59#[allow(non_upper_case_globals)]
  60const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;
  61#[allow(non_upper_case_globals)]
  62const NSTrackingMouseMoved: NSUInteger = 0x02;
  63#[allow(non_upper_case_globals)]
  64const NSTrackingActiveAlways: NSUInteger = 0x80;
  65#[allow(non_upper_case_globals)]
  66const NSTrackingInVisibleRect: NSUInteger = 0x200;
  67#[allow(non_upper_case_globals)]
  68const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
  69#[allow(non_upper_case_globals)]
  70const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
  71
  72#[ctor]
  73unsafe fn build_classes() {
  74    WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
  75    PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));
  76    VIEW_CLASS = {
  77        let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
  78        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
  79
  80        decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
  81
  82        decl.add_method(
  83            sel!(performKeyEquivalent:),
  84            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
  85        );
  86        decl.add_method(
  87            sel!(keyDown:),
  88            handle_key_down as extern "C" fn(&Object, Sel, id),
  89        );
  90        decl.add_method(
  91            sel!(mouseDown:),
  92            handle_view_event as extern "C" fn(&Object, Sel, id),
  93        );
  94        decl.add_method(
  95            sel!(mouseUp:),
  96            handle_view_event as extern "C" fn(&Object, Sel, id),
  97        );
  98        decl.add_method(
  99            sel!(rightMouseDown:),
 100            handle_view_event as extern "C" fn(&Object, Sel, id),
 101        );
 102        decl.add_method(
 103            sel!(rightMouseUp:),
 104            handle_view_event as extern "C" fn(&Object, Sel, id),
 105        );
 106        decl.add_method(
 107            sel!(otherMouseDown:),
 108            handle_view_event as extern "C" fn(&Object, Sel, id),
 109        );
 110        decl.add_method(
 111            sel!(otherMouseUp:),
 112            handle_view_event as extern "C" fn(&Object, Sel, id),
 113        );
 114        decl.add_method(
 115            sel!(mouseMoved:),
 116            handle_view_event as extern "C" fn(&Object, Sel, id),
 117        );
 118        decl.add_method(
 119            sel!(mouseExited:),
 120            handle_view_event as extern "C" fn(&Object, Sel, id),
 121        );
 122        decl.add_method(
 123            sel!(mouseDragged:),
 124            handle_view_event as extern "C" fn(&Object, Sel, id),
 125        );
 126        decl.add_method(
 127            sel!(scrollWheel:),
 128            handle_view_event as extern "C" fn(&Object, Sel, id),
 129        );
 130        decl.add_method(
 131            sel!(flagsChanged:),
 132            handle_view_event as extern "C" fn(&Object, Sel, id),
 133        );
 134        decl.add_method(
 135            sel!(cancelOperation:),
 136            cancel_operation as extern "C" fn(&Object, Sel, id),
 137        );
 138
 139        decl.add_method(
 140            sel!(makeBackingLayer),
 141            make_backing_layer as extern "C" fn(&Object, Sel) -> id,
 142        );
 143
 144        decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
 145        decl.add_method(
 146            sel!(viewDidChangeBackingProperties),
 147            view_did_change_backing_properties as extern "C" fn(&Object, Sel),
 148        );
 149        decl.add_method(
 150            sel!(setFrameSize:),
 151            set_frame_size as extern "C" fn(&Object, Sel, NSSize),
 152        );
 153        decl.add_method(
 154            sel!(displayLayer:),
 155            display_layer as extern "C" fn(&Object, Sel, id),
 156        );
 157
 158        decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
 159        decl.add_method(
 160            sel!(validAttributesForMarkedText),
 161            valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
 162        );
 163        decl.add_method(
 164            sel!(hasMarkedText),
 165            has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
 166        );
 167        decl.add_method(
 168            sel!(markedRange),
 169            marked_range as extern "C" fn(&Object, Sel) -> NSRange,
 170        );
 171        decl.add_method(
 172            sel!(selectedRange),
 173            selected_range as extern "C" fn(&Object, Sel) -> NSRange,
 174        );
 175        decl.add_method(
 176            sel!(firstRectForCharacterRange:actualRange:),
 177            first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
 178        );
 179        decl.add_method(
 180            sel!(insertText:replacementRange:),
 181            insert_text as extern "C" fn(&Object, Sel, id, NSRange),
 182        );
 183        decl.add_method(
 184            sel!(setMarkedText:selectedRange:replacementRange:),
 185            set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
 186        );
 187        decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
 188        decl.add_method(
 189            sel!(attributedSubstringForProposedRange:actualRange:),
 190            attributed_substring_for_proposed_range
 191                as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
 192        );
 193        decl.add_method(
 194            sel!(viewDidChangeEffectiveAppearance),
 195            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
 196        );
 197
 198        // Suppress beep on keystrokes with modifier keys.
 199        decl.add_method(
 200            sel!(doCommandBySelector:),
 201            do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
 202        );
 203
 204        decl.add_method(
 205            sel!(acceptsFirstMouse:),
 206            accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
 207        );
 208
 209        decl.register()
 210    };
 211}
 212
 213pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
 214    point(
 215        px(position.x as f32),
 216        // MacOS screen coordinates are relative to bottom left
 217        window_height - px(position.y as f32),
 218    )
 219}
 220
 221unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
 222    let mut decl = ClassDecl::new(name, superclass).unwrap();
 223    decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 224    decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
 225    decl.add_method(
 226        sel!(canBecomeMainWindow),
 227        yes as extern "C" fn(&Object, Sel) -> BOOL,
 228    );
 229    decl.add_method(
 230        sel!(canBecomeKeyWindow),
 231        yes as extern "C" fn(&Object, Sel) -> BOOL,
 232    );
 233    decl.add_method(
 234        sel!(windowDidResize:),
 235        window_did_resize as extern "C" fn(&Object, Sel, id),
 236    );
 237    decl.add_method(
 238        sel!(windowWillEnterFullScreen:),
 239        window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
 240    );
 241    decl.add_method(
 242        sel!(windowWillExitFullScreen:),
 243        window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
 244    );
 245    decl.add_method(
 246        sel!(windowDidMove:),
 247        window_did_move as extern "C" fn(&Object, Sel, id),
 248    );
 249    decl.add_method(
 250        sel!(windowDidBecomeKey:),
 251        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 252    );
 253    decl.add_method(
 254        sel!(windowDidResignKey:),
 255        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 256    );
 257    decl.add_method(
 258        sel!(windowShouldClose:),
 259        window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
 260    );
 261    decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
 262    decl.register()
 263}
 264
 265///Used to track what the IME does when we send it a keystroke.
 266///This is only used to handle the case where the IME mysteriously
 267///swallows certain keys.
 268///
 269///Basically a direct copy of the approach that WezTerm uses in:
 270///github.com/wez/wezterm : d5755f3e : window/src/os/macos/window.rs
 271enum ImeState {
 272    Continue,
 273    Acted,
 274    None,
 275}
 276
 277struct InsertText {
 278    replacement_range: Option<Range<usize>>,
 279    text: String,
 280}
 281
 282struct MacWindowState {
 283    handle: AnyWindowHandle,
 284    executor: Executor,
 285    native_window: id,
 286    renderer: MetalRenderer,
 287    scene_to_render: Option<Scene>,
 288    kind: WindowKind,
 289    event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
 290    activate_callback: Option<Box<dyn FnMut(bool)>>,
 291    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 292    fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
 293    moved_callback: Option<Box<dyn FnMut()>>,
 294    should_close_callback: Option<Box<dyn FnMut() -> bool>>,
 295    close_callback: Option<Box<dyn FnOnce()>>,
 296    appearance_changed_callback: Option<Box<dyn FnMut()>>,
 297    input_handler: Option<Box<dyn PlatformInputHandler>>,
 298    pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
 299    last_key_equivalent: Option<KeyDownEvent>,
 300    synthetic_drag_counter: usize,
 301    last_fresh_keydown: Option<Keystroke>,
 302    traffic_light_position: Option<Point<Pixels>>,
 303    previous_modifiers_changed_event: Option<InputEvent>,
 304    // State tracking what the IME did after the last request
 305    ime_state: ImeState,
 306    // Retains the last IME Text
 307    ime_text: Option<String>,
 308}
 309
 310impl MacWindowState {
 311    fn move_traffic_light(&self) {
 312        if let Some(traffic_light_position) = self.traffic_light_position {
 313            let titlebar_height = self.titlebar_height();
 314
 315            unsafe {
 316                let close_button: id = msg_send![
 317                    self.native_window,
 318                    standardWindowButton: NSWindowButton::NSWindowCloseButton
 319                ];
 320                let min_button: id = msg_send![
 321                    self.native_window,
 322                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
 323                ];
 324                let zoom_button: id = msg_send![
 325                    self.native_window,
 326                    standardWindowButton: NSWindowButton::NSWindowZoomButton
 327                ];
 328
 329                let mut close_button_frame: CGRect = msg_send![close_button, frame];
 330                let mut min_button_frame: CGRect = msg_send![min_button, frame];
 331                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
 332                let mut origin = point(
 333                    traffic_light_position.x,
 334                    titlebar_height
 335                        - traffic_light_position.y
 336                        - px(close_button_frame.size.height as f32),
 337                );
 338                let button_spacing =
 339                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
 340
 341                close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 342                let _: () = msg_send![close_button, setFrame: close_button_frame];
 343                origin.x += button_spacing;
 344
 345                min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 346                let _: () = msg_send![min_button, setFrame: min_button_frame];
 347                origin.x += button_spacing;
 348
 349                zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 350                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
 351                origin.x += button_spacing;
 352            }
 353        }
 354    }
 355
 356    fn is_fullscreen(&self) -> bool {
 357        unsafe {
 358            let style_mask = self.native_window.styleMask();
 359            style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
 360        }
 361    }
 362
 363    fn bounds(&self) -> WindowBounds {
 364        unsafe {
 365            if self.is_fullscreen() {
 366                return WindowBounds::Fullscreen;
 367            }
 368
 369            let frame = self.frame();
 370            let screen_size = self.native_window.screen().visibleFrame().into();
 371            if frame.size == screen_size {
 372                WindowBounds::Maximized
 373            } else {
 374                WindowBounds::Fixed(frame)
 375            }
 376        }
 377    }
 378
 379    fn frame(&self) -> Bounds<GlobalPixels> {
 380        unsafe {
 381            let frame = NSWindow::frame(self.native_window);
 382            display_bounds_from_native(mem::transmute::<NSRect, CGRect>(frame))
 383        }
 384    }
 385
 386    fn content_size(&self) -> Size<Pixels> {
 387        let NSSize { width, height, .. } =
 388            unsafe { NSView::frame(self.native_window.contentView()) }.size;
 389        size(px(width as f32), px(height as f32))
 390    }
 391
 392    fn scale_factor(&self) -> f32 {
 393        get_scale_factor(self.native_window)
 394    }
 395
 396    fn titlebar_height(&self) -> Pixels {
 397        unsafe {
 398            let frame = NSWindow::frame(self.native_window);
 399            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
 400            px((frame.size.height - content_layout_rect.size.height) as f32)
 401        }
 402    }
 403
 404    fn to_screen_ns_point(&self, point: Point<Pixels>) -> NSPoint {
 405        unsafe {
 406            let point = NSPoint::new(
 407                point.x.into(),
 408                (self.content_size().height - point.y).into(),
 409            );
 410            msg_send![self.native_window, convertPointToScreen: point]
 411        }
 412    }
 413}
 414
 415unsafe impl Send for MacWindowState {}
 416
 417pub struct MacWindow(Arc<Mutex<MacWindowState>>);
 418
 419impl MacWindow {
 420    pub fn open(handle: AnyWindowHandle, options: WindowOptions, executor: Executor) -> Self {
 421        unsafe {
 422            let pool = NSAutoreleasePool::new(nil);
 423
 424            let mut style_mask;
 425            if let Some(titlebar) = options.titlebar.as_ref() {
 426                style_mask = NSWindowStyleMask::NSClosableWindowMask
 427                    | NSWindowStyleMask::NSMiniaturizableWindowMask
 428                    | NSWindowStyleMask::NSResizableWindowMask
 429                    | NSWindowStyleMask::NSTitledWindowMask;
 430
 431                if titlebar.appears_transparent {
 432                    style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 433                }
 434            } else {
 435                style_mask = NSWindowStyleMask::NSTitledWindowMask
 436                    | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 437            }
 438
 439            let native_window: id = match options.kind {
 440                WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
 441                WindowKind::PopUp => {
 442                    style_mask |= NSWindowStyleMaskNonactivatingPanel;
 443                    msg_send![PANEL_CLASS, alloc]
 444                }
 445            };
 446
 447            let display = options
 448                .display_id
 449                .and_then(|display_id| MacDisplay::all().find(|display| display.id() == display_id))
 450                .unwrap_or_else(|| MacDisplay::primary());
 451
 452            let mut target_screen = nil;
 453            let screens = NSScreen::screens(nil);
 454            let count: u64 = cocoa::foundation::NSArray::count(screens);
 455            for i in 0..count {
 456                let screen = cocoa::foundation::NSArray::objectAtIndex(screens, i);
 457                let device_description = NSScreen::deviceDescription(screen);
 458                let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
 459                let screen_number = device_description.objectForKey_(screen_number_key);
 460                let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
 461                if screen_number as u32 == display.id().0 {
 462                    target_screen = screen;
 463                    break;
 464                }
 465            }
 466
 467            let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
 468                NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
 469                style_mask,
 470                NSBackingStoreBuffered,
 471                NO,
 472                target_screen,
 473            );
 474            assert!(!native_window.is_null());
 475
 476            let screen = native_window.screen();
 477            match options.bounds {
 478                WindowBounds::Fullscreen => {
 479                    native_window.toggleFullScreen_(nil);
 480                }
 481                WindowBounds::Maximized => {
 482                    native_window.setFrame_display_(screen.visibleFrame(), YES);
 483                }
 484                WindowBounds::Fixed(bounds) => {
 485                    let display_bounds = display.bounds();
 486                    let frame = if bounds.intersects(&display_bounds) {
 487                        display_bounds_to_native(bounds)
 488                    } else {
 489                        display_bounds_to_native(display_bounds)
 490                    };
 491                    native_window.setFrame_display_(mem::transmute::<CGRect, NSRect>(frame), YES);
 492                }
 493            }
 494
 495            let native_view: id = msg_send![VIEW_CLASS, alloc];
 496            let native_view = NSView::init(native_view);
 497
 498            assert!(!native_view.is_null());
 499
 500            let window = Self(Arc::new(Mutex::new(MacWindowState {
 501                handle,
 502                executor,
 503                native_window,
 504                renderer: MetalRenderer::new(true),
 505                scene_to_render: None,
 506                kind: options.kind,
 507                event_callback: None,
 508                activate_callback: None,
 509                resize_callback: None,
 510                fullscreen_callback: None,
 511                moved_callback: None,
 512                should_close_callback: None,
 513                close_callback: None,
 514                appearance_changed_callback: None,
 515                input_handler: None,
 516                pending_key_down: None,
 517                last_key_equivalent: None,
 518                synthetic_drag_counter: 0,
 519                last_fresh_keydown: None,
 520                traffic_light_position: options
 521                    .titlebar
 522                    .as_ref()
 523                    .and_then(|titlebar| titlebar.traffic_light_position),
 524                previous_modifiers_changed_event: None,
 525                ime_state: ImeState::None,
 526                ime_text: None,
 527            })));
 528
 529            (*native_window).set_ivar(
 530                WINDOW_STATE_IVAR,
 531                Arc::into_raw(window.0.clone()) as *const c_void,
 532            );
 533            native_window.setDelegate_(native_window);
 534            (*native_view).set_ivar(
 535                WINDOW_STATE_IVAR,
 536                Arc::into_raw(window.0.clone()) as *const c_void,
 537            );
 538
 539            if let Some(title) = options
 540                .titlebar
 541                .as_ref()
 542                .and_then(|t| t.title.as_ref().map(AsRef::as_ref))
 543            {
 544                native_window.setTitle_(NSString::alloc(nil).init_str(title));
 545            }
 546
 547            native_window.setMovable_(options.is_movable as BOOL);
 548
 549            if options
 550                .titlebar
 551                .map_or(true, |titlebar| titlebar.appears_transparent)
 552            {
 553                native_window.setTitlebarAppearsTransparent_(YES);
 554                native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
 555            }
 556
 557            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
 558            native_view.setWantsBestResolutionOpenGLSurface_(YES);
 559
 560            // From winit crate: On Mojave, views automatically become layer-backed shortly after
 561            // being added to a native_window. Changing the layer-backedness of a view breaks the
 562            // association between the view and its associated OpenGL context. To work around this,
 563            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
 564            // itself and break the association with its context.
 565            native_view.setWantsLayer(YES);
 566            let _: () = msg_send![
 567                native_view,
 568                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
 569            ];
 570
 571            native_window.setContentView_(native_view.autorelease());
 572            native_window.makeFirstResponder_(native_view);
 573
 574            if options.center {
 575                native_window.center();
 576            }
 577
 578            match options.kind {
 579                WindowKind::Normal => {
 580                    native_window.setLevel_(NSNormalWindowLevel);
 581                    native_window.setAcceptsMouseMovedEvents_(YES);
 582                }
 583                WindowKind::PopUp => {
 584                    // Use a tracking area to allow receiving MouseMoved events even when
 585                    // the window or application aren't active, which is often the case
 586                    // e.g. for notification windows.
 587                    let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
 588                    let _: () = msg_send![
 589                        tracking_area,
 590                        initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
 591                        options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
 592                        owner: native_view
 593                        userInfo: nil
 594                    ];
 595                    let _: () =
 596                        msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
 597
 598                    native_window.setLevel_(NSPopUpWindowLevel);
 599                    let _: () = msg_send![
 600                        native_window,
 601                        setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
 602                    ];
 603                    native_window.setCollectionBehavior_(
 604                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
 605                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
 606                    );
 607                }
 608            }
 609            if options.focus {
 610                native_window.makeKeyAndOrderFront_(nil);
 611            } else if options.show {
 612                native_window.orderFront_(nil);
 613            }
 614
 615            window.0.lock().move_traffic_light();
 616            pool.drain();
 617
 618            window
 619        }
 620    }
 621
 622    pub fn main_window() -> Option<AnyWindowHandle> {
 623        unsafe {
 624            let app = NSApplication::sharedApplication(nil);
 625            let main_window: id = msg_send![app, mainWindow];
 626            if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
 627                let handle = get_window_state(&*main_window).lock().handle;
 628                Some(handle)
 629            } else {
 630                None
 631            }
 632        }
 633    }
 634}
 635
 636impl Drop for MacWindow {
 637    fn drop(&mut self) {
 638        let this = self.0.clone();
 639        let executor = self.0.lock().executor.clone();
 640        executor
 641            .run_on_main(move || unsafe {
 642                this.lock().native_window.close();
 643            })
 644            .detach();
 645    }
 646}
 647
 648impl PlatformWindow for MacWindow {
 649    fn bounds(&self) -> WindowBounds {
 650        self.0.as_ref().lock().bounds()
 651    }
 652
 653    fn content_size(&self) -> Size<Pixels> {
 654        self.0.as_ref().lock().content_size().into()
 655    }
 656
 657    fn scale_factor(&self) -> f32 {
 658        self.0.as_ref().lock().scale_factor()
 659    }
 660
 661    fn titlebar_height(&self) -> Pixels {
 662        self.0.as_ref().lock().titlebar_height()
 663    }
 664
 665    fn appearance(&self) -> WindowAppearance {
 666        unsafe {
 667            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
 668            WindowAppearance::from_native(appearance)
 669        }
 670    }
 671
 672    fn display(&self) -> Rc<dyn PlatformDisplay> {
 673        unsafe {
 674            let screen = self.0.lock().native_window.screen();
 675            let device_description: id = msg_send![screen, deviceDescription];
 676            let screen_number: id = NSDictionary::valueForKey_(
 677                device_description,
 678                NSString::alloc(nil).init_str("NSScreenNumber"),
 679            );
 680
 681            let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
 682
 683            Rc::new(MacDisplay(screen_number))
 684        }
 685    }
 686
 687    fn mouse_position(&self) -> Point<Pixels> {
 688        let position = unsafe {
 689            self.0
 690                .lock()
 691                .native_window
 692                .mouseLocationOutsideOfEventStream()
 693        };
 694        convert_mouse_position(position, self.content_size().height)
 695    }
 696
 697    fn as_any_mut(&mut self) -> &mut dyn Any {
 698        self
 699    }
 700
 701    fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
 702        self.0.as_ref().lock().input_handler = Some(input_handler);
 703    }
 704
 705    fn prompt(
 706        &self,
 707        level: WindowPromptLevel,
 708        msg: &str,
 709        answers: &[&str],
 710    ) -> oneshot::Receiver<usize> {
 711        // macOs applies overrides to modal window buttons after they are added.
 712        // Two most important for this logic are:
 713        // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
 714        // * Last button added to the modal via `addButtonWithTitle` stays focused
 715        // * Focused buttons react on "space"/" " keypresses
 716        // * Usage of `keyEquivalent`, `makeFirstResponder` or `setInitialFirstResponder` does not change the focus
 717        //
 718        // See also https://developer.apple.com/documentation/appkit/nsalert/1524532-addbuttonwithtitle#discussion
 719        // ```
 720        // By default, the first button has a key equivalent of Return,
 721        // any button with a title of “Cancel” has a key equivalent of Escape,
 722        // and any button with the title “Don’t Save” has a key equivalent of Command-D (but only if it’s not the first button).
 723        // ```
 724        //
 725        // To avoid situations when the last element added is "Cancel" and it gets the focus
 726        // (hence stealing both ESC and Space shortcuts), we find and add one non-Cancel button
 727        // last, so it gets focus and a Space shortcut.
 728        // This way, "Save this file? Yes/No/Cancel"-ish modals will get all three buttons mapped with a key.
 729        let latest_non_cancel_label = answers
 730            .iter()
 731            .enumerate()
 732            .rev()
 733            .find(|(_, &label)| label != "Cancel")
 734            .filter(|&(label_index, _)| label_index > 0);
 735
 736        unsafe {
 737            let alert: id = msg_send![class!(NSAlert), alloc];
 738            let alert: id = msg_send![alert, init];
 739            let alert_style = match level {
 740                WindowPromptLevel::Info => 1,
 741                WindowPromptLevel::Warning => 0,
 742                WindowPromptLevel::Critical => 2,
 743            };
 744            let _: () = msg_send![alert, setAlertStyle: alert_style];
 745            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
 746
 747            for (ix, answer) in answers
 748                .iter()
 749                .enumerate()
 750                .filter(|&(ix, _)| Some(ix) != latest_non_cancel_label.map(|(ix, _)| ix))
 751            {
 752                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 753                let _: () = msg_send![button, setTag: ix as NSInteger];
 754            }
 755            if let Some((ix, answer)) = latest_non_cancel_label {
 756                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 757                let _: () = msg_send![button, setTag: ix as NSInteger];
 758            }
 759
 760            let (done_tx, done_rx) = oneshot::channel();
 761            let done_tx = Cell::new(Some(done_tx));
 762            let block = ConcreteBlock::new(move |answer: NSInteger| {
 763                if let Some(done_tx) = done_tx.take() {
 764                    let _ = done_tx.send(answer.try_into().unwrap());
 765                }
 766            });
 767            let block = block.copy();
 768            let native_window = self.0.lock().native_window;
 769            let executor = self.0.lock().executor.clone();
 770            executor
 771                .spawn_on_main_local(async move {
 772                    let _: () = msg_send![
 773                        alert,
 774                        beginSheetModalForWindow: native_window
 775                        completionHandler: block
 776                    ];
 777                })
 778                .detach();
 779
 780            done_rx
 781        }
 782    }
 783
 784    fn activate(&self) {
 785        let window = self.0.lock().native_window;
 786        let executor = self.0.lock().executor.clone();
 787        executor
 788            .spawn_on_main_local(async move {
 789                unsafe {
 790                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
 791                }
 792            })
 793            .detach();
 794    }
 795
 796    fn set_title(&mut self, title: &str) {
 797        unsafe {
 798            let app = NSApplication::sharedApplication(nil);
 799            let window = self.0.lock().native_window;
 800            let title = ns_string(title);
 801            let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
 802            let _: () = msg_send![window, setTitle: title];
 803            self.0.lock().move_traffic_light();
 804        }
 805    }
 806
 807    fn set_edited(&mut self, edited: bool) {
 808        unsafe {
 809            let window = self.0.lock().native_window;
 810            msg_send![window, setDocumentEdited: edited as BOOL]
 811        }
 812
 813        // Changing the document edited state resets the traffic light position,
 814        // so we have to move it again.
 815        self.0.lock().move_traffic_light();
 816    }
 817
 818    fn show_character_palette(&self) {
 819        unsafe {
 820            let app = NSApplication::sharedApplication(nil);
 821            let window = self.0.lock().native_window;
 822            let _: () = msg_send![app, orderFrontCharacterPalette: window];
 823        }
 824    }
 825
 826    fn minimize(&self) {
 827        let window = self.0.lock().native_window;
 828        unsafe {
 829            window.miniaturize_(nil);
 830        }
 831    }
 832
 833    fn zoom(&self) {
 834        let this = self.0.lock();
 835        let window = this.native_window;
 836        this.executor
 837            .spawn_on_main_local(async move {
 838                unsafe {
 839                    window.zoom_(nil);
 840                }
 841            })
 842            .detach();
 843    }
 844
 845    fn toggle_full_screen(&self) {
 846        let this = self.0.lock();
 847        let window = this.native_window;
 848        this.executor
 849            .spawn_on_main_local(async move {
 850                unsafe {
 851                    window.toggleFullScreen_(nil);
 852                }
 853            })
 854            .detach();
 855    }
 856
 857    fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
 858        self.0.as_ref().lock().event_callback = Some(callback);
 859    }
 860
 861    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
 862        self.0.as_ref().lock().activate_callback = Some(callback);
 863    }
 864
 865    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
 866        self.0.as_ref().lock().resize_callback = Some(callback);
 867    }
 868
 869    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
 870        self.0.as_ref().lock().fullscreen_callback = Some(callback);
 871    }
 872
 873    fn on_moved(&self, callback: Box<dyn FnMut()>) {
 874        self.0.as_ref().lock().moved_callback = Some(callback);
 875    }
 876
 877    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
 878        self.0.as_ref().lock().should_close_callback = Some(callback);
 879    }
 880
 881    fn on_close(&self, callback: Box<dyn FnOnce()>) {
 882        self.0.as_ref().lock().close_callback = Some(callback);
 883    }
 884
 885    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
 886        self.0.lock().appearance_changed_callback = Some(callback);
 887    }
 888
 889    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
 890        let self_borrow = self.0.lock();
 891        let self_handle = self_borrow.handle;
 892
 893        unsafe {
 894            let app = NSApplication::sharedApplication(nil);
 895
 896            // Convert back to screen coordinates
 897            let screen_point = self_borrow.to_screen_ns_point(position);
 898
 899            let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
 900            let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
 901
 902            let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
 903            let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
 904            if is_panel == YES || is_window == YES {
 905                let topmost_window = get_window_state(&*top_most_window).lock().handle;
 906                topmost_window == self_handle
 907            } else {
 908                // Someone else's window is on top
 909                false
 910            }
 911        }
 912    }
 913
 914    fn draw(&self, scene: Scene) {
 915        let mut this = self.0.lock();
 916        this.scene_to_render = Some(scene);
 917        unsafe {
 918            let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES];
 919        }
 920    }
 921
 922    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
 923        self.0.lock().renderer.sprite_atlas().clone()
 924    }
 925}
 926
 927fn get_scale_factor(native_window: id) -> f32 {
 928    unsafe {
 929        let screen: id = msg_send![native_window, screen];
 930        NSScreen::backingScaleFactor(screen) as f32
 931    }
 932}
 933
 934unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
 935    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 936    let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
 937    let rc2 = rc1.clone();
 938    mem::forget(rc1);
 939    rc2
 940}
 941
 942unsafe fn drop_window_state(object: &Object) {
 943    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 944    Rc::from_raw(raw as *mut RefCell<MacWindowState>);
 945}
 946
 947extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
 948    YES
 949}
 950
 951extern "C" fn dealloc_window(this: &Object, _: Sel) {
 952    unsafe {
 953        drop_window_state(this);
 954        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
 955    }
 956}
 957
 958extern "C" fn dealloc_view(this: &Object, _: Sel) {
 959    unsafe {
 960        drop_window_state(this);
 961        let _: () = msg_send![super(this, class!(NSView)), dealloc];
 962    }
 963}
 964
 965extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
 966    handle_key_event(this, native_event, true)
 967}
 968
 969extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
 970    handle_key_event(this, native_event, false);
 971}
 972
 973extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
 974    let window_state = unsafe { get_window_state(this) };
 975    let mut lock = window_state.as_ref().lock();
 976
 977    let window_height = lock.content_size().height;
 978    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
 979
 980    if let Some(InputEvent::KeyDown(event)) = event {
 981        // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
 982        // If that event isn't handled, it will then dispatch a "key down" event. GPUI
 983        // makes no distinction between these two types of events, so we need to ignore
 984        // the "key down" event if we've already just processed its "key equivalent" version.
 985        if key_equivalent {
 986            lock.last_key_equivalent = Some(event.clone());
 987        } else if lock.last_key_equivalent.take().as_ref() == Some(&event) {
 988            return NO;
 989        }
 990
 991        let keydown = event.keystroke.clone();
 992        let fn_modifier = keydown.modifiers.function;
 993        // Ignore events from held-down keys after some of the initially-pressed keys
 994        // were released.
 995        if event.is_held {
 996            if lock.last_fresh_keydown.as_ref() != Some(&keydown) {
 997                return YES;
 998            }
 999        } else {
1000            lock.last_fresh_keydown = Some(keydown);
1001        }
1002        lock.pending_key_down = Some((event, None));
1003        drop(lock);
1004
1005        // Send the event to the input context for IME handling, unless the `fn` modifier is
1006        // being pressed.
1007        if !fn_modifier {
1008            unsafe {
1009                let input_context: id = msg_send![this, inputContext];
1010                let _: BOOL = msg_send![input_context, handleEvent: native_event];
1011            }
1012        }
1013
1014        let mut handled = false;
1015        let mut lock = window_state.lock();
1016        let ime_text = lock.ime_text.clone();
1017        if let Some((event, insert_text)) = lock.pending_key_down.take() {
1018            let is_held = event.is_held;
1019            if let Some(mut callback) = lock.event_callback.take() {
1020                drop(lock);
1021
1022                let is_composing =
1023                    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1024                        .flatten()
1025                        .is_some();
1026                if !is_composing {
1027                    // if the IME has changed the key, we'll first emit an event with the character
1028                    // generated by the IME system; then fallback to the keystroke if that is not
1029                    // handled.
1030                    // cases that we have working:
1031                    // - " on a brazillian layout by typing <quote><space>
1032                    // - ctrl-` on a brazillian layout by typing <ctrl-`>
1033                    // - $ on a czech QWERTY layout by typing <alt-4>
1034                    // - 4 on a czech QWERTY layout by typing <shift-4>
1035                    // - ctrl-4 on a czech QWERTY layout by typing <ctrl-alt-4> (or <ctrl-shift-4>)
1036                    if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) {
1037                        let event_with_ime_text = KeyDownEvent {
1038                            is_held: false,
1039                            keystroke: Keystroke {
1040                                // we match ctrl because some use-cases need it.
1041                                // we don't match alt because it's often used to generate the optional character
1042                                // we don't match shift because we're not here with letters (usually)
1043                                // we don't match cmd/fn because they don't seem to use IME
1044                                modifiers: Default::default(),
1045                                key: ime_text.clone().unwrap(),
1046                                ime_key: None, // todo!("handle IME key")
1047                            },
1048                        };
1049                        handled = callback(InputEvent::KeyDown(event_with_ime_text));
1050                    }
1051                    if !handled {
1052                        // empty key happens when you type a deadkey in input composition.
1053                        // (e.g. on a brazillian keyboard typing quote is a deadkey)
1054                        if !event.keystroke.key.is_empty() {
1055                            handled = callback(InputEvent::KeyDown(event));
1056                        }
1057                    }
1058                }
1059
1060                if !handled {
1061                    if let Some(insert) = insert_text {
1062                        handled = true;
1063                        with_input_handler(this, |input_handler| {
1064                            input_handler
1065                                .replace_text_in_range(insert.replacement_range, &insert.text)
1066                        });
1067                    } else if !is_composing && is_held {
1068                        if let Some(last_insert_text) = ime_text {
1069                            //MacOS IME is a bit funky, and even when you've told it there's nothing to
1070                            //inter it will still swallow certain keys (e.g. 'f', 'j') and not others
1071                            //(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
1072                            with_input_handler(this, |input_handler| {
1073                                if input_handler.selected_text_range().is_none() {
1074                                    handled = true;
1075                                    input_handler.replace_text_in_range(None, &last_insert_text)
1076                                }
1077                            });
1078                        }
1079                    }
1080                }
1081
1082                window_state.lock().event_callback = Some(callback);
1083            }
1084        } else {
1085            handled = true;
1086        }
1087
1088        handled as BOOL
1089    } else {
1090        NO
1091    }
1092}
1093
1094extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
1095    let window_state = unsafe { get_window_state(this) };
1096    let weak_window_state = Arc::downgrade(&window_state);
1097    let mut lock = window_state.as_ref().lock();
1098    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
1099
1100    let window_height = lock.content_size().height;
1101    let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
1102
1103    if let Some(mut event) = event {
1104        let synthesized_second_event = match &mut event {
1105            InputEvent::MouseDown(
1106                event @ MouseDownEvent {
1107                    button: MouseButton::Left,
1108                    modifiers: Modifiers { control: true, .. },
1109                    ..
1110                },
1111            ) => {
1112                *event = MouseDownEvent {
1113                    button: MouseButton::Right,
1114                    modifiers: Modifiers {
1115                        control: false,
1116                        ..event.modifiers
1117                    },
1118                    click_count: 1,
1119                    ..*event
1120                };
1121
1122                Some(InputEvent::MouseDown(MouseDownEvent {
1123                    button: MouseButton::Right,
1124                    ..*event
1125                }))
1126            }
1127
1128            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
1129            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
1130            // user is still holding ctrl when releasing the left mouse button
1131            InputEvent::MouseUp(MouseUpEvent {
1132                button: MouseButton::Left,
1133                modifiers: Modifiers { control: true, .. },
1134                ..
1135            }) => {
1136                lock.synthetic_drag_counter += 1;
1137                return;
1138            }
1139
1140            _ => None,
1141        };
1142
1143        match &event {
1144            InputEvent::MouseMoved(
1145                event @ MouseMoveEvent {
1146                    pressed_button: Some(_),
1147                    ..
1148                },
1149            ) => {
1150                lock.synthetic_drag_counter += 1;
1151                let executor = lock.executor.clone();
1152                executor
1153                    .spawn_on_main_local(synthetic_drag(
1154                        weak_window_state,
1155                        lock.synthetic_drag_counter,
1156                        event.clone(),
1157                    ))
1158                    .detach();
1159            }
1160
1161            InputEvent::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
1162
1163            InputEvent::MouseUp(MouseUpEvent {
1164                button: MouseButton::Left,
1165                ..
1166            }) => {
1167                lock.synthetic_drag_counter += 1;
1168            }
1169
1170            InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1171                // Only raise modifiers changed event when they have actually changed
1172                if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
1173                    modifiers: prev_modifiers,
1174                })) = &lock.previous_modifiers_changed_event
1175                {
1176                    if prev_modifiers == modifiers {
1177                        return;
1178                    }
1179                }
1180
1181                lock.previous_modifiers_changed_event = Some(event.clone());
1182            }
1183
1184            _ => {}
1185        }
1186
1187        if let Some(mut callback) = lock.event_callback.take() {
1188            drop(lock);
1189            callback(event);
1190            if let Some(event) = synthesized_second_event {
1191                callback(event);
1192            }
1193            window_state.lock().event_callback = Some(callback);
1194        }
1195    }
1196}
1197
1198// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
1199// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
1200extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
1201    let window_state = unsafe { get_window_state(this) };
1202    let mut lock = window_state.as_ref().lock();
1203
1204    let keystroke = Keystroke {
1205        modifiers: Default::default(),
1206        key: ".".into(),
1207        ime_key: None,
1208    };
1209    let event = InputEvent::KeyDown(KeyDownEvent {
1210        keystroke: keystroke.clone(),
1211        is_held: false,
1212    });
1213
1214    lock.last_fresh_keydown = Some(keystroke);
1215    if let Some(mut callback) = lock.event_callback.take() {
1216        drop(lock);
1217        callback(event);
1218        window_state.lock().event_callback = Some(callback);
1219    }
1220}
1221
1222extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1223    let window_state = unsafe { get_window_state(this) };
1224    window_state.as_ref().lock().move_traffic_light();
1225}
1226
1227extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1228    window_fullscreen_changed(this, true);
1229}
1230
1231extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1232    window_fullscreen_changed(this, false);
1233}
1234
1235fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
1236    let window_state = unsafe { get_window_state(this) };
1237    let mut lock = window_state.as_ref().lock();
1238    if let Some(mut callback) = lock.fullscreen_callback.take() {
1239        drop(lock);
1240        callback(is_fullscreen);
1241        window_state.lock().fullscreen_callback = Some(callback);
1242    }
1243}
1244
1245extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
1246    let window_state = unsafe { get_window_state(this) };
1247    let mut lock = window_state.as_ref().lock();
1248    if let Some(mut callback) = lock.moved_callback.take() {
1249        drop(lock);
1250        callback();
1251        window_state.lock().moved_callback = Some(callback);
1252    }
1253}
1254
1255extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1256    let window_state = unsafe { get_window_state(this) };
1257    let lock = window_state.lock();
1258    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
1259
1260    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1261    // `windowDidBecomeKey` message to the previous key window even though that window
1262    // isn't actually key. This causes a bug if the application is later activated while
1263    // the pop-up is still open, making it impossible to activate the previous key window
1264    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1265    // the app and re-activate it, which is a pretty bad UX.
1266    // The following code detects the spurious event and invokes `resignKeyWindow`:
1267    // in theory, we're not supposed to invoke this method manually but it balances out
1268    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1269    if selector == sel!(windowDidBecomeKey:) {
1270        if !is_active {
1271            unsafe {
1272                let _: () = msg_send![lock.native_window, resignKeyWindow];
1273                return;
1274            }
1275        }
1276    }
1277
1278    let executor = lock.executor.clone();
1279    drop(lock);
1280    executor
1281        .spawn_on_main_local(async move {
1282            let mut lock = window_state.as_ref().lock();
1283            if let Some(mut callback) = lock.activate_callback.take() {
1284                drop(lock);
1285                callback(is_active);
1286                window_state.lock().activate_callback = Some(callback);
1287            };
1288        })
1289        .detach();
1290}
1291
1292extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1293    let window_state = unsafe { get_window_state(this) };
1294    let mut lock = window_state.as_ref().lock();
1295    if let Some(mut callback) = lock.should_close_callback.take() {
1296        drop(lock);
1297        let should_close = callback();
1298        window_state.lock().should_close_callback = Some(callback);
1299        should_close as BOOL
1300    } else {
1301        YES
1302    }
1303}
1304
1305extern "C" fn close_window(this: &Object, _: Sel) {
1306    unsafe {
1307        let close_callback = {
1308            let window_state = get_window_state(this);
1309            window_state
1310                .as_ref()
1311                .try_lock()
1312                .and_then(|mut window_state| window_state.close_callback.take())
1313        };
1314
1315        if let Some(callback) = close_callback {
1316            callback();
1317        }
1318
1319        let _: () = msg_send![super(this, class!(NSWindow)), close];
1320    }
1321}
1322
1323extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1324    let window_state = unsafe { get_window_state(this) };
1325    let window_state = window_state.as_ref().lock();
1326    window_state.renderer.layer().as_ptr() as id
1327}
1328
1329extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1330    let window_state = unsafe { get_window_state(this) };
1331    let mut lock = window_state.as_ref().lock();
1332
1333    unsafe {
1334        let scale_factor = lock.scale_factor() as f64;
1335        let size = lock.content_size();
1336        let drawable_size: NSSize = NSSize {
1337            width: f64::from(size.width) * scale_factor,
1338            height: f64::from(size.height) * scale_factor,
1339        };
1340
1341        let _: () = msg_send![
1342            lock.renderer.layer(),
1343            setContentsScale: scale_factor
1344        ];
1345        let _: () = msg_send![
1346            lock.renderer.layer(),
1347            setDrawableSize: drawable_size
1348        ];
1349    }
1350
1351    if let Some(mut callback) = lock.resize_callback.take() {
1352        let content_size = lock.content_size();
1353        let scale_factor = lock.scale_factor();
1354        drop(lock);
1355        callback(content_size, scale_factor);
1356        window_state.as_ref().lock().resize_callback = Some(callback);
1357    };
1358}
1359
1360extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1361    let window_state = unsafe { get_window_state(this) };
1362    let lock = window_state.as_ref().lock();
1363
1364    if lock.content_size() == size.into() {
1365        return;
1366    }
1367
1368    unsafe {
1369        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1370    }
1371
1372    let scale_factor = lock.scale_factor() as f64;
1373    let drawable_size: NSSize = NSSize {
1374        width: size.width * scale_factor,
1375        height: size.height * scale_factor,
1376    };
1377
1378    unsafe {
1379        let _: () = msg_send![
1380            lock.renderer.layer(),
1381            setDrawableSize: drawable_size
1382        ];
1383    }
1384
1385    drop(lock);
1386    let mut lock = window_state.lock();
1387    if let Some(mut callback) = lock.resize_callback.take() {
1388        let content_size = lock.content_size();
1389        let scale_factor = lock.scale_factor();
1390        drop(lock);
1391        callback(content_size, scale_factor);
1392        window_state.lock().resize_callback = Some(callback);
1393    };
1394}
1395
1396extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1397    unsafe {
1398        let window_state = get_window_state(this);
1399        let mut window_state = window_state.as_ref().lock();
1400        if let Some(scene) = window_state.scene_to_render.take() {
1401            window_state.renderer.draw(&scene);
1402        }
1403    }
1404}
1405
1406extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1407    unsafe { msg_send![class!(NSArray), array] }
1408}
1409
1410extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1411    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1412        .flatten()
1413        .is_some() as BOOL
1414}
1415
1416extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1417    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1418        .flatten()
1419        .map_or(NSRange::invalid(), |range| range.into())
1420}
1421
1422extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1423    with_input_handler(this, |input_handler| input_handler.selected_text_range())
1424        .flatten()
1425        .map_or(NSRange::invalid(), |range| range.into())
1426}
1427
1428extern "C" fn first_rect_for_character_range(
1429    this: &Object,
1430    _: Sel,
1431    range: NSRange,
1432    _: id,
1433) -> NSRect {
1434    let frame = unsafe {
1435        let window = get_window_state(this).lock().native_window;
1436        NSView::frame(window)
1437    };
1438    with_input_handler(this, |input_handler| {
1439        input_handler.bounds_for_range(range.to_range()?)
1440    })
1441    .flatten()
1442    .map_or(
1443        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1444        |bounds| {
1445            NSRect::new(
1446                NSPoint::new(
1447                    frame.origin.x + bounds.origin.x as f64,
1448                    frame.origin.y + frame.size.height - bounds.origin.y as f64,
1449                ),
1450                NSSize::new(bounds.size.width as f64, bounds.size.height as f64),
1451            )
1452        },
1453    )
1454}
1455
1456extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1457    unsafe {
1458        let window_state = get_window_state(this);
1459        let mut lock = window_state.lock();
1460        let pending_key_down = lock.pending_key_down.take();
1461        drop(lock);
1462
1463        let is_attributed_string: BOOL =
1464            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1465        let text: id = if is_attributed_string == YES {
1466            msg_send![text, string]
1467        } else {
1468            text
1469        };
1470        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1471            .to_str()
1472            .unwrap();
1473        let replacement_range = replacement_range.to_range();
1474
1475        window_state.lock().ime_text = Some(text.to_string());
1476        window_state.lock().ime_state = ImeState::Acted;
1477
1478        let is_composing =
1479            with_input_handler(this, |input_handler| input_handler.marked_text_range())
1480                .flatten()
1481                .is_some();
1482
1483        if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
1484            with_input_handler(this, |input_handler| {
1485                input_handler.replace_text_in_range(replacement_range, text)
1486            });
1487        } else {
1488            let mut pending_key_down = pending_key_down.unwrap();
1489            pending_key_down.1 = Some(InsertText {
1490                replacement_range,
1491                text: text.to_string(),
1492            });
1493            window_state.lock().pending_key_down = Some(pending_key_down);
1494        }
1495    }
1496}
1497
1498extern "C" fn set_marked_text(
1499    this: &Object,
1500    _: Sel,
1501    text: id,
1502    selected_range: NSRange,
1503    replacement_range: NSRange,
1504) {
1505    unsafe {
1506        let window_state = get_window_state(this);
1507        window_state.lock().pending_key_down.take();
1508
1509        let is_attributed_string: BOOL =
1510            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1511        let text: id = if is_attributed_string == YES {
1512            msg_send![text, string]
1513        } else {
1514            text
1515        };
1516        let selected_range = selected_range.to_range();
1517        let replacement_range = replacement_range.to_range();
1518        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1519            .to_str()
1520            .unwrap();
1521
1522        window_state.lock().ime_state = ImeState::Acted;
1523        window_state.lock().ime_text = Some(text.to_string());
1524
1525        with_input_handler(this, |input_handler| {
1526            input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
1527        });
1528    }
1529}
1530
1531extern "C" fn unmark_text(this: &Object, _: Sel) {
1532    unsafe {
1533        let state = get_window_state(this);
1534        let mut borrow = state.lock();
1535        borrow.ime_state = ImeState::Acted;
1536        borrow.ime_text.take();
1537    }
1538
1539    with_input_handler(this, |input_handler| input_handler.unmark_text());
1540}
1541
1542extern "C" fn attributed_substring_for_proposed_range(
1543    this: &Object,
1544    _: Sel,
1545    range: NSRange,
1546    _actual_range: *mut c_void,
1547) -> id {
1548    with_input_handler(this, |input_handler| {
1549        let range = range.to_range()?;
1550        if range.is_empty() {
1551            return None;
1552        }
1553
1554        let selected_text = input_handler.text_for_range(range)?;
1555        unsafe {
1556            let string: id = msg_send![class!(NSAttributedString), alloc];
1557            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1558            Some(string)
1559        }
1560    })
1561    .flatten()
1562    .unwrap_or(nil)
1563}
1564
1565extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1566    unsafe {
1567        let state = get_window_state(this);
1568        let mut borrow = state.lock();
1569        borrow.ime_state = ImeState::Continue;
1570        borrow.ime_text.take();
1571    }
1572}
1573
1574extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1575    unsafe {
1576        let state = get_window_state(this);
1577        let mut lock = state.as_ref().lock();
1578        if let Some(mut callback) = lock.appearance_changed_callback.take() {
1579            drop(lock);
1580            callback();
1581            state.lock().appearance_changed_callback = Some(callback);
1582        }
1583    }
1584}
1585
1586extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1587    unsafe {
1588        let state = get_window_state(this);
1589        let lock = state.as_ref().lock();
1590        return if lock.kind == WindowKind::PopUp {
1591            YES
1592        } else {
1593            NO
1594        };
1595    }
1596}
1597
1598async fn synthetic_drag(
1599    window_state: Weak<Mutex<MacWindowState>>,
1600    drag_id: usize,
1601    event: MouseMoveEvent,
1602) {
1603    loop {
1604        Timer::after(Duration::from_millis(16)).await;
1605        if let Some(window_state) = window_state.upgrade() {
1606            let mut lock = window_state.lock();
1607            if lock.synthetic_drag_counter == drag_id {
1608                if let Some(mut callback) = lock.event_callback.take() {
1609                    drop(lock);
1610                    callback(InputEvent::MouseMoved(event.clone()));
1611                    window_state.lock().event_callback = Some(callback);
1612                }
1613            } else {
1614                break;
1615            }
1616        }
1617    }
1618}
1619
1620fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
1621where
1622    F: FnOnce(&mut dyn PlatformInputHandler) -> R,
1623{
1624    let window_state = unsafe { get_window_state(window) };
1625    let mut lock = window_state.as_ref().lock();
1626    if let Some(mut input_handler) = lock.input_handler.take() {
1627        drop(lock);
1628        let result = f(input_handler.as_mut());
1629        window_state.lock().input_handler = Some(input_handler);
1630        Some(result)
1631    } else {
1632        None
1633    }
1634}