window.rs

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