window.rs

   1use super::{ns_string, renderer, MacDisplay, NSRange};
   2use crate::{
   3    platform::PlatformInputHandler, point, px, size, AnyWindowHandle, Bounds, DevicePixels,
   4    DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke,
   5    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
   6    Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,
   7    Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowKind, WindowParams,
   8};
   9use block::ConcreteBlock;
  10use cocoa::{
  11    appkit::{
  12        CGPoint, NSApplication, NSBackingStoreBuffered, NSColor, NSEvent, NSEventModifierFlags,
  13        NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,
  14        NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
  15        NSWindowOcclusionState, NSWindowStyleMask, NSWindowTitleVisibility,
  16    },
  17    base::{id, nil},
  18    foundation::{
  19        NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSPoint, NSRect,
  20        NSSize, NSString, NSUInteger,
  21    },
  22};
  23use core_graphics::display::{CGDirectDisplayID, CGRect};
  24use ctor::ctor;
  25use futures::channel::oneshot;
  26use objc::{
  27    class,
  28    declare::ClassDecl,
  29    msg_send,
  30    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
  31    sel, sel_impl,
  32};
  33use parking_lot::Mutex;
  34use raw_window_handle as rwh;
  35use smallvec::SmallVec;
  36use std::{
  37    cell::Cell,
  38    ffi::{c_void, CStr},
  39    mem,
  40    ops::Range,
  41    os::raw::c_char,
  42    path::PathBuf,
  43    ptr::{self, NonNull},
  44    rc::Rc,
  45    sync::{Arc, Weak},
  46    time::Duration,
  47};
  48use util::ResultExt;
  49
  50const WINDOW_STATE_IVAR: &str = "windowState";
  51
  52static mut WINDOW_CLASS: *const Class = ptr::null();
  53static mut PANEL_CLASS: *const Class = ptr::null();
  54static mut VIEW_CLASS: *const Class = ptr::null();
  55
  56#[allow(non_upper_case_globals)]
  57const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =
  58    unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) };
  59#[allow(non_upper_case_globals)]
  60const NSNormalWindowLevel: NSInteger = 0;
  61#[allow(non_upper_case_globals)]
  62const NSPopUpWindowLevel: NSInteger = 101;
  63#[allow(non_upper_case_globals)]
  64const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;
  65#[allow(non_upper_case_globals)]
  66const NSTrackingMouseMoved: NSUInteger = 0x02;
  67#[allow(non_upper_case_globals)]
  68const NSTrackingActiveAlways: NSUInteger = 0x80;
  69#[allow(non_upper_case_globals)]
  70const NSTrackingInVisibleRect: NSUInteger = 0x200;
  71#[allow(non_upper_case_globals)]
  72const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
  73#[allow(non_upper_case_globals)]
  74const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
  75// https://developer.apple.com/documentation/appkit/nsdragoperation
  76type NSDragOperation = NSUInteger;
  77#[allow(non_upper_case_globals)]
  78const NSDragOperationNone: NSDragOperation = 0;
  79#[allow(non_upper_case_globals)]
  80const NSDragOperationCopy: NSDragOperation = 1;
  81
  82#[link(name = "CoreGraphics", kind = "framework")]
  83extern "C" {
  84    // Widely used private APIs; Apple uses them for their Terminal.app.
  85    fn CGSMainConnectionID() -> id;
  86    fn CGSSetWindowBackgroundBlurRadius(
  87        connection_id: id,
  88        window_id: NSInteger,
  89        radius: i64,
  90    ) -> i32;
  91}
  92
  93#[ctor]
  94unsafe fn build_classes() {
  95    WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
  96    PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));
  97    VIEW_CLASS = {
  98        let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
  99        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 100
 101        decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
 102
 103        decl.add_method(
 104            sel!(performKeyEquivalent:),
 105            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
 106        );
 107        decl.add_method(
 108            sel!(keyDown:),
 109            handle_key_down as extern "C" fn(&Object, Sel, id),
 110        );
 111        decl.add_method(
 112            sel!(mouseDown:),
 113            handle_view_event as extern "C" fn(&Object, Sel, id),
 114        );
 115        decl.add_method(
 116            sel!(mouseUp:),
 117            handle_view_event as extern "C" fn(&Object, Sel, id),
 118        );
 119        decl.add_method(
 120            sel!(rightMouseDown:),
 121            handle_view_event as extern "C" fn(&Object, Sel, id),
 122        );
 123        decl.add_method(
 124            sel!(rightMouseUp:),
 125            handle_view_event as extern "C" fn(&Object, Sel, id),
 126        );
 127        decl.add_method(
 128            sel!(otherMouseDown:),
 129            handle_view_event as extern "C" fn(&Object, Sel, id),
 130        );
 131        decl.add_method(
 132            sel!(otherMouseUp:),
 133            handle_view_event as extern "C" fn(&Object, Sel, id),
 134        );
 135        decl.add_method(
 136            sel!(mouseMoved:),
 137            handle_view_event as extern "C" fn(&Object, Sel, id),
 138        );
 139        decl.add_method(
 140            sel!(mouseExited:),
 141            handle_view_event as extern "C" fn(&Object, Sel, id),
 142        );
 143        decl.add_method(
 144            sel!(mouseDragged:),
 145            handle_view_event as extern "C" fn(&Object, Sel, id),
 146        );
 147        decl.add_method(
 148            sel!(scrollWheel:),
 149            handle_view_event as extern "C" fn(&Object, Sel, id),
 150        );
 151        decl.add_method(
 152            sel!(flagsChanged:),
 153            handle_view_event as extern "C" fn(&Object, Sel, id),
 154        );
 155        decl.add_method(
 156            sel!(cancelOperation:),
 157            cancel_operation as extern "C" fn(&Object, Sel, id),
 158        );
 159
 160        decl.add_method(
 161            sel!(makeBackingLayer),
 162            make_backing_layer as extern "C" fn(&Object, Sel) -> id,
 163        );
 164
 165        decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
 166        decl.add_method(
 167            sel!(viewDidChangeBackingProperties),
 168            view_did_change_backing_properties as extern "C" fn(&Object, Sel),
 169        );
 170        decl.add_method(
 171            sel!(setFrameSize:),
 172            set_frame_size as extern "C" fn(&Object, Sel, NSSize),
 173        );
 174        decl.add_method(
 175            sel!(displayLayer:),
 176            display_layer as extern "C" fn(&Object, Sel, id),
 177        );
 178
 179        decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
 180        decl.add_method(
 181            sel!(validAttributesForMarkedText),
 182            valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
 183        );
 184        decl.add_method(
 185            sel!(hasMarkedText),
 186            has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
 187        );
 188        decl.add_method(
 189            sel!(markedRange),
 190            marked_range as extern "C" fn(&Object, Sel) -> NSRange,
 191        );
 192        decl.add_method(
 193            sel!(selectedRange),
 194            selected_range as extern "C" fn(&Object, Sel) -> NSRange,
 195        );
 196        decl.add_method(
 197            sel!(firstRectForCharacterRange:actualRange:),
 198            first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
 199        );
 200        decl.add_method(
 201            sel!(insertText:replacementRange:),
 202            insert_text as extern "C" fn(&Object, Sel, id, NSRange),
 203        );
 204        decl.add_method(
 205            sel!(setMarkedText:selectedRange:replacementRange:),
 206            set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
 207        );
 208        decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
 209        decl.add_method(
 210            sel!(attributedSubstringForProposedRange:actualRange:),
 211            attributed_substring_for_proposed_range
 212                as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
 213        );
 214        decl.add_method(
 215            sel!(viewDidChangeEffectiveAppearance),
 216            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
 217        );
 218
 219        // Suppress beep on keystrokes with modifier keys.
 220        decl.add_method(
 221            sel!(doCommandBySelector:),
 222            do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
 223        );
 224
 225        decl.add_method(
 226            sel!(acceptsFirstMouse:),
 227            accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
 228        );
 229
 230        decl.register()
 231    };
 232}
 233
 234pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
 235    point(
 236        px(position.x as f32),
 237        // MacOS screen coordinates are relative to bottom left
 238        window_height - px(position.y as f32),
 239    )
 240}
 241
 242unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
 243    let mut decl = ClassDecl::new(name, superclass).unwrap();
 244    decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 245    decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
 246    decl.add_method(
 247        sel!(canBecomeMainWindow),
 248        yes as extern "C" fn(&Object, Sel) -> BOOL,
 249    );
 250    decl.add_method(
 251        sel!(canBecomeKeyWindow),
 252        yes as extern "C" fn(&Object, Sel) -> BOOL,
 253    );
 254    decl.add_method(
 255        sel!(windowDidResize:),
 256        window_did_resize as extern "C" fn(&Object, Sel, id),
 257    );
 258    decl.add_method(
 259        sel!(windowDidChangeOcclusionState:),
 260        window_did_change_occlusion_state as extern "C" fn(&Object, Sel, id),
 261    );
 262    decl.add_method(
 263        sel!(windowDidMove:),
 264        window_did_move as extern "C" fn(&Object, Sel, id),
 265    );
 266    decl.add_method(
 267        sel!(windowDidChangeScreen:),
 268        window_did_change_screen as extern "C" fn(&Object, Sel, id),
 269    );
 270    decl.add_method(
 271        sel!(windowDidBecomeKey:),
 272        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 273    );
 274    decl.add_method(
 275        sel!(windowDidResignKey:),
 276        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 277    );
 278    decl.add_method(
 279        sel!(windowShouldClose:),
 280        window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
 281    );
 282
 283    decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
 284
 285    decl.add_method(
 286        sel!(draggingEntered:),
 287        dragging_entered as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
 288    );
 289    decl.add_method(
 290        sel!(draggingUpdated:),
 291        dragging_updated as extern "C" fn(&Object, Sel, id) -> NSDragOperation,
 292    );
 293    decl.add_method(
 294        sel!(draggingExited:),
 295        dragging_exited as extern "C" fn(&Object, Sel, id),
 296    );
 297    decl.add_method(
 298        sel!(performDragOperation:),
 299        perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
 300    );
 301    decl.add_method(
 302        sel!(concludeDragOperation:),
 303        conclude_drag_operation as extern "C" fn(&Object, Sel, id),
 304    );
 305    decl.add_method(
 306        sel!(windowDidMiniaturize:),
 307        window_did_miniaturize as extern "C" fn(&Object, Sel, id),
 308    );
 309    decl.add_method(
 310        sel!(windowDidDeminiaturize:),
 311        window_did_deminiaturize as extern "C" fn(&Object, Sel, id),
 312    );
 313
 314    decl.register()
 315}
 316
 317#[allow(clippy::enum_variant_names)]
 318enum ImeInput {
 319    InsertText(String, Option<Range<usize>>),
 320    SetMarkedText(String, Option<Range<usize>>, Option<Range<usize>>),
 321    UnmarkText,
 322}
 323
 324struct MacWindowState {
 325    handle: AnyWindowHandle,
 326    executor: ForegroundExecutor,
 327    native_window: id,
 328    native_view: NonNull<Object>,
 329    display_link: Option<DisplayLink>,
 330    renderer: renderer::Renderer,
 331    request_frame_callback: Option<Box<dyn FnMut()>>,
 332    event_callback: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
 333    activate_callback: Option<Box<dyn FnMut(bool)>>,
 334    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
 335    moved_callback: Option<Box<dyn FnMut()>>,
 336    should_close_callback: Option<Box<dyn FnMut() -> bool>>,
 337    close_callback: Option<Box<dyn FnOnce()>>,
 338    appearance_changed_callback: Option<Box<dyn FnMut()>>,
 339    input_handler: Option<PlatformInputHandler>,
 340    last_key_equivalent: Option<KeyDownEvent>,
 341    synthetic_drag_counter: usize,
 342    last_fresh_keydown: Option<Keystroke>,
 343    traffic_light_position: Option<Point<Pixels>>,
 344    previous_modifiers_changed_event: Option<PlatformInput>,
 345    // State tracking what the IME did after the last request
 346    input_during_keydown: Option<SmallVec<[ImeInput; 1]>>,
 347    previous_keydown_inserted_text: Option<String>,
 348    external_files_dragged: bool,
 349    // Whether the next left-mouse click is also the focusing click.
 350    first_mouse: bool,
 351    minimized: bool,
 352}
 353
 354impl MacWindowState {
 355    fn move_traffic_light(&self) {
 356        if let Some(traffic_light_position) = self.traffic_light_position {
 357            if self.is_fullscreen() {
 358                // Moving traffic lights while fullscreen doesn't work,
 359                // see https://github.com/zed-industries/zed/issues/4712
 360                return;
 361            }
 362
 363            let titlebar_height = self.titlebar_height();
 364
 365            unsafe {
 366                let close_button: id = msg_send![
 367                    self.native_window,
 368                    standardWindowButton: NSWindowButton::NSWindowCloseButton
 369                ];
 370                let min_button: id = msg_send![
 371                    self.native_window,
 372                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
 373                ];
 374                let zoom_button: id = msg_send![
 375                    self.native_window,
 376                    standardWindowButton: NSWindowButton::NSWindowZoomButton
 377                ];
 378
 379                let mut close_button_frame: CGRect = msg_send![close_button, frame];
 380                let mut min_button_frame: CGRect = msg_send![min_button, frame];
 381                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
 382                let mut origin = point(
 383                    traffic_light_position.x,
 384                    titlebar_height
 385                        - traffic_light_position.y
 386                        - px(close_button_frame.size.height as f32),
 387                );
 388                let button_spacing =
 389                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
 390
 391                close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 392                let _: () = msg_send![close_button, setFrame: close_button_frame];
 393                origin.x += button_spacing;
 394
 395                min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 396                let _: () = msg_send![min_button, setFrame: min_button_frame];
 397                origin.x += button_spacing;
 398
 399                zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 400                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
 401                origin.x += button_spacing;
 402            }
 403        }
 404    }
 405
 406    fn start_display_link(&mut self) {
 407        self.stop_display_link();
 408        unsafe {
 409            if !self
 410                .native_window
 411                .occlusionState()
 412                .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible)
 413            {
 414                return;
 415            }
 416        }
 417        let display_id = unsafe { display_id_for_screen(self.native_window.screen()) };
 418        if let Some(mut display_link) =
 419            DisplayLink::new(display_id, self.native_view.as_ptr() as *mut c_void, step).log_err()
 420        {
 421            display_link.start().log_err();
 422            self.display_link = Some(display_link);
 423        }
 424    }
 425
 426    fn stop_display_link(&mut self) {
 427        self.display_link = None;
 428    }
 429
 430    fn is_maximized(&self) -> bool {
 431        unsafe {
 432            let bounds = self.bounds();
 433            let screen_size = self.native_window.screen().visibleFrame().into();
 434            bounds.size == screen_size
 435        }
 436    }
 437
 438    fn is_minimized(&self) -> bool {
 439        self.minimized
 440    }
 441
 442    fn is_fullscreen(&self) -> bool {
 443        unsafe {
 444            let style_mask = self.native_window.styleMask();
 445            style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
 446        }
 447    }
 448
 449    fn bounds(&self) -> Bounds<DevicePixels> {
 450        let mut window_frame = unsafe { NSWindow::frame(self.native_window) };
 451        let screen_frame = unsafe {
 452            let screen = NSWindow::screen(self.native_window);
 453            NSScreen::frame(screen)
 454        };
 455
 456        // Flip the y coordinate to be top-left origin
 457        window_frame.origin.y =
 458            screen_frame.size.height - window_frame.origin.y - window_frame.size.height;
 459
 460        let bounds = Bounds::new(
 461            point(
 462                ((window_frame.origin.x - screen_frame.origin.x) as i32).into(),
 463                ((window_frame.origin.y - screen_frame.origin.y) as i32).into(),
 464            ),
 465            size(
 466                (window_frame.size.width as i32).into(),
 467                (window_frame.size.height as i32).into(),
 468            ),
 469        );
 470        bounds
 471    }
 472
 473    fn content_size(&self) -> Size<Pixels> {
 474        let NSSize { width, height, .. } =
 475            unsafe { NSView::frame(self.native_window.contentView()) }.size;
 476        size(px(width as f32), px(height as f32))
 477    }
 478
 479    fn scale_factor(&self) -> f32 {
 480        get_scale_factor(self.native_window)
 481    }
 482
 483    fn update_drawable_size(&mut self, drawable_size: NSSize) {
 484        self.renderer.update_drawable_size(Size {
 485            width: drawable_size.width,
 486            height: drawable_size.height,
 487        })
 488    }
 489
 490    fn titlebar_height(&self) -> Pixels {
 491        unsafe {
 492            let frame = NSWindow::frame(self.native_window);
 493            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
 494            px((frame.size.height - content_layout_rect.size.height) as f32)
 495        }
 496    }
 497}
 498
 499unsafe impl Send for MacWindowState {}
 500
 501pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>);
 502
 503impl MacWindow {
 504    pub fn open(
 505        handle: AnyWindowHandle,
 506        WindowParams {
 507            window_background,
 508            bounds,
 509            titlebar,
 510            kind,
 511            is_movable,
 512            focus,
 513            show,
 514            display_id,
 515        }: WindowParams,
 516        executor: ForegroundExecutor,
 517        renderer_context: renderer::Context,
 518    ) -> Self {
 519        unsafe {
 520            let pool = NSAutoreleasePool::new(nil);
 521
 522            let mut style_mask;
 523            if let Some(titlebar) = titlebar.as_ref() {
 524                style_mask = NSWindowStyleMask::NSClosableWindowMask
 525                    | NSWindowStyleMask::NSMiniaturizableWindowMask
 526                    | NSWindowStyleMask::NSResizableWindowMask
 527                    | NSWindowStyleMask::NSTitledWindowMask;
 528
 529                if titlebar.appears_transparent {
 530                    style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 531                }
 532            } else {
 533                style_mask = NSWindowStyleMask::NSTitledWindowMask
 534                    | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 535            }
 536
 537            let native_window: id = match kind {
 538                WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
 539                WindowKind::PopUp => {
 540                    style_mask |= NSWindowStyleMaskNonactivatingPanel;
 541                    msg_send![PANEL_CLASS, alloc]
 542                }
 543            };
 544
 545            let display = display_id
 546                .and_then(MacDisplay::find_by_id)
 547                .unwrap_or_else(|| MacDisplay::primary());
 548
 549            let mut target_screen = nil;
 550            let mut screen_frame = None;
 551
 552            let screens = NSScreen::screens(nil);
 553            let count: u64 = cocoa::foundation::NSArray::count(screens);
 554            for i in 0..count {
 555                let screen = cocoa::foundation::NSArray::objectAtIndex(screens, i);
 556                let frame = NSScreen::visibleFrame(screen);
 557                let display_id = display_id_for_screen(screen);
 558                if display_id == display.0 {
 559                    screen_frame = Some(frame);
 560                    target_screen = screen;
 561                }
 562            }
 563
 564            let screen_frame = screen_frame.unwrap_or_else(|| {
 565                let screen = NSScreen::mainScreen(nil);
 566                target_screen = screen;
 567                NSScreen::visibleFrame(screen)
 568            });
 569
 570            let window_rect = NSRect::new(
 571                NSPoint::new(
 572                    screen_frame.origin.x + bounds.origin.x.0 as f64,
 573                    screen_frame.origin.y
 574                        + (display.bounds().size.height - bounds.origin.y).0 as f64,
 575                ),
 576                NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
 577            );
 578
 579            let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
 580                window_rect,
 581                style_mask,
 582                NSBackingStoreBuffered,
 583                NO,
 584                target_screen,
 585            );
 586            assert!(!native_window.is_null());
 587            let () = msg_send![
 588                native_window,
 589                registerForDraggedTypes:
 590                    NSArray::arrayWithObject(nil, NSFilenamesPboardType)
 591            ];
 592            let () = msg_send![
 593                native_window,
 594                setReleasedWhenClosed: NO
 595            ];
 596
 597            let native_view: id = msg_send![VIEW_CLASS, alloc];
 598            let native_view = NSView::init(native_view);
 599            assert!(!native_view.is_null());
 600
 601            let window_size = {
 602                let scale = get_scale_factor(native_window);
 603                size(
 604                    bounds.size.width.0 as f32 * scale,
 605                    bounds.size.height.0 as f32 * scale,
 606                )
 607            };
 608
 609            let mut window = Self(Arc::new(Mutex::new(MacWindowState {
 610                handle,
 611                executor,
 612                native_window,
 613                native_view: NonNull::new_unchecked(native_view),
 614                display_link: None,
 615                renderer: renderer::new_renderer(
 616                    renderer_context,
 617                    native_window as *mut _,
 618                    native_view as *mut _,
 619                    window_size,
 620                    window_background != WindowBackgroundAppearance::Opaque,
 621                ),
 622                request_frame_callback: None,
 623                event_callback: None,
 624                activate_callback: None,
 625                resize_callback: None,
 626                moved_callback: None,
 627                should_close_callback: None,
 628                close_callback: None,
 629                appearance_changed_callback: None,
 630                input_handler: None,
 631                last_key_equivalent: None,
 632                synthetic_drag_counter: 0,
 633                last_fresh_keydown: None,
 634                traffic_light_position: titlebar
 635                    .as_ref()
 636                    .and_then(|titlebar| titlebar.traffic_light_position),
 637                previous_modifiers_changed_event: None,
 638                input_during_keydown: None,
 639                previous_keydown_inserted_text: None,
 640                external_files_dragged: false,
 641                first_mouse: false,
 642                minimized: false,
 643            })));
 644
 645            (*native_window).set_ivar(
 646                WINDOW_STATE_IVAR,
 647                Arc::into_raw(window.0.clone()) as *const c_void,
 648            );
 649            native_window.setDelegate_(native_window);
 650            (*native_view).set_ivar(
 651                WINDOW_STATE_IVAR,
 652                Arc::into_raw(window.0.clone()) as *const c_void,
 653            );
 654
 655            if let Some(title) = titlebar
 656                .as_ref()
 657                .and_then(|t| t.title.as_ref().map(AsRef::as_ref))
 658            {
 659                native_window.setTitle_(NSString::alloc(nil).init_str(title));
 660            }
 661
 662            native_window.setMovable_(is_movable as BOOL);
 663
 664            if titlebar.map_or(true, |titlebar| titlebar.appears_transparent) {
 665                native_window.setTitlebarAppearsTransparent_(YES);
 666                native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
 667            }
 668
 669            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
 670            native_view.setWantsBestResolutionOpenGLSurface_(YES);
 671
 672            // From winit crate: On Mojave, views automatically become layer-backed shortly after
 673            // being added to a native_window. Changing the layer-backedness of a view breaks the
 674            // association between the view and its associated OpenGL context. To work around this,
 675            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
 676            // itself and break the association with its context.
 677            native_view.setWantsLayer(YES);
 678            let _: () = msg_send![
 679                native_view,
 680                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
 681            ];
 682
 683            native_window.setContentView_(native_view.autorelease());
 684            native_window.makeFirstResponder_(native_view);
 685
 686            window.set_background_appearance(window_background);
 687
 688            match kind {
 689                WindowKind::Normal => {
 690                    native_window.setLevel_(NSNormalWindowLevel);
 691                    native_window.setAcceptsMouseMovedEvents_(YES);
 692                }
 693                WindowKind::PopUp => {
 694                    // Use a tracking area to allow receiving MouseMoved events even when
 695                    // the window or application aren't active, which is often the case
 696                    // e.g. for notification windows.
 697                    let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
 698                    let _: () = msg_send![
 699                        tracking_area,
 700                        initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
 701                        options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
 702                        owner: native_view
 703                        userInfo: nil
 704                    ];
 705                    let _: () =
 706                        msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
 707
 708                    native_window.setLevel_(NSPopUpWindowLevel);
 709                    let _: () = msg_send![
 710                        native_window,
 711                        setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
 712                    ];
 713                    native_window.setCollectionBehavior_(
 714                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
 715                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
 716                    );
 717                }
 718            }
 719
 720            if focus {
 721                native_window.makeKeyAndOrderFront_(nil);
 722            } else if show {
 723                native_window.orderFront_(nil);
 724            }
 725
 726            // Set the initial position of the window to the specified origin.
 727            // Although we already specified the position using `initWithContentRect_styleMask_backing_defer_screen_`,
 728            // the window position might be incorrect if the main screen (the screen that contains the window that has focus)
 729            //  is different from the primary screen.
 730            NSWindow::setFrameTopLeftPoint_(native_window, window_rect.origin);
 731            window.0.lock().move_traffic_light();
 732
 733            pool.drain();
 734
 735            window
 736        }
 737    }
 738
 739    pub fn active_window() -> Option<AnyWindowHandle> {
 740        unsafe {
 741            let app = NSApplication::sharedApplication(nil);
 742            let main_window: id = msg_send![app, mainWindow];
 743            if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
 744                let handle = get_window_state(&*main_window).lock().handle;
 745                Some(handle)
 746            } else {
 747                None
 748            }
 749        }
 750    }
 751}
 752
 753impl Drop for MacWindow {
 754    fn drop(&mut self) {
 755        let mut this = self.0.lock();
 756        this.renderer.destroy();
 757        let window = this.native_window;
 758        this.display_link.take();
 759        unsafe {
 760            this.native_window.setDelegate_(nil);
 761        }
 762        this.executor
 763            .spawn(async move {
 764                unsafe {
 765                    window.close();
 766                    window.autorelease();
 767                }
 768            })
 769            .detach();
 770    }
 771}
 772
 773impl PlatformWindow for MacWindow {
 774    fn bounds(&self) -> Bounds<DevicePixels> {
 775        self.0.as_ref().lock().bounds()
 776    }
 777
 778    fn is_maximized(&self) -> bool {
 779        self.0.as_ref().lock().is_maximized()
 780    }
 781
 782    fn is_minimized(&self) -> bool {
 783        self.0.as_ref().lock().is_minimized()
 784    }
 785
 786    fn content_size(&self) -> Size<Pixels> {
 787        self.0.as_ref().lock().content_size()
 788    }
 789
 790    fn scale_factor(&self) -> f32 {
 791        self.0.as_ref().lock().scale_factor()
 792    }
 793
 794    fn appearance(&self) -> WindowAppearance {
 795        unsafe {
 796            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
 797            WindowAppearance::from_native(appearance)
 798        }
 799    }
 800
 801    fn display(&self) -> Rc<dyn PlatformDisplay> {
 802        unsafe {
 803            let screen = self.0.lock().native_window.screen();
 804            let device_description: id = msg_send![screen, deviceDescription];
 805            let screen_number: id = NSDictionary::valueForKey_(
 806                device_description,
 807                NSString::alloc(nil).init_str("NSScreenNumber"),
 808            );
 809
 810            let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
 811
 812            Rc::new(MacDisplay(screen_number))
 813        }
 814    }
 815
 816    fn mouse_position(&self) -> Point<Pixels> {
 817        let position = unsafe {
 818            self.0
 819                .lock()
 820                .native_window
 821                .mouseLocationOutsideOfEventStream()
 822        };
 823        convert_mouse_position(position, self.content_size().height)
 824    }
 825
 826    fn modifiers(&self) -> Modifiers {
 827        unsafe {
 828            let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
 829
 830            let control = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
 831            let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
 832            let shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
 833            let command = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
 834            let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask);
 835
 836            Modifiers {
 837                control,
 838                alt,
 839                shift,
 840                platform: command,
 841                function,
 842            }
 843        }
 844    }
 845
 846    fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
 847        self.0.as_ref().lock().input_handler = Some(input_handler);
 848    }
 849
 850    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
 851        self.0.as_ref().lock().input_handler.take()
 852    }
 853
 854    fn prompt(
 855        &self,
 856        level: PromptLevel,
 857        msg: &str,
 858        detail: Option<&str>,
 859        answers: &[&str],
 860    ) -> Option<oneshot::Receiver<usize>> {
 861        // macOs applies overrides to modal window buttons after they are added.
 862        // Two most important for this logic are:
 863        // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
 864        // * Last button added to the modal via `addButtonWithTitle` stays focused
 865        // * Focused buttons react on "space"/" " keypresses
 866        // * Usage of `keyEquivalent`, `makeFirstResponder` or `setInitialFirstResponder` does not change the focus
 867        //
 868        // See also https://developer.apple.com/documentation/appkit/nsalert/1524532-addbuttonwithtitle#discussion
 869        // ```
 870        // By default, the first button has a key equivalent of Return,
 871        // any button with a title of “Cancel” has a key equivalent of Escape,
 872        // 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).
 873        // ```
 874        //
 875        // To avoid situations when the last element added is "Cancel" and it gets the focus
 876        // (hence stealing both ESC and Space shortcuts), we find and add one non-Cancel button
 877        // last, so it gets focus and a Space shortcut.
 878        // This way, "Save this file? Yes/No/Cancel"-ish modals will get all three buttons mapped with a key.
 879        let latest_non_cancel_label = answers
 880            .iter()
 881            .enumerate()
 882            .rev()
 883            .find(|(_, &label)| label != "Cancel")
 884            .filter(|&(label_index, _)| label_index > 0);
 885
 886        unsafe {
 887            let alert: id = msg_send![class!(NSAlert), alloc];
 888            let alert: id = msg_send![alert, init];
 889            let alert_style = match level {
 890                PromptLevel::Info => 1,
 891                PromptLevel::Warning => 0,
 892                PromptLevel::Critical | PromptLevel::Destructive => 2,
 893            };
 894            let _: () = msg_send![alert, setAlertStyle: alert_style];
 895            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
 896            if let Some(detail) = detail {
 897                let _: () = msg_send![alert, setInformativeText: ns_string(detail)];
 898            }
 899
 900            for (ix, answer) in answers
 901                .iter()
 902                .enumerate()
 903                .filter(|&(ix, _)| Some(ix) != latest_non_cancel_label.map(|(ix, _)| ix))
 904            {
 905                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 906                let _: () = msg_send![button, setTag: ix as NSInteger];
 907                if level == PromptLevel::Destructive && answer != &"Cancel" {
 908                    let _: () = msg_send![button, setHasDestructiveAction: YES];
 909                }
 910            }
 911            if let Some((ix, answer)) = latest_non_cancel_label {
 912                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 913                let _: () = msg_send![button, setTag: ix as NSInteger];
 914                if level == PromptLevel::Destructive {
 915                    let _: () = msg_send![button, setHasDestructiveAction: YES];
 916                }
 917            }
 918
 919            let (done_tx, done_rx) = oneshot::channel();
 920            let done_tx = Cell::new(Some(done_tx));
 921            let block = ConcreteBlock::new(move |answer: NSInteger| {
 922                if let Some(done_tx) = done_tx.take() {
 923                    let _ = done_tx.send(answer.try_into().unwrap());
 924                }
 925            });
 926            let block = block.copy();
 927            let native_window = self.0.lock().native_window;
 928            let executor = self.0.lock().executor.clone();
 929            executor
 930                .spawn(async move {
 931                    let _: () = msg_send![
 932                        alert,
 933                        beginSheetModalForWindow: native_window
 934                        completionHandler: block
 935                    ];
 936                })
 937                .detach();
 938
 939            Some(done_rx)
 940        }
 941    }
 942
 943    fn activate(&self) {
 944        let window = self.0.lock().native_window;
 945        let executor = self.0.lock().executor.clone();
 946        executor
 947            .spawn(async move {
 948                unsafe {
 949                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
 950                }
 951            })
 952            .detach();
 953    }
 954
 955    fn is_active(&self) -> bool {
 956        unsafe { self.0.lock().native_window.isKeyWindow() == YES }
 957    }
 958
 959    fn set_title(&mut self, title: &str) {
 960        unsafe {
 961            let app = NSApplication::sharedApplication(nil);
 962            let window = self.0.lock().native_window;
 963            let title = ns_string(title);
 964            let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
 965            let _: () = msg_send![window, setTitle: title];
 966            self.0.lock().move_traffic_light();
 967        }
 968    }
 969
 970    fn set_app_id(&mut self, _app_id: &str) {}
 971
 972    fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
 973        let mut this = self.0.as_ref().lock();
 974        this.renderer
 975            .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
 976
 977        let blur_radius = if background_appearance == WindowBackgroundAppearance::Blurred {
 978            80
 979        } else {
 980            0
 981        };
 982        let opaque = if background_appearance == WindowBackgroundAppearance::Opaque {
 983            YES
 984        } else {
 985            NO
 986        };
 987        unsafe {
 988            this.native_window.setOpaque_(opaque);
 989            // Shadows for transparent windows cause artifacts and performance issues
 990            this.native_window.setHasShadow_(opaque);
 991            let clear_color = if opaque == YES {
 992                NSColor::colorWithSRGBRed_green_blue_alpha_(nil, 0f64, 0f64, 0f64, 1f64)
 993            } else {
 994                NSColor::clearColor(nil)
 995            };
 996            this.native_window.setBackgroundColor_(clear_color);
 997            let window_number = this.native_window.windowNumber();
 998            CGSSetWindowBackgroundBlurRadius(CGSMainConnectionID(), window_number, blur_radius);
 999        }
