window.rs

   1use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
   2use crate::{
   3    AnyWindowHandle, Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor,
   4    KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
   5    MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
   6    PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, ScaledPixels, Size,
   7    Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea,
   8    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 set_input_handler(&mut self, input_handler: PlatformInputHandler) {
 894        self.0.as_ref().lock().input_handler = Some(input_handler);
 895    }
 896
 897    fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
 898        self.0.as_ref().lock().input_handler.take()
 899    }
 900
 901    fn prompt(
 902        &self,
 903        level: PromptLevel,
 904        msg: &str,
 905        detail: Option<&str>,
 906        answers: &[PromptButton],
 907    ) -> Option<oneshot::Receiver<usize>> {
 908        // macOs applies overrides to modal window buttons after they are added.
 909        // Two most important for this logic are:
 910        // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
 911        // * Last button added to the modal via `addButtonWithTitle` stays focused
 912        // * Focused buttons react on "space"/" " keypresses
 913        // * Usage of `keyEquivalent`, `makeFirstResponder` or `setInitialFirstResponder` does not change the focus
 914        //
 915        // See also https://developer.apple.com/documentation/appkit/nsalert/1524532-addbuttonwithtitle#discussion
 916        // ```
 917        // By default, the first button has a key equivalent of Return,
 918        // any button with a title of “Cancel” has a key equivalent of Escape,
 919        // 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).
 920        // ```
 921        //
 922        // To avoid situations when the last element added is "Cancel" and it gets the focus
 923        // (hence stealing both ESC and Space shortcuts), we find and add one non-Cancel button
 924        // last, so it gets focus and a Space shortcut.
 925        // This way, "Save this file? Yes/No/Cancel"-ish modals will get all three buttons mapped with a key.
 926        let latest_non_cancel_label = answers
 927            .iter()
 928            .enumerate()
 929            .rev()
 930            .find(|(_, label)| !label.is_cancel())
 931            .filter(|&(label_index, _)| label_index > 0);
 932
 933        unsafe {
 934            let alert: id = msg_send![class!(NSAlert), alloc];
 935            let alert: id = msg_send![alert, init];
 936            let alert_style = match level {
 937                PromptLevel::Info => 1,
 938                PromptLevel::Warning => 0,
 939                PromptLevel::Critical => 2,
 940            };
 941            let _: () = msg_send![alert, setAlertStyle: alert_style];
 942            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
 943            if let Some(detail) = detail {
 944                let _: () = msg_send![alert, setInformativeText: ns_string(detail)];
 945            }
 946
 947            for (ix, answer) in answers
 948                .iter()
 949                .enumerate()
 950                .filter(|&(ix, _)| Some(ix) != latest_non_cancel_label.map(|(ix, _)| ix))
 951            {
 952                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer.label())];
 953                let _: () = msg_send![button, setTag: ix as NSInteger];
 954
 955                if answer.is_cancel() {
 956                    // Bind Escape Key to Cancel Button
 957                    if let Some(key) = std::char::from_u32(super::events::ESCAPE_KEY as u32) {
 958                        let _: () =
 959                            msg_send![button, setKeyEquivalent: ns_string(&key.to_string())];
 960                    }
 961                }
 962            }
 963            if let Some((ix, answer)) = latest_non_cancel_label {
 964                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer.label())];
 965                let _: () = msg_send![button, setTag: ix as NSInteger];
 966            }
 967
 968            let (done_tx, done_rx) = oneshot::channel();
 969            let done_tx = Cell::new(Some(done_tx));
 970            let block = ConcreteBlock::new(move |answer: NSInteger| {
 971                if let Some(done_tx) = done_tx.take() {
 972                    let _ = done_tx.send(answer.try_into().unwrap());
 973                }
 974            });
 975            let block = block.copy();
 976            let native_window = self.0.lock().native_window;
 977            let executor = self.0.lock().executor.clone();
 978            executor
 979                .spawn(async move {
 980                    let _: () = msg_send![
 981                        alert,
 982                        beginSheetModalForWindow: native_window
 983                        completionHandler: block
 984                    ];
 985                })
 986                .detach();
 987
 988            Some(done_rx)
 989        }
 990    }
 991
 992    fn activate(&self) {
 993        let window = self.0.lock().native_window;
 994        let executor = self.0.lock().executor.clone();
 995        executor
 996            .spawn(async move {
 997                unsafe {
 998                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
 999                }
1000            })
1001            .detach();
1002    }
1003
1004    fn is_active(&self) -> bool {
1005        unsafe { self.0.lock().native_window.isKeyWindow() == YES }
1006    }
1007
1008    // is_hovered is unused on macOS. See Window::is_window_hovered.
1009    fn is_hovered(&self) -> bool {
1010        false
1011    }
1012
1013    fn set_title(&mut self, title: &str) {
1014        unsafe {
1015            let app = NSApplication::sharedApplication(nil);
1016            let window = self.0.lock().native_window;
1017            let title = ns_string(title);
1018            let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
1019            let _: () = msg_send![window, setTitle: title];
1020            self.0.lock().move_traffic_light();
1021        }
1022    }
1023
1024    fn set_app_id(&mut self, _app_id: &str) {}
1025
1026    fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
1027        let mut this = self.0.as_ref().lock();
1028        this.renderer
1029            .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
1030
1031        let blur_radius = if background_appearance == WindowBackgroundAppearance::Blurred {
1032            80
1033        } else {
1034            0
1035        };
1036        let opaque = (background_appearance == WindowBackgroundAppearance::Opaque).to_objc();
1037
1038        unsafe {
1039            this.native_window.setOpaque_(opaque);
1040            // Shadows for transparent windows cause artifacts and performance issues
1041            this.native_window.setHasShadow_(opaque);
1042            let clear_color = if opaque == YES {
1043                NSColor::colorWithSRGBRed_green_blue_alpha_(nil, 0f64, 0f64, 0f64, 1f64)
1044            } else {
1045                NSColor::clearColor(nil)
1046            };
1047            this.native_window.setBackgroundColor_(clear_color);
1048            let window_number = this.native_window.windowNumber();
1049            CGSSetWindowBackgroundBlurRadius(CGSMainConnectionID(), window_number, blur_radius);
1050        }
1051    }
1052
1053    fn set_edited(&mut self, edited: bool) {
1054        unsafe {
1055            let window = self.0.lock().native_window;
1056            msg_send![window, setDocumentEdited: edited as BOOL]
1057        }
1058
1059        // Changing the document edited state resets the traffic light position,
1060        // so we have to move it again.
1061        self.0.lock().move_traffic_light();
1062    }
1063
1064    fn show_character_palette(&self) {
1065        let this = self.0.lock();
1066        let window = this.native_window;
1067        this.executor
1068            .spawn(async move {
1069                unsafe {
1070                    let app = NSApplication::sharedApplication(nil);
1071                    let _: () = msg_send![app, orderFrontCharacterPalette: window];
1072                }
1073            })
1074            .detach();
1075    }
1076
1077    fn minimize(&self) {
1078        let window = self.0.lock().native_window;
1079        unsafe {
1080            window.miniaturize_(nil);
1081        }
1082    }
1083
1084    fn zoom(&self) {
1085        let this = self.0.lock();
1086        let window = this.native_window;
1087        this.executor
1088            .spawn(async move {
1089                unsafe {
1090                    window.zoom_(nil);
1091                }
1092            })
1093            .detach();
1094    }
1095
1096    fn toggle_fullscreen(&self) {
1097        let this = self.0.lock();
1098        let window = this.native_window;
1099        this.executor
1100            .spawn(async move {
1101                unsafe {
1102                    window.toggleFullScreen_(nil);
1103                }
1104            })
1105            .detach();
1106    }
1107
1108    fn is_fullscreen(&self) -> bool {
1109        let this = self.0.lock();
1110        let window = this.native_window;
1111
1112        unsafe {
1113            window
1114                .styleMask()
1115                .contains(NSWindowStyleMask::NSFullScreenWindowMask)
1116        }
1117    }
1118
1119    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
1120        self.0.as_ref().lock().request_frame_callback = Some(callback);
1121    }
1122
1123    fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
1124        self.0.as_ref().lock().event_callback = Some(callback);
1125    }
1126
1127    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
1128        self.0.as_ref().lock().activate_callback = Some(callback);
1129    }
1130
1131    fn on_hover_status_change(&self, _: Box<dyn FnMut(bool)>) {}
1132
1133    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
1134        self.0.as_ref().lock().resize_callback = Some(callback);
1135    }
1136
1137    fn on_moved(&self, callback: Box<dyn FnMut()>) {
1138        self.0.as_ref().lock().moved_callback = Some(callback);
1139    }
1140
1141    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
1142        self.0.as_ref().lock().should_close_callback = Some(callback);
1143    }
1144
1145    fn on_close(&self, callback: Box<dyn FnOnce()>) {
1146        self.0.as_ref().lock().close_callback = Some(callback);
1147    }
1148
1149    fn on_hit_test_window_control(&self, _callback: Box<dyn FnMut() -> Option<WindowControlArea>>) {
1150    }
1151
1152    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1153        self.0.lock().appearance_changed_callback = Some(callback);
1154    }
1155
1156    fn draw(&self, scene: &crate::Scene) {
1157        let mut this = self.0.lock();
1158        this.renderer.draw(scene);
1159    }
1160
1161    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1162        self.0.lock().renderer.sprite_atlas().clone()
1163    }
1164
1165    fn gpu_specs(&self) -> Option<crate::GpuSpecs> {
1166        None
1167    }
1168
1169    fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
1170        let executor = self.0.lock().executor.clone();
1171        executor
1172            .spawn(async move {
1173                unsafe {
1174                    let input_context: id =
1175                        msg_send![class!(NSTextInputContext), currentInputContext];
1176                    if input_context.is_null() {
1177                        return;
1178                    }
1179                    let _: () = msg_send![input_context, invalidateCharacterCoordinates];
1180                }
1181            })
1182            .detach()
1183    }
1184
1185    fn titlebar_double_click(&self) {
1186        let this = self.0.lock();
1187        let window = this.native_window;
1188        this.executor
1189            .spawn(async move {
1190                unsafe {
1191                    let defaults: id = NSUserDefaults::standardUserDefaults();
1192                    let domain = NSString::alloc(nil).init_str("NSGlobalDomain");
1193                    let key = NSString::alloc(nil).init_str("AppleActionOnDoubleClick");
1194
1195                    let dict: id = msg_send![defaults, persistentDomainForName: domain];
1196                    let action: id = if !dict.is_null() {
1197                        msg_send![dict, objectForKey: key]
1198                    } else {
1199                        nil
1200                    };
1201
1202                    let action_str = if !action.is_null() {
1203                        CStr::from_ptr(NSString::UTF8String(action)).to_string_lossy()
1204                    } else {
1205                        "".into()
1206                    };
1207
1208                    match action_str.as_ref() {
1209                        "Minimize" => {
1210                            window.miniaturize_(nil);
1211                        }
1212                        "Maximize" => {
1213                            window.zoom_(nil);
1214                        }
1215                        "Fill" => {
1216                            // There is no documented API for "Fill" action, so we'll just zoom the window
1217                            window.zoom_(nil);
1218                        }
1219                        _ => {
1220                            window.zoom_(nil);
1221                        }
1222                    }
1223                }
1224            })
1225            .detach();
1226    }
1227}
1228
1229impl rwh::HasWindowHandle for MacWindow {
1230    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1231        // SAFETY: The AppKitWindowHandle is a wrapper around a pointer to an NSView
1232        unsafe {
1233            Ok(rwh::WindowHandle::borrow_raw(rwh::RawWindowHandle::AppKit(
1234                rwh::AppKitWindowHandle::new(self.0.lock().native_view.cast()),
1235            )))
1236        }
1237    }
1238}
1239
1240impl rwh::HasDisplayHandle for MacWindow {
1241    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1242        // SAFETY: This is a no-op on macOS
1243        unsafe {
1244            Ok(rwh::DisplayHandle::borrow_raw(
1245                rwh::AppKitDisplayHandle::new().into(),
1246            ))
1247        }
1248    }
1249}
1250
1251fn get_scale_factor(native_window: id) -> f32 {
1252    let factor = unsafe {
1253        let screen: id = msg_send![native_window, screen];
1254        if screen.is_null() {
1255            return 1.0;
1256        }
1257        NSScreen::backingScaleFactor(screen) as f32
1258    };
1259
1260    // We are not certain what triggers this, but it seems that sometimes
1261    // this method would return 0 (https://github.com/zed-industries/zed/issues/6412)
1262    // It seems most likely that this would happen if the window has no screen
1263    // (if it is off-screen), though we'd expect to see viewDidChangeBackingProperties before
1264    // it was rendered for real.
1265    // Regardless, attempt to avoid the issue here.
1266    if factor == 0.0 { 2. } else { factor }
1267}
1268
1269unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
1270    unsafe {
1271        let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1272        let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1273        let rc2 = rc1.clone();
1274        mem::forget(rc1);
1275        rc2
1276    }
1277}
1278
1279unsafe fn drop_window_state(object: &Object) {
1280    unsafe {
1281        let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1282        Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1283    }
1284}
1285
1286extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
1287    YES
1288}
1289
1290extern "C" fn dealloc_window(this: &Object, _: Sel) {
1291    unsafe {
1292        drop_window_state(this);
1293        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
1294    }
1295}
1296
1297extern "C" fn dealloc_view(this: &Object, _: Sel) {
1298    unsafe {
1299        drop_window_state(this);
1300        let _: () = msg_send![super(this, class!(NSView)), dealloc];
1301    }
1302}
1303
1304extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
1305    handle_key_event(this, native_event, true)
1306}
1307
1308extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
1309    handle_key_event(this, native_event, false);
1310}
1311
1312extern "C" fn handle_key_up(this: &Object, _: Sel, native_event: id) {
1313    handle_key_event(this, native_event, false);
1314}
1315
1316// Things to test if you're modifying this method:
1317//  U.S. layout:
1318//   - The IME consumes characters like 'j' and 'k', which makes paging through `less` in
1319//     the terminal behave incorrectly by default. This behavior should be patched by our
1320//     IME integration
1321//   - `alt-t` should open the tasks menu
1322//   - In vim mode, this keybinding should work:
1323//     ```
1324//        {
1325//          "context": "Editor && vim_mode == insert",
1326//          "bindings": {"j j": "vim::NormalBefore"}
1327//        }
1328//     ```
1329//     and typing 'j k' in insert mode with this keybinding should insert the two characters
1330//  Brazilian layout:
1331//   - `" space` should create an unmarked quote
1332//   - `" backspace` should delete the marked quote
1333//   - `" "`should create an unmarked quote and a second marked quote
1334//   - `" up` should insert a quote, unmark it, and move up one line
1335//   - `" cmd-down` should insert a quote, unmark it, and move to the end of the file
1336//   - `cmd-ctrl-space` and clicking on an emoji should type it
1337//  Czech (QWERTY) layout:
1338//   - in vim mode `option-4`  should go to end of line (same as $)
1339//  Japanese (Romaji) layout:
1340//   - type `a i left down up enter enter` should create an unmarked text "愛"
1341extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
1342    let window_state = unsafe { get_window_state(this) };
1343    let mut lock = window_state.as_ref().lock();
1344
1345    let window_height = lock.content_size().height;
1346    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1347
1348    let Some(event) = event else {
1349        return NO;
1350    };
1351
1352    let run_callback = |event: PlatformInput| -> BOOL {
1353        let mut callback = window_state.as_ref().lock().event_callback.take();
1354        let handled: BOOL = if let Some(callback) = callback.as_mut() {
1355            !callback(event).propagate as BOOL
1356        } else {
1357            NO
1358        };
1359        window_state.as_ref().lock().event_callback = callback;
1360        handled
1361    };
1362
1363    match event {
1364        PlatformInput::KeyDown(mut key_down_event) => {
1365            // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
1366            // If that event isn't handled, it will then dispatch a "key down" event. GPUI
1367            // makes no distinction between these two types of events, so we need to ignore
1368            // the "key down" event if we've already just processed its "key equivalent" version.
1369            if key_equivalent {
1370                lock.last_key_equivalent = Some(key_down_event.clone());
1371            } else if lock.last_key_equivalent.take().as_ref() == Some(&key_down_event) {
1372                return NO;
1373            }
1374
1375            drop(lock);
1376
1377            let is_composing =
1378                with_input_handler(this, |input_handler| input_handler.marked_text_range())
1379                    .flatten()
1380                    .is_some();
1381
1382            // If we're composing, send the key to the input handler first;
1383            // otherwise we only send to the input handler if we don't have a matching binding.
1384            // The input handler may call `do_command_by_selector` if it doesn't know how to handle
1385            // a key. If it does so, it will return YES so we won't send the key twice.
1386            // We also do this for non-printing keys (like arrow keys and escape) as the IME menu
1387            // may need them even if there is no marked text;
1388            // however we skip keys with control or the input handler adds control-characters to the buffer.
1389            // and keys with function, as the input handler swallows them.
1390            if is_composing
1391                || (key_down_event.keystroke.key_char.is_none()
1392                    && !key_down_event.keystroke.modifiers.control
1393                    && !key_down_event.keystroke.modifiers.function)
1394            {
1395                {
1396                    let mut lock = window_state.as_ref().lock();
1397                    lock.keystroke_for_do_command = Some(key_down_event.keystroke.clone());
1398                    lock.do_command_handled.take();
1399                    drop(lock);
1400                }
1401
1402                let handled: BOOL = unsafe {
1403                    let input_context: id = msg_send![this, inputContext];
1404                    msg_send![input_context, handleEvent: native_event]
1405                };
1406                window_state.as_ref().lock().keystroke_for_do_command.take();
1407                if let Some(handled) = window_state.as_ref().lock().do_command_handled.take() {
1408                    return handled as BOOL;
1409                } else if handled == YES {
1410                    return YES;
1411                }
1412
1413                let handled = run_callback(PlatformInput::KeyDown(key_down_event));
1414                return handled;
1415            }
1416
1417            let handled = run_callback(PlatformInput::KeyDown(key_down_event.clone()));
1418            if handled == YES {
1419                return YES;
1420            }
1421
1422            if key_down_event.is_held {
1423                if let Some(key_char) = key_down_event.keystroke.key_char.as_ref() {
1424                    let handled = with_input_handler(&this, |input_handler| {
1425                        if !input_handler.apple_press_and_hold_enabled() {
1426                            input_handler.replace_text_in_range(None, &key_char);
1427                            return YES;
1428                        }
1429                        NO
1430                    });
1431                    if handled == Some(YES) {
1432                        return YES;
1433                    }
1434                }
1435            }
1436
1437            // Don't send key equivalents to the input handler,
1438            // or macOS shortcuts like cmd-` will stop working.
1439            if key_equivalent {
1440                return NO;
1441            }
1442
1443            unsafe {
1444                let input_context: id = msg_send![this, inputContext];
1445                msg_send![input_context, handleEvent: native_event]
1446            }
1447        }
1448
1449        PlatformInput::KeyUp(_) => {
1450            drop(lock);
1451            run_callback(event)
1452        }
1453
1454        _ => NO,
1455    }
1456}
1457
1458extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
1459    let window_state = unsafe { get_window_state(this) };
1460    let weak_window_state = Arc::downgrade(&window_state);
1461    let mut lock = window_state.as_ref().lock();
1462    let window_height = lock.content_size().height;
1463    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1464
1465    if let Some(mut event) = event {
1466        match &mut event {
1467            PlatformInput::MouseDown(
1468                event @ MouseDownEvent {
1469                    button: MouseButton::Left,
1470                    modifiers: Modifiers { control: true, .. },
1471                    ..
1472                },
1473            ) => {
1474                // On mac, a ctrl-left click should be handled as a right click.
1475                *event = MouseDownEvent {
1476                    button: MouseButton::Right,
1477                    modifiers: Modifiers {
1478                        control: false,
1479                        ..event.modifiers
1480                    },
1481                    click_count: 1,
1482                    ..*event
1483                };
1484            }
1485
1486            // Handles focusing click.
1487            PlatformInput::MouseDown(
1488                event @ MouseDownEvent {
1489                    button: MouseButton::Left,
1490                    ..
1491                },
1492            ) if (lock.first_mouse) => {
1493                *event = MouseDownEvent {
1494                    first_mouse: true,
1495                    ..*event
1496                };
1497                lock.first_mouse = false;
1498            }
1499
1500            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
1501            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
1502            // user is still holding ctrl when releasing the left mouse button
1503            PlatformInput::MouseUp(
1504                event @ MouseUpEvent {
1505                    button: MouseButton::Left,
1506                    modifiers: Modifiers { control: true, .. },
1507                    ..
1508                },
1509            ) => {
1510                *event = MouseUpEvent {
1511                    button: MouseButton::Right,
1512                    modifiers: Modifiers {
1513                        control: false,
1514                        ..event.modifiers
1515                    },
1516                    click_count: 1,
1517                    ..*event
1518                };
1519            }
1520
1521            _ => {}
1522        };
1523
1524        match &event {
1525            PlatformInput::MouseDown(_) => {
1526                drop(lock);
1527                unsafe {
1528                    let input_context: id = msg_send![this, inputContext];
1529                    msg_send![input_context, handleEvent: native_event]
1530                }
1531                lock = window_state.as_ref().lock();
1532            }
1533            PlatformInput::MouseMove(
1534                event @ MouseMoveEvent {
1535                    pressed_button: Some(_),
1536                    ..
1537                },
1538            ) => {
1539                // Synthetic drag is used for selecting long buffer contents while buffer is being scrolled.
1540                // External file drag and drop is able to emit its own synthetic mouse events which will conflict
1541                // with these ones.
1542                if !lock.external_files_dragged {
1543                    lock.synthetic_drag_counter += 1;
1544                    let executor = lock.executor.clone();
1545                    executor
1546                        .spawn(synthetic_drag(
1547                            weak_window_state,
1548                            lock.synthetic_drag_counter,
1549                            event.clone(),
1550                        ))
1551                        .detach();
1552                }
1553            }
1554
1555            PlatformInput::MouseUp(MouseUpEvent { .. }) => {
1556                lock.synthetic_drag_counter += 1;
1557            }
1558
1559            PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1560                // Only raise modifiers changed event when they have actually changed
1561                if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1562                    modifiers: prev_modifiers,
1563                })) = &lock.previous_modifiers_changed_event
1564                {
1565                    if prev_modifiers == modifiers {
1566                        return;
1567                    }
1568                }
1569
1570                lock.previous_modifiers_changed_event = Some(event.clone());
1571            }
1572
1573            _ => {}
1574        }
1575
1576        if let Some(mut callback) = lock.event_callback.take() {
1577            drop(lock);
1578            callback(event);
1579            window_state.lock().event_callback = Some(callback);
1580        }
1581    }
1582}
1583
1584extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
1585    let window_state = unsafe { get_window_state(this) };
1586    let lock = &mut *window_state.lock();
1587    unsafe {
1588        if lock
1589            .native_window
1590            .occlusionState()
1591            .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible)
1592        {
1593            lock.start_display_link();
1594        } else {
1595            lock.stop_display_link();
1596        }
1597    }
1598}
1599
1600extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1601    let window_state = unsafe { get_window_state(this) };
1602    window_state.as_ref().lock().move_traffic_light();
1603}
1604
1605extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1606    let window_state = unsafe { get_window_state(this) };
1607    let mut lock = window_state.as_ref().lock();
1608    lock.fullscreen_restore_bounds = lock.bounds();
1609
1610    let min_version = NSOperatingSystemVersion::new(15, 3, 0);
1611
1612    if is_macos_version_at_least(min_version) {
1613        unsafe {
1614            lock.native_window.setTitlebarAppearsTransparent_(NO);
1615        }
1616    }
1617}
1618
1619extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1620    let window_state = unsafe { get_window_state(this) };
1621    let mut lock = window_state.as_ref().lock();
1622
1623    let min_version = NSOperatingSystemVersion::new(15, 3, 0);
1624
1625    if is_macos_version_at_least(min_version) && lock.transparent_titlebar {
1626        unsafe {
1627            lock.native_window.setTitlebarAppearsTransparent_(YES);
1628        }
1629    }
1630}
1631
1632pub(crate) fn is_macos_version_at_least(version: NSOperatingSystemVersion) -> bool {
1633    unsafe { NSProcessInfo::processInfo(nil).isOperatingSystemAtLeastVersion(version) }
1634}
1635
1636extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
1637    let window_state = unsafe { get_window_state(this) };
1638    let mut lock = window_state.as_ref().lock();
1639    if let Some(mut callback) = lock.moved_callback.take() {
1640        drop(lock);
1641        callback();
1642        window_state.lock().moved_callback = Some(callback);
1643    }
1644}
1645
1646extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
1647    let window_state = unsafe { get_window_state(this) };
1648    let mut lock = window_state.as_ref().lock();
1649    lock.start_display_link();
1650}
1651
1652extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1653    let window_state = unsafe { get_window_state(this) };
1654    let lock = window_state.lock();
1655    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
1656
1657    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1658    // `windowDidBecomeKey` message to the previous key window even though that window
1659    // isn't actually key. This causes a bug if the application is later activated while
1660    // the pop-up is still open, making it impossible to activate the previous key window
1661    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1662    // the app and re-activate it, which is a pretty bad UX.
1663    // The following code detects the spurious event and invokes `resignKeyWindow`:
1664    // in theory, we're not supposed to invoke this method manually but it balances out
1665    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1666    if selector == sel!(windowDidBecomeKey:) && !is_active {
1667        unsafe {
1668            let _: () = msg_send![lock.native_window, resignKeyWindow];
1669            return;
1670        }
1671    }
1672
1673    let executor = lock.executor.clone();
1674    drop(lock);
1675    executor
1676        .spawn(async move {
1677            let mut lock = window_state.as_ref().lock();
1678            if let Some(mut callback) = lock.activate_callback.take() {
1679                drop(lock);
1680                callback(is_active);
1681                window_state.lock().activate_callback = Some(callback);
1682            };
1683        })
1684        .detach();
1685}
1686
1687extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1688    let window_state = unsafe { get_window_state(this) };
1689    let mut lock = window_state.as_ref().lock();
1690    if let Some(mut callback) = lock.should_close_callback.take() {
1691        drop(lock);
1692        let should_close = callback();
1693        window_state.lock().should_close_callback = Some(callback);
1694        should_close as BOOL
1695    } else {
1696        YES
1697    }
1698}
1699
1700extern "C" fn close_window(this: &Object, _: Sel) {
1701    unsafe {
1702        let close_callback = {
1703            let window_state = get_window_state(this);
1704            let mut lock = window_state.as_ref().lock();
1705            lock.close_callback.take()
1706        };
1707
1708        if let Some(callback) = close_callback {
1709            callback();
1710        }
1711
1712        let _: () = msg_send![super(this, class!(NSWindow)), close];
1713    }
1714}
1715
1716extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1717    let window_state = unsafe { get_window_state(this) };
1718    let window_state = window_state.as_ref().lock();
1719    window_state.renderer.layer_ptr() as id
1720}
1721
1722extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1723    let window_state = unsafe { get_window_state(this) };
1724    let mut lock = window_state.as_ref().lock();
1725
1726    let scale_factor = lock.scale_factor();
1727    let size = lock.content_size();
1728    let drawable_size = size.to_device_pixels(scale_factor);
1729    unsafe {
1730        let _: () = msg_send![
1731            lock.renderer.layer(),
1732            setContentsScale: scale_factor as f64
1733        ];
1734    }
1735
1736    lock.renderer.update_drawable_size(drawable_size);
1737
1738    if let Some(mut callback) = lock.resize_callback.take() {
1739        let content_size = lock.content_size();
1740        let scale_factor = lock.scale_factor();
1741        drop(lock);
1742        callback(content_size, scale_factor);
1743        window_state.as_ref().lock().resize_callback = Some(callback);
1744    };
1745}
1746
1747extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1748    let window_state = unsafe { get_window_state(this) };
1749    let mut lock = window_state.as_ref().lock();
1750
1751    let new_size = Size::<Pixels>::from(size);
1752    if lock.content_size() == new_size {
1753        return;
1754    }
1755
1756    unsafe {
1757        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1758    }
1759
1760    let scale_factor = lock.scale_factor();
1761    let drawable_size = new_size.to_device_pixels(scale_factor);
1762    lock.renderer.update_drawable_size(drawable_size);
1763
1764    if let Some(mut callback) = lock.resize_callback.take() {
1765        let content_size = lock.content_size();
1766        let scale_factor = lock.scale_factor();
1767        drop(lock);
1768        callback(content_size, scale_factor);
1769        window_state.lock().resize_callback = Some(callback);
1770    };
1771}
1772
1773extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1774    let window_state = unsafe { get_window_state(this) };
1775    let mut lock = window_state.lock();
1776    if let Some(mut callback) = lock.request_frame_callback.take() {
1777        #[cfg(not(feature = "macos-blade"))]
1778        lock.renderer.set_presents_with_transaction(true);
1779        lock.stop_display_link();
1780        drop(lock);
1781        callback(Default::default());
1782
1783        let mut lock = window_state.lock();
1784        lock.request_frame_callback = Some(callback);
1785        #[cfg(not(feature = "macos-blade"))]
1786        lock.renderer.set_presents_with_transaction(false);
1787        lock.start_display_link();
1788    }
1789}
1790
1791unsafe extern "C" fn step(view: *mut c_void) {
1792    let view = view as id;
1793    let window_state = unsafe { get_window_state(&*view) };
1794    let mut lock = window_state.lock();
1795
1796    if let Some(mut callback) = lock.request_frame_callback.take() {
1797        drop(lock);
1798        callback(Default::default());
1799        window_state.lock().request_frame_callback = Some(callback);
1800    }
1801}
1802
1803extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1804    unsafe { msg_send![class!(NSArray), array] }
1805}
1806
1807extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1808    let has_marked_text_result =
1809        with_input_handler(this, |input_handler| input_handler.marked_text_range()).flatten();
1810
1811    has_marked_text_result.is_some() as BOOL
1812}
1813
1814extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1815    let marked_range_result =
1816        with_input_handler(this, |input_handler| input_handler.marked_text_range()).flatten();
1817
1818    marked_range_result.map_or(NSRange::invalid(), |range| range.into())
1819}
1820
1821extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1822    let selected_range_result = with_input_handler(this, |input_handler| {
1823        input_handler.selected_text_range(false)
1824    })
1825    .flatten();
1826
1827    selected_range_result.map_or(NSRange::invalid(), |selection| selection.range.into())
1828}
1829
1830extern "C" fn first_rect_for_character_range(
1831    this: &Object,
1832    _: Sel,
1833    range: NSRange,
1834    _: id,
1835) -> NSRect {
1836    let frame = get_frame(this);
1837    with_input_handler(this, |input_handler| {
1838        input_handler.bounds_for_range(range.to_range()?)
1839    })
1840    .flatten()
1841    .map_or(
1842        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1843        |bounds| {
1844            NSRect::new(
1845                NSPoint::new(
1846                    frame.origin.x + bounds.origin.x.0 as f64,
1847                    frame.origin.y + frame.size.height
1848                        - bounds.origin.y.0 as f64
1849                        - bounds.size.height.0 as f64,
1850                ),
1851                NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
1852            )
1853        },
1854    )
1855}
1856
1857fn get_frame(this: &Object) -> NSRect {
1858    unsafe {
1859        let state = get_window_state(this);
1860        let lock = state.lock();
1861        let mut frame = NSWindow::frame(lock.native_window);
1862        let content_layout_rect: CGRect = msg_send![lock.native_window, contentLayoutRect];
1863        let style_mask: NSWindowStyleMask = msg_send![lock.native_window, styleMask];
1864        if !style_mask.contains(NSWindowStyleMask::NSFullSizeContentViewWindowMask) {
1865            frame.origin.y -= frame.size.height - content_layout_rect.size.height;
1866        }
1867        frame
1868    }
1869}
1870
1871extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1872    unsafe {
1873        let is_attributed_string: BOOL =
1874            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1875        let text: id = if is_attributed_string == YES {
1876            msg_send![text, string]
1877        } else {
1878            text
1879        };
1880
1881        let text = text.to_str();
1882        let replacement_range = replacement_range.to_range();
1883        with_input_handler(this, |input_handler| {
1884            input_handler.replace_text_in_range(replacement_range, &text)
1885        });
1886    }
1887}
1888
1889extern "C" fn set_marked_text(
1890    this: &Object,
1891    _: Sel,
1892    text: id,
1893    selected_range: NSRange,
1894    replacement_range: NSRange,
1895) {
1896    unsafe {
1897        let is_attributed_string: BOOL =
1898            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1899        let text: id = if is_attributed_string == YES {
1900            msg_send![text, string]
1901        } else {
1902            text
1903        };
1904        let selected_range = selected_range.to_range();
1905        let replacement_range = replacement_range.to_range();
1906        let text = text.to_str();
1907        with_input_handler(this, |input_handler| {
1908            input_handler.replace_and_mark_text_in_range(replacement_range, &text, selected_range)
1909        });
1910    }
1911}
1912extern "C" fn unmark_text(this: &Object, _: Sel) {
1913    with_input_handler(this, |input_handler| input_handler.unmark_text());
1914}
1915
1916extern "C" fn attributed_substring_for_proposed_range(
1917    this: &Object,
1918    _: Sel,
1919    range: NSRange,
1920    actual_range: *mut c_void,
1921) -> id {
1922    with_input_handler(this, |input_handler| {
1923        let range = range.to_range()?;
1924        if range.is_empty() {
1925            return None;
1926        }
1927        let mut adjusted: Option<Range<usize>> = None;
1928
1929        let selected_text = input_handler.text_for_range(range.clone(), &mut adjusted)?;
1930        if let Some(adjusted) = adjusted {
1931            if adjusted != range {
1932                unsafe { (actual_range as *mut NSRange).write(NSRange::from(adjusted)) };
1933            }
1934        }
1935        unsafe {
1936            let string: id = msg_send![class!(NSAttributedString), alloc];
1937            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1938            Some(string)
1939        }
1940    })
1941    .flatten()
1942    .unwrap_or(nil)
1943}
1944
1945// We ignore which selector it asks us to do because the user may have
1946// bound the shortcut to something else.
1947extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1948    let state = unsafe { get_window_state(this) };
1949    let mut lock = state.as_ref().lock();
1950    let keystroke = lock.keystroke_for_do_command.take();
1951    let mut event_callback = lock.event_callback.take();
1952    drop(lock);
1953
1954    if let Some((keystroke, mut callback)) = keystroke.zip(event_callback.as_mut()) {
1955        let handled = (callback)(PlatformInput::KeyDown(KeyDownEvent {
1956            keystroke,
1957            is_held: false,
1958        }));
1959        state.as_ref().lock().do_command_handled = Some(!handled.propagate);
1960    }
1961
1962    state.as_ref().lock().event_callback = event_callback;
1963}
1964
1965extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1966    unsafe {
1967        let state = get_window_state(this);
1968        let mut lock = state.as_ref().lock();
1969        if let Some(mut callback) = lock.appearance_changed_callback.take() {
1970            drop(lock);
1971            callback();
1972            state.lock().appearance_changed_callback = Some(callback);
1973        }
1974    }
1975}
1976
1977extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1978    let window_state = unsafe { get_window_state(this) };
1979    let mut lock = window_state.as_ref().lock();
1980    lock.first_mouse = true;
1981    YES
1982}
1983
1984extern "C" fn character_index_for_point(this: &Object, _: Sel, position: NSPoint) -> u64 {
1985    let position = screen_point_to_gpui_point(this, position);
1986    with_input_handler(this, |input_handler| {
1987        input_handler.character_index_for_point(position)
1988    })
1989    .flatten()
1990    .map(|index| index as u64)
1991    .unwrap_or(NSNotFound as u64)
1992}
1993
1994fn screen_point_to_gpui_point(this: &Object, position: NSPoint) -> Point<Pixels> {
1995    let frame = get_frame(this);
1996    let window_x = position.x - frame.origin.x;
1997    let window_y = frame.size.height - (position.y - frame.origin.y);
1998    let position = point(px(window_x as f32), px(window_y as f32));
1999    position
2000}
2001
2002extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
2003    let window_state = unsafe { get_window_state(this) };
2004    let position = drag_event_position(&window_state, dragging_info);
2005    let paths = external_paths_from_event(dragging_info);
2006    if let Some(event) =
2007        paths.map(|paths| PlatformInput::FileDrop(FileDropEvent::Entered { position, paths }))
2008    {
2009        if send_new_event(&window_state, event) {
2010            window_state.lock().external_files_dragged = true;
2011            return NSDragOperationCopy;
2012        }
2013    }
2014    NSDragOperationNone
2015}
2016
2017extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
2018    let window_state = unsafe { get_window_state(this) };
2019    let position = drag_event_position(&window_state, dragging_info);
2020    if send_new_event(
2021        &window_state,
2022        PlatformInput::FileDrop(FileDropEvent::Pending { position }),
2023    ) {
2024        NSDragOperationCopy
2025    } else {
2026        NSDragOperationNone
2027    }
2028}
2029
2030extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
2031    let window_state = unsafe { get_window_state(this) };
2032    send_new_event(
2033        &window_state,
2034        PlatformInput::FileDrop(FileDropEvent::Exited),
2035    );
2036    window_state.lock().external_files_dragged = false;
2037}
2038
2039extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -> BOOL {
2040    let window_state = unsafe { get_window_state(this) };
2041    let position = drag_event_position(&window_state, dragging_info);
2042    send_new_event(
2043        &window_state,
2044        PlatformInput::FileDrop(FileDropEvent::Submit { position }),
2045    )
2046    .to_objc()
2047}
2048
2049fn external_paths_from_event(dragging_info: *mut Object) -> Option<ExternalPaths> {
2050    let mut paths = SmallVec::new();
2051    let pasteboard: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
2052    let filenames = unsafe { NSPasteboard::propertyListForType(pasteboard, NSFilenamesPboardType) };
2053    if filenames == nil {
2054        return None;
2055    }
2056    for file in unsafe { filenames.iter() } {
2057        let path = unsafe {
2058            let f = NSString::UTF8String(file);
2059            CStr::from_ptr(f).to_string_lossy().into_owned()
2060        };
2061        paths.push(PathBuf::from(path))
2062    }
2063    Some(ExternalPaths(paths))
2064}
2065
2066extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
2067    let window_state = unsafe { get_window_state(this) };
2068    send_new_event(
2069        &window_state,
2070        PlatformInput::FileDrop(FileDropEvent::Exited),
2071    );
2072}
2073
2074async fn synthetic_drag(
2075    window_state: Weak<Mutex<MacWindowState>>,
2076    drag_id: usize,
2077    event: MouseMoveEvent,
2078) {
2079    loop {
2080        Timer::after(Duration::from_millis(16)).await;
2081        if let Some(window_state) = window_state.upgrade() {
2082            let mut lock = window_state.lock();
2083            if lock.synthetic_drag_counter == drag_id {
2084                if let Some(mut callback) = lock.event_callback.take() {
2085                    drop(lock);
2086                    callback(PlatformInput::MouseMove(event.clone()));
2087                    window_state.lock().event_callback = Some(callback);
2088                }
2089            } else {
2090                break;
2091            }
2092        }
2093    }
2094}
2095
2096fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: PlatformInput) -> bool {
2097    let window_state = window_state_lock.lock().event_callback.take();
2098    if let Some(mut callback) = window_state {
2099        callback(e);
2100        window_state_lock.lock().event_callback = Some(callback);
2101        true
2102    } else {
2103        false
2104    }
2105}
2106
2107fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id) -> Point<Pixels> {
2108    let drag_location: NSPoint = unsafe { msg_send![dragging_info, draggingLocation] };
2109    convert_mouse_position(drag_location, window_state.lock().content_size().height)
2110}
2111
2112fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
2113where
2114    F: FnOnce(&mut PlatformInputHandler) -> R,
2115{
2116    let window_state = unsafe { get_window_state(window) };
2117    let mut lock = window_state.as_ref().lock();
2118    if let Some(mut input_handler) = lock.input_handler.take() {
2119        drop(lock);
2120        let result = f(&mut input_handler);
2121        window_state.lock().input_handler = Some(input_handler);
2122        Some(result)
2123    } else {
2124        None
2125    }
2126}
2127
2128unsafe fn display_id_for_screen(screen: id) -> CGDirectDisplayID {
2129    unsafe {
2130        let device_description = NSScreen::deviceDescription(screen);
2131        let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
2132        let screen_number = device_description.objectForKey_(screen_number_key);
2133        let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
2134        screen_number as CGDirectDisplayID
2135    }
2136}