1000    }
1001
1002    fn set_edited(&mut self, edited: bool) {
1003        unsafe {
1004            let window = self.0.lock().native_window;
1005            msg_send![window, setDocumentEdited: edited as BOOL]
1006        }
1007
1008        // Changing the document edited state resets the traffic light position,
1009        // so we have to move it again.
1010        self.0.lock().move_traffic_light();
1011    }
1012
1013    fn show_character_palette(&self) {
1014        let this = self.0.lock();
1015        let window = this.native_window;
1016        this.executor
1017            .spawn(async move {
1018                unsafe {
1019                    let app = NSApplication::sharedApplication(nil);
1020                    let _: () = msg_send![app, orderFrontCharacterPalette: window];
1021                }
1022            })
1023            .detach();
1024    }
1025
1026    fn minimize(&self) {
1027        let window = self.0.lock().native_window;
1028        unsafe {
1029            window.miniaturize_(nil);
1030        }
1031    }
1032
1033    fn zoom(&self) {
1034        let this = self.0.lock();
1035        let window = this.native_window;
1036        this.executor
1037            .spawn(async move {
1038                unsafe {
1039                    window.zoom_(nil);
1040                }
1041            })
1042            .detach();
1043    }
1044
1045    fn toggle_fullscreen(&self) {
1046        let this = self.0.lock();
1047        let window = this.native_window;
1048        this.executor
1049            .spawn(async move {
1050                unsafe {
1051                    window.toggleFullScreen_(nil);
1052                }
1053            })
1054            .detach();
1055    }
1056
1057    fn is_fullscreen(&self) -> bool {
1058        let this = self.0.lock();
1059        let window = this.native_window;
1060
1061        unsafe {
1062            window
1063                .styleMask()
1064                .contains(NSWindowStyleMask::NSFullScreenWindowMask)
1065        }
1066    }
1067
1068    fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
1069        self.0.as_ref().lock().request_frame_callback = Some(callback);
1070    }
1071
1072    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
1073        self.0.as_ref().lock().event_callback = Some(callback);
1074    }
1075
1076    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1077        self.0.as_ref().lock().activate_callback = Some(callback);
1078    }
1079
1080    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1081        self.0.as_ref().lock().resize_callback = Some(callback);
1082    }
1083
1084    fn on_moved(&self, callback: Box<dyn FnMut()>) {
1085        self.0.as_ref().lock().moved_callback = Some(callback);
1086    }
1087
1088    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1089        self.0.as_ref().lock().should_close_callback = Some(callback);
1090    }
1091
1092    fn on_close(&self, callback: Box<dyn FnOnce()>) {
1093        self.0.as_ref().lock().close_callback = Some(callback);
1094    }
1095
1096    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1097        self.0.lock().appearance_changed_callback = Some(callback);
1098    }
1099
1100    fn draw(&self, scene: &crate::Scene) {
1101        let mut this = self.0.lock();
1102        this.renderer.draw(scene);
1103    }
1104
1105    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1106        self.0.lock().renderer.sprite_atlas().clone()
1107    }
1108}
1109
1110impl rwh::HasWindowHandle for MacWindow {
1111    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1112        // SAFETY: The AppKitWindowHandle is a wrapper around a pointer to an NSView
1113        unsafe {
1114            Ok(rwh::WindowHandle::borrow_raw(rwh::RawWindowHandle::AppKit(
1115                rwh::AppKitWindowHandle::new(self.0.lock().native_view.cast()),
1116            )))
1117        }
1118    }
1119}
1120
1121impl rwh::HasDisplayHandle for MacWindow {
1122    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1123        // SAFETY: This is a no-op on macOS
1124        unsafe {
1125            Ok(rwh::DisplayHandle::borrow_raw(
1126                rwh::AppKitDisplayHandle::new().into(),
1127            ))
1128        }
1129    }
1130}
1131
1132fn get_scale_factor(native_window: id) -> f32 {
1133    let factor = unsafe {
1134        let screen: id = msg_send![native_window, screen];
1135        NSScreen::backingScaleFactor(screen) as f32
1136    };
1137
1138    // We are not certain what triggers this, but it seems that sometimes
1139    // this method would return 0 (https://github.com/zed-industries/zed/issues/6412)
1140    // It seems most likely that this would happen if the window has no screen
1141    // (if it is off-screen), though we'd expect to see viewDidChangeBackingProperties before
1142    // it was rendered for real.
1143    // Regardless, attempt to avoid the issue here.
1144    if factor == 0.0 {
1145        2.
1146    } else {
1147        factor
1148    }
1149}
1150
1151unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
1152    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1153    let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1154    let rc2 = rc1.clone();
1155    mem::forget(rc1);
1156    rc2
1157}
1158
1159unsafe fn drop_window_state(object: &Object) {
1160    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1161    Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1162}
1163
1164extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
1165    YES
1166}
1167
1168extern "C" fn dealloc_window(this: &Object, _: Sel) {
1169    unsafe {
1170        drop_window_state(this);
1171        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
1172    }
1173}
1174
1175extern "C" fn dealloc_view(this: &Object, _: Sel) {
1176    unsafe {
1177        drop_window_state(this);
1178        let _: () = msg_send![super(this, class!(NSView)), dealloc];
1179    }
1180}
1181
1182extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
1183    handle_key_event(this, native_event, true)
1184}
1185
1186extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
1187    handle_key_event(this, native_event, false);
1188}
1189
1190// Things to test if you're modifying this method:
1191//  Brazilian layout:
1192//   - `" space` should type a quote
1193//   - `" backspace` should delete the marked quote
1194//   - `" up` should type the quote, unmark it, and move up one line
1195//   - `" cmd-down` should not leave a marked quote behind (it maybe should dispatch the key though?)
1196//   - `cmd-ctrl-space` and clicking on an emoji should type it
1197//  Czech (QWERTY) layout:
1198//   - in vim mode `option-4`  should go to end of line (same as $)
1199extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
1200    let window_state = unsafe { get_window_state(this) };
1201    let mut lock = window_state.as_ref().lock();
1202
1203    let window_height = lock.content_size().height;
1204    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1205
1206    if let Some(PlatformInput::KeyDown(mut event)) = event {
1207        // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
1208        // If that event isn't handled, it will then dispatch a "key down" event. GPUI
1209        // makes no distinction between these two types of events, so we need to ignore
1210        // the "key down" event if we've already just processed its "key equivalent" version.
1211        if key_equivalent {
1212            lock.last_key_equivalent = Some(event.clone());
1213        } else if lock.last_key_equivalent.take().as_ref() == Some(&event) {
1214            return NO;
1215        }
1216
1217        let keydown = event.keystroke.clone();
1218        let fn_modifier = keydown.modifiers.function;
1219        // Ignore events from held-down keys after some of the initially-pressed keys
1220        // were released.
1221        if event.is_held {
1222            if lock.last_fresh_keydown.as_ref() != Some(&keydown) {
1223                return YES;
1224            }
1225        } else {
1226            lock.last_fresh_keydown = Some(keydown.clone());
1227        }
1228        lock.input_during_keydown = Some(SmallVec::new());
1229        drop(lock);
1230
1231        // Send the event to the input context for IME handling, unless the `fn` modifier is
1232        // being pressed.
1233        // this will call back into `insert_text`, etc.
1234        if !fn_modifier {
1235            unsafe {
1236                let input_context: id = msg_send![this, inputContext];
1237                let _: BOOL = msg_send![input_context, handleEvent: native_event];
1238            }
1239        }
1240
1241        let mut handled = false;
1242        let mut lock = window_state.lock();
1243        let previous_keydown_inserted_text = lock.previous_keydown_inserted_text.take();
1244        let mut input_during_keydown = lock.input_during_keydown.take().unwrap();
1245        let mut callback = lock.event_callback.take();
1246        drop(lock);
1247
1248        let last_ime = input_during_keydown.pop();
1249        // on a brazilian keyboard typing `"` and then hitting `up` will cause two IME
1250        // events, one to unmark the quote, and one to send the up arrow.
1251        for ime in input_during_keydown {
1252            send_to_input_handler(this, ime);
1253        }
1254
1255        let is_composing =
1256            with_input_handler(this, |input_handler| input_handler.marked_text_range())
1257                .flatten()
1258                .is_some();
1259
1260        if let Some(ime) = last_ime {
1261            if let ImeInput::InsertText(text, _) = &ime {
1262                if !is_composing {
1263                    window_state.lock().previous_keydown_inserted_text = Some(text.clone());
1264                    if let Some(callback) = callback.as_mut() {
1265                        event.keystroke.ime_key = Some(text.clone());
1266                        handled = !callback(PlatformInput::KeyDown(event)).propagate;
1267                    }
1268                }
1269            }
1270
1271            if !handled {
1272                handled = true;
1273                send_to_input_handler(this, ime);
1274            }
1275        } else if !is_composing {
1276            let is_held = event.is_held;
1277
1278            if let Some(callback) = callback.as_mut() {
1279                handled = !callback(PlatformInput::KeyDown(event)).propagate;
1280            }
1281
1282            if !handled && is_held {
1283                if let Some(text) = previous_keydown_inserted_text {
1284                    // MacOS IME is a bit funky, and even when you've told it there's nothing to
1285                    // enter it will still swallow certain keys (e.g. 'f', 'j') and not others
1286                    // (e.g. 'n'). This is a problem for certain kinds of views, like the terminal.
1287                    with_input_handler(this, |input_handler| {
1288                        if input_handler.selected_text_range().is_none() {
1289                            handled = true;
1290                            input_handler.replace_text_in_range(None, &text)
1291                        }
1292                    });
1293                    window_state.lock().previous_keydown_inserted_text = Some(text);
1294                }
1295            }
1296        }
1297
1298        window_state.lock().event_callback = callback;
1299
1300        handled as BOOL
1301    } else {
1302        NO
1303    }
1304}
1305
1306extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
1307    let window_state = unsafe { get_window_state(this) };
1308    let weak_window_state = Arc::downgrade(&window_state);
1309    let mut lock = window_state.as_ref().lock();
1310    let window_height = lock.content_size().height;
1311    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1312
1313    if let Some(mut event) = event {
1314        match &mut event {
1315            PlatformInput::MouseDown(
1316                event @ MouseDownEvent {
1317                    button: MouseButton::Left,
1318                    modifiers: Modifiers { control: true, .. },
1319                    ..
1320                },
1321            ) => {
1322                // On mac, a ctrl-left click should be handled as a right click.
1323                *event = MouseDownEvent {
1324                    button: MouseButton::Right,
1325                    modifiers: Modifiers {
1326                        control: false,
1327                        ..event.modifiers
1328                    },
1329                    click_count: 1,
1330                    ..*event
1331                };
1332            }
1333
1334            // Handles focusing click.
1335            PlatformInput::MouseDown(
1336                event @ MouseDownEvent {
1337                    button: MouseButton::Left,
1338                    ..
1339                },
1340            ) if (lock.first_mouse) => {
1341                *event = MouseDownEvent {
1342                    first_mouse: true,
1343                    ..*event
1344                };
1345                lock.first_mouse = false;
1346            }
1347
1348            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
1349            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
1350            // user is still holding ctrl when releasing the left mouse button
1351            PlatformInput::MouseUp(
1352                event @ MouseUpEvent {
1353                    button: MouseButton::Left,
1354                    modifiers: Modifiers { control: true, .. },
1355                    ..
1356                },
1357            ) => {
1358                *event = MouseUpEvent {
1359                    button: MouseButton::Right,
1360                    modifiers: Modifiers {
1361                        control: false,
1362                        ..event.modifiers
1363                    },
1364                    click_count: 1,
1365                    ..*event
1366                };
1367            }
1368
1369            _ => {}
1370        };
1371
1372        match &event {
1373            PlatformInput::MouseMove(
1374                event @ MouseMoveEvent {
1375                    pressed_button: Some(_),
1376                    ..
1377                },
1378            ) => {
1379                // Synthetic drag is used for selecting long buffer contents while buffer is being scrolled.
1380                // External file drag and drop is able to emit its own synthetic mouse events which will conflict
1381                // with these ones.
1382                if !lock.external_files_dragged {
1383                    lock.synthetic_drag_counter += 1;
1384                    let executor = lock.executor.clone();
1385                    executor
1386                        .spawn(synthetic_drag(
1387                            weak_window_state,
1388                            lock.synthetic_drag_counter,
1389                            event.clone(),
1390                        ))
1391                        .detach();
1392                }
1393            }
1394
1395            PlatformInput::MouseUp(MouseUpEvent { .. }) => {
1396                lock.synthetic_drag_counter += 1;
1397            }
1398
1399            PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1400                // Only raise modifiers changed event when they have actually changed
1401                if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1402                    modifiers: prev_modifiers,
1403                })) = &lock.previous_modifiers_changed_event
1404                {
1405                    if prev_modifiers == modifiers {
1406                        return;
1407                    }
1408                }
1409
1410                lock.previous_modifiers_changed_event = Some(event.clone());
1411            }
1412
1413            _ => {}
1414        }
1415
1416        if let Some(mut callback) = lock.event_callback.take() {
1417            drop(lock);
1418            callback(event);
1419            window_state.lock().event_callback = Some(callback);
1420        }
1421    }
1422}
1423
1424// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
1425// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
1426extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
1427    let window_state = unsafe { get_window_state(this) };
1428    let mut lock = window_state.as_ref().lock();
1429
1430    let keystroke = Keystroke {
1431        modifiers: Default::default(),
1432        key: ".".into(),
1433        ime_key: None,
1434    };
1435    let event = PlatformInput::KeyDown(KeyDownEvent {
1436        keystroke: keystroke.clone(),
1437        is_held: false,
1438    });
1439
1440    lock.last_fresh_keydown = Some(keystroke);
1441    if let Some(mut callback) = lock.event_callback.take() {
1442        drop(lock);
1443        callback(event);
1444        window_state.lock().event_callback = Some(callback);
1445    }
1446}
1447
1448extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
1449    let window_state = unsafe { get_window_state(this) };
1450    let lock = &mut *window_state.lock();
1451    unsafe {
1452        if lock
1453            .native_window
1454            .occlusionState()
1455            .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible)
1456        {
1457            lock.start_display_link();
1458        } else {
1459            lock.stop_display_link();
1460        }
1461    }
1462}
1463
1464extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1465    let window_state = unsafe { get_window_state(this) };
1466    window_state.as_ref().lock().move_traffic_light();
1467}
1468
1469extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
1470    let window_state = unsafe { get_window_state(this) };
1471    let mut lock = window_state.as_ref().lock();
1472    if let Some(mut callback) = lock.moved_callback.take() {
1473        drop(lock);
1474        callback();
1475        window_state.lock().moved_callback = Some(callback);
1476    }
1477}
1478
1479extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
1480    let window_state = unsafe { get_window_state(this) };
1481    let mut lock = window_state.as_ref().lock();
1482    lock.start_display_link();
1483}
1484
1485extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1486    let window_state = unsafe { get_window_state(this) };
1487    let lock = window_state.lock();
1488    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
1489
1490    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1491    // `windowDidBecomeKey` message to the previous key window even though that window
1492    // isn't actually key. This causes a bug if the application is later activated while
1493    // the pop-up is still open, making it impossible to activate the previous key window
1494    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1495    // the app and re-activate it, which is a pretty bad UX.
1496    // The following code detects the spurious event and invokes `resignKeyWindow`:
1497    // in theory, we're not supposed to invoke this method manually but it balances out
1498    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1499    if selector == sel!(windowDidBecomeKey:) && !is_active {
1500        unsafe {
1501            let _: () = msg_send![lock.native_window, resignKeyWindow];
1502            return;
1503        }
1504    }
1505
1506    let executor = lock.executor.clone();
1507    drop(lock);
1508    executor
1509        .spawn(async move {
1510            let mut lock = window_state.as_ref().lock();
1511            if let Some(mut callback) = lock.activate_callback.take() {
1512                drop(lock);
1513                callback(is_active);
1514                window_state.lock().activate_callback = Some(callback);
1515            };
1516        })
1517        .detach();
1518}
1519
1520extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1521    let window_state = unsafe { get_window_state(this) };
1522    let mut lock = window_state.as_ref().lock();
1523    if let Some(mut callback) = lock.should_close_callback.take() {
1524        drop(lock);
1525        let should_close = callback();
1526        window_state.lock().should_close_callback = Some(callback);
1527        should_close as BOOL
1528    } else {
1529        YES
1530    }
1531}
1532
1533extern "C" fn close_window(this: &Object, _: Sel) {
1534    unsafe {
1535        let close_callback = {
1536            let window_state = get_window_state(this);
1537            let mut lock = window_state.as_ref().lock();
1538            lock.close_callback.take()
1539        };
1540
1541        if let Some(callback) = close_callback {
1542            callback();
1543        }
1544
1545        let _: () = msg_send![super(this, class!(NSWindow)), close];
1546    }
1547}
1548
1549extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1550    let window_state = unsafe { get_window_state(this) };
1551    let window_state = window_state.as_ref().lock();
1552    window_state.renderer.layer_ptr() as id
1553}
1554
1555extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1556    let window_state = unsafe { get_window_state(this) };
1557    let mut lock = window_state.as_ref().lock();
1558
1559    let scale_factor = lock.scale_factor() as f64;
1560    let size = lock.content_size();
1561    let drawable_size: NSSize = NSSize {
1562        width: f64::from(size.width) * scale_factor,
1563        height: f64::from(size.height) * scale_factor,
1564    };
1565    unsafe {
1566        let _: () = msg_send![
1567            lock.renderer.layer(),
1568            setContentsScale: scale_factor
1569        ];
1570    }
1571
1572    lock.update_drawable_size(drawable_size);
1573
1574    if let Some(mut callback) = lock.resize_callback.take() {
1575        let content_size = lock.content_size();
1576        let scale_factor = lock.scale_factor();
1577        drop(lock);
1578        callback(content_size, scale_factor);
1579        window_state.as_ref().lock().resize_callback = Some(callback);
1580    };
1581}
1582
1583extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1584    let window_state = unsafe { get_window_state(this) };
1585    let mut lock = window_state.as_ref().lock();
1586
1587    if lock.content_size() == size.into() {
1588        return;
1589    }
1590
1591    unsafe {
1592        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1593    }
1594
1595    let scale_factor = lock.scale_factor() as f64;
1596    let drawable_size: NSSize = NSSize {
1597        width: size.width * scale_factor,
1598        height: size.height * scale_factor,
1599    };
1600
1601    lock.update_drawable_size(drawable_size);
1602
1603    drop(lock);
1604    let mut lock = window_state.lock();
1605    if let Some(mut callback) = lock.resize_callback.take() {
1606        let content_size = lock.content_size();
1607        let scale_factor = lock.scale_factor();
1608        drop(lock);
1609        callback(content_size, scale_factor);
1610        window_state.lock().resize_callback = Some(callback);
1611    };
1612}
1613
1614extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1615    let window_state = unsafe { get_window_state(this) };
1616    let mut lock = window_state.lock();
1617    if let Some(mut callback) = lock.request_frame_callback.take() {
1618        #[cfg(not(feature = "macos-blade"))]
1619        lock.renderer.set_presents_with_transaction(true);
1620        lock.stop_display_link();
1621        drop(lock);
1622        callback();
1623
1624        let mut lock = window_state.lock();
1625        lock.request_frame_callback = Some(callback);
1626        #[cfg(not(feature = "macos-blade"))]
1627        lock.renderer.set_presents_with_transaction(false);
1628        lock.start_display_link();
1629    }
1630}
1631
1632unsafe extern "C" fn step(view: *mut c_void) {
1633    let view = view as id;
1634    let window_state = unsafe { get_window_state(&*view) };
1635    let mut lock = window_state.lock();
1636
1637    if let Some(mut callback) = lock.request_frame_callback.take() {
1638        drop(lock);
1639        callback();
1640        window_state.lock().request_frame_callback = Some(callback);
1641    }
1642}
1643
1644extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1645    unsafe { msg_send![class!(NSArray), array] }
1646}
1647
1648extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1649    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1650        .flatten()
1651        .is_some() as BOOL
1652}
1653
1654extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1655    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1656        .flatten()
1657        .map_or(NSRange::invalid(), |range| range.into())
1658}
1659
1660extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1661    with_input_handler(this, |input_handler| input_handler.selected_text_range())
1662        .flatten()
1663        .map_or(NSRange::invalid(), |range| range.into())
1664}
1665
1666extern "C" fn first_rect_for_character_range(
1667    this: &Object,
1668    _: Sel,
1669    range: NSRange,
1670    _: id,
1671) -> NSRect {
1672    let frame = unsafe {
1673        let window = get_window_state(this).lock().native_window;
1674        NSView::frame(window)
1675    };
1676    with_input_handler(this, |input_handler| {
1677        input_handler.bounds_for_range(range.to_range()?)
1678    })
1679    .flatten()
1680    .map_or(
1681        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1682        |bounds| {
1683            NSRect::new(
1684                NSPoint::new(
1685                    frame.origin.x + bounds.origin.x.0 as f64,
1686                    frame.origin.y + frame.size.height
1687                        - bounds.origin.y.0 as f64
1688                        - bounds.size.height.0 as f64,
1689                ),
1690                NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
1691            )
1692        },
1693    )
1694}
1695
1696extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1697    unsafe {
1698        let is_attributed_string: BOOL =
1699            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1700        let text: id = if is_attributed_string == YES {
1701            msg_send![text, string]
1702        } else {
1703            text
1704        };
1705        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1706            .to_str()
1707            .unwrap();
1708        let replacement_range = replacement_range.to_range();
1709        send_to_input_handler(
1710            this,
1711            ImeInput::InsertText(text.to_string(), replacement_range),
1712        );
1713    }
1714}
1715
1716extern "C" fn set_marked_text(
1717    this: &Object,
1718    _: Sel,
1719    text: id,
1720    selected_range: NSRange,
1721    replacement_range: NSRange,
1722) {
1723    unsafe {
1724        let is_attributed_string: BOOL =
1725            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1726        let text: id = if is_attributed_string == YES {
1727            msg_send![text, string]
1728        } else {
1729            text
1730        };
1731        let selected_range = selected_range.to_range();
1732        let replacement_range = replacement_range.to_range();
1733        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1734            .to_str()
1735            .unwrap();
1736
1737        send_to_input_handler(
1738            this,
1739            ImeInput::SetMarkedText(text.to_string(), replacement_range, selected_range),
1740        );
1741    }
1742}
1743extern "C" fn unmark_text(this: &Object, _: Sel) {
1744    send_to_input_handler(this, ImeInput::UnmarkText);
1745}
1746
1747extern "C" fn attributed_substring_for_proposed_range(
1748    this: &Object,
1749    _: Sel,
1750    range: NSRange,
1751    _actual_range: *mut c_void,
1752) -> id {
1753    with_input_handler(this, |input_handler| {
1754        let range = range.to_range()?;
1755        if range.is_empty() {
1756            return None;
1757        }
1758
1759        let selected_text = input_handler.text_for_range(range)?;
1760        unsafe {
1761            let string: id = msg_send![class!(NSAttributedString), alloc];
1762            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1763            Some(string)
1764        }
1765    })
1766    .flatten()
1767    .unwrap_or(nil)
1768}
1769
1770extern "C" fn do_command_by_selector(_: &Object, _: Sel, _: Sel) {}
1771
1772extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1773    unsafe {
1774        let state = get_window_state(this);
1775        let mut lock = state.as_ref().lock();
1776        if let Some(mut callback) = lock.appearance_changed_callback.take() {
1777            drop(lock);
1778            callback();
1779            state.lock().appearance_changed_callback = Some(callback);
1780        }
1781    }
1782}
1783
1784extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1785    let window_state = unsafe { get_window_state(this) };
1786    let mut lock = window_state.as_ref().lock();
1787    lock.first_mouse = true;
1788    YES
1789}
1790
1791extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
1792    let window_state = unsafe { get_window_state(this) };
1793    let position = drag_event_position(&window_state, dragging_info);
1794    let paths = external_paths_from_event(dragging_info);
1795    if let Some(event) =
1796        paths.map(|paths| PlatformInput::FileDrop(FileDropEvent::Entered { position, paths }))
1797    {
1798        if send_new_event(&window_state, event) {
1799            window_state.lock().external_files_dragged = true;
1800            return NSDragOperationCopy;
1801        }
1802    }
1803    NSDragOperationNone
1804}
1805
1806extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
1807    let window_state = unsafe { get_window_state(this) };
1808    let position = drag_event_position(&window_state, dragging_info);
1809    if send_new_event(
1810        &window_state,
1811        PlatformInput::FileDrop(FileDropEvent::Pending { position }),
1812    ) {
1813        NSDragOperationCopy
1814    } else {
1815        NSDragOperationNone
1816    }
1817}
1818
1819extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
1820    let window_state = unsafe { get_window_state(this) };
1821    send_new_event(
1822        &window_state,
1823        PlatformInput::FileDrop(FileDropEvent::Exited),
1824    );
1825    window_state.lock().external_files_dragged = false;
1826}
1827
1828extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -> BOOL {
1829    let window_state = unsafe { get_window_state(this) };
1830    let position = drag_event_position(&window_state, dragging_info);
1831    if send_new_event(
1832        &window_state,
1833        PlatformInput::FileDrop(FileDropEvent::Submit { position }),
1834    ) {
1835        YES
1836    } else {
1837        NO
1838    }
1839}
1840
1841fn external_paths_from_event(dragging_info: *mut Object) -> Option<ExternalPaths> {
1842    let mut paths = SmallVec::new();
1843    let pasteboard: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
1844    let filenames = unsafe { NSPasteboard::propertyListForType(pasteboard, NSFilenamesPboardType) };
1845    if filenames == nil {
1846        return None;
1847    }
1848    for file in unsafe { filenames.iter() } {
1849        let path = unsafe {
1850            let f = NSString::UTF8String(file);
1851            CStr::from_ptr(f).to_string_lossy().into_owned()
1852        };
1853        paths.push(PathBuf::from(path))
1854    }
1855    Some(ExternalPaths(paths))
1856}
1857
1858extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
1859    let window_state = unsafe { get_window_state(this) };
1860    send_new_event(
1861        &window_state,
1862        PlatformInput::FileDrop(FileDropEvent::Exited),
1863    );
1864}
1865
1866extern "C" fn window_did_miniaturize(this: &Object, _: Sel, _: id) {
1867    let window_state = unsafe { get_window_state(this) };
1868
1869    window_state.lock().minimized = true;
1870}
1871
1872extern "C" fn window_did_deminiaturize(this: &Object, _: Sel, _: id) {
1873    let window_state = unsafe { get_window_state(this) };
1874
1875    window_state.lock().minimized = false;
1876}
1877
1878async fn synthetic_drag(
1879    window_state: Weak<Mutex<MacWindowState>>,
1880    drag_id: usize,
1881    event: MouseMoveEvent,
1882) {
1883    loop {
1884        Timer::after(Duration::from_millis(16)).await;
1885        if let Some(window_state) = window_state.upgrade() {
1886            let mut lock = window_state.lock();
1887            if lock.synthetic_drag_counter == drag_id {
1888                if let Some(mut callback) = lock.event_callback.take() {
1889                    drop(lock);
1890                    callback(PlatformInput::MouseMove(event.clone()));
1891                    window_state.lock().event_callback = Some(callback);
1892                }
1893            } else {
1894                break;
1895            }
1896        }
1897    }
1898}
1899
1900fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: PlatformInput) -> bool {
1901    let window_state = window_state_lock.lock().event_callback.take();
1902    if let Some(mut callback) = window_state {
1903        callback(e);
1904        window_state_lock.lock().event_callback = Some(callback);
1905        true
1906    } else {
1907        false
1908    }
1909}
1910
1911fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id) -> Point<Pixels> {
1912    let drag_location: NSPoint = unsafe { msg_send![dragging_info, draggingLocation] };
1913    convert_mouse_position(drag_location, window_state.lock().content_size().height)
1914}
1915
1916fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
1917where
1918    F: FnOnce(&mut PlatformInputHandler) -> R,
1919{
1920    let window_state = unsafe { get_window_state(window) };
1921    let mut lock = window_state.as_ref().lock();
1922    if let Some(mut input_handler) = lock.input_handler.take() {
1923        drop(lock);
1924        let result = f(&mut input_handler);
1925        window_state.lock().input_handler = Some(input_handler);
1926        Some(result)
1927    } else {
1928        None
1929    }
1930}
1931
1932fn send_to_input_handler(window: &Object, ime: ImeInput) {
1933    unsafe {
1934        let window_state = get_window_state(window);
1935        let mut lock = window_state.lock();
1936        if let Some(ime_input) = lock.input_during_keydown.as_mut() {
1937            ime_input.push(ime);
1938            return;
1939        }
1940        if let Some(mut input_handler) = lock.input_handler.take() {
1941            drop(lock);
1942            match ime {
1943                ImeInput::InsertText(text, range) => {
1944                    input_handler.replace_text_in_range(range, &text)
1945                }
1946                ImeInput::SetMarkedText(text, range, marked_range) => {
1947                    input_handler.replace_and_mark_text_in_range(range, &text, marked_range)
1948                }
1949                ImeInput::UnmarkText => input_handler.unmark_text(),
1950            }
1951            window_state.lock().input_handler = Some(input_handler);
1952        }
1953    }
1954}
1955
1956unsafe fn display_id_for_screen(screen: id) -> CGDirectDisplayID {
1957    let device_description = NSScreen::deviceDescription(screen);
1958    let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
1959    let screen_number = device_description.objectForKey_(screen_number_key);
1960    let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
1961    screen_number as CGDirectDisplayID
1962}