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, WindowKind, WindowParams,
   8    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_appearance_changed(&self, callback: Box<dyn FnMut()>) {
1150        self.0.lock().appearance_changed_callback = Some(callback);
1151    }
1152
1153    fn draw(&self, scene: &crate::Scene) {
1154        let mut this = self.0.lock();
1155        this.renderer.draw(scene);
1156    }
1157
1158    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
1159        self.0.lock().renderer.sprite_atlas().clone()
1160    }
1161
1162    fn gpu_specs(&self) -> Option<crate::GpuSpecs> {
1163        None
1164    }
1165
1166    fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
1167        let executor = self.0.lock().executor.clone();
1168        executor
1169            .spawn(async move {
1170                unsafe {
1171                    let input_context: id =
1172                        msg_send![class!(NSTextInputContext), currentInputContext];
1173                    if input_context.is_null() {
1174                        return;
1175                    }
1176                    let _: () = msg_send![input_context, invalidateCharacterCoordinates];
1177                }
1178            })
1179            .detach()
1180    }
1181
1182    fn titlebar_double_click(&self) {
1183        let this = self.0.lock();
1184        let window = this.native_window;
1185        this.executor
1186            .spawn(async move {
1187                unsafe {
1188                    let defaults: id = NSUserDefaults::standardUserDefaults();
1189                    let domain = NSString::alloc(nil).init_str("NSGlobalDomain");
1190                    let key = NSString::alloc(nil).init_str("AppleActionOnDoubleClick");
1191
1192                    let dict: id = msg_send![defaults, persistentDomainForName: domain];
1193                    let action: id = if !dict.is_null() {
1194                        msg_send![dict, objectForKey: key]
1195                    } else {
1196                        nil
1197                    };
1198
1199                    let action_str = if !action.is_null() {
1200                        CStr::from_ptr(NSString::UTF8String(action)).to_string_lossy()
1201                    } else {
1202                        "".into()
1203                    };
1204
1205                    match action_str.as_ref() {
1206                        "Minimize" => {
1207                            window.miniaturize_(nil);
1208                        }
1209                        "Maximize" => {
1210                            window.zoom_(nil);
1211                        }
1212                        "Fill" => {
1213                            // There is no documented API for "Fill" action, so we'll just zoom the window
1214                            window.zoom_(nil);
1215                        }
1216                        _ => {
1217                            window.zoom_(nil);
1218                        }
1219                    }
1220                }
1221            })
1222            .detach();
1223    }
1224}
1225
1226impl rwh::HasWindowHandle for MacWindow {
1227    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
1228        // SAFETY: The AppKitWindowHandle is a wrapper around a pointer to an NSView
1229        unsafe {
1230            Ok(rwh::WindowHandle::borrow_raw(rwh::RawWindowHandle::AppKit(
1231                rwh::AppKitWindowHandle::new(self.0.lock().native_view.cast()),
1232            )))
1233        }
1234    }
1235}
1236
1237impl rwh::HasDisplayHandle for MacWindow {
1238    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
1239        // SAFETY: This is a no-op on macOS
1240        unsafe {
1241            Ok(rwh::DisplayHandle::borrow_raw(
1242                rwh::AppKitDisplayHandle::new().into(),
1243            ))
1244        }
1245    }
1246}
1247
1248fn get_scale_factor(native_window: id) -> f32 {
1249    let factor = unsafe {
1250        let screen: id = msg_send![native_window, screen];
1251        if screen.is_null() {
1252            return 1.0;
1253        }
1254        NSScreen::backingScaleFactor(screen) as f32
1255    };
1256
1257    // We are not certain what triggers this, but it seems that sometimes
1258    // this method would return 0 (https://github.com/zed-industries/zed/issues/6412)
1259    // It seems most likely that this would happen if the window has no screen
1260    // (if it is off-screen), though we'd expect to see viewDidChangeBackingProperties before
1261    // it was rendered for real.
1262    // Regardless, attempt to avoid the issue here.
1263    if factor == 0.0 { 2. } else { factor }
1264}
1265
1266unsafe fn get_window_state(object: &Object) -> Arc<Mutex<MacWindowState>> {
1267    unsafe {
1268        let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1269        let rc1 = Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1270        let rc2 = rc1.clone();
1271        mem::forget(rc1);
1272        rc2
1273    }
1274}
1275
1276unsafe fn drop_window_state(object: &Object) {
1277    unsafe {
1278        let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
1279        Arc::from_raw(raw as *mut Mutex<MacWindowState>);
1280    }
1281}
1282
1283extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
1284    YES
1285}
1286
1287extern "C" fn dealloc_window(this: &Object, _: Sel) {
1288    unsafe {
1289        drop_window_state(this);
1290        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
1291    }
1292}
1293
1294extern "C" fn dealloc_view(this: &Object, _: Sel) {
1295    unsafe {
1296        drop_window_state(this);
1297        let _: () = msg_send![super(this, class!(NSView)), dealloc];
1298    }
1299}
1300
1301extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
1302    handle_key_event(this, native_event, true)
1303}
1304
1305extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
1306    handle_key_event(this, native_event, false);
1307}
1308
1309extern "C" fn handle_key_up(this: &Object, _: Sel, native_event: id) {
1310    handle_key_event(this, native_event, false);
1311}
1312
1313// Things to test if you're modifying this method:
1314//  U.S. layout:
1315//   - The IME consumes characters like 'j' and 'k', which makes paging through `less` in
1316//     the terminal behave incorrectly by default. This behavior should be patched by our
1317//     IME integration
1318//   - `alt-t` should open the tasks menu
1319//   - In vim mode, this keybinding should work:
1320//     ```
1321//        {
1322//          "context": "Editor && vim_mode == insert",
1323//          "bindings": {"j j": "vim::NormalBefore"}
1324//        }
1325//     ```
1326//     and typing 'j k' in insert mode with this keybinding should insert the two characters
1327//  Brazilian layout:
1328//   - `" space` should create an unmarked quote
1329//   - `" backspace` should delete the marked quote
1330//   - `" "`should create an unmarked quote and a second marked quote
1331//   - `" up` should insert a quote, unmark it, and move up one line
1332//   - `" cmd-down` should insert a quote, unmark it, and move to the end of the file
1333//   - `cmd-ctrl-space` and clicking on an emoji should type it
1334//  Czech (QWERTY) layout:
1335//   - in vim mode `option-4`  should go to end of line (same as $)
1336//  Japanese (Romaji) layout:
1337//   - type `a i left down up enter enter` should create an unmarked text "愛"
1338extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
1339    let window_state = unsafe { get_window_state(this) };
1340    let mut lock = window_state.as_ref().lock();
1341
1342    let window_height = lock.content_size().height;
1343    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1344
1345    let Some(event) = event else {
1346        return NO;
1347    };
1348
1349    let run_callback = |event: PlatformInput| -> BOOL {
1350        let mut callback = window_state.as_ref().lock().event_callback.take();
1351        let handled: BOOL = if let Some(callback) = callback.as_mut() {
1352            !callback(event).propagate as BOOL
1353        } else {
1354            NO
1355        };
1356        window_state.as_ref().lock().event_callback = callback;
1357        handled
1358    };
1359
1360    match event {
1361        PlatformInput::KeyDown(mut key_down_event) => {
1362            // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
1363            // If that event isn't handled, it will then dispatch a "key down" event. GPUI
1364            // makes no distinction between these two types of events, so we need to ignore
1365            // the "key down" event if we've already just processed its "key equivalent" version.
1366            if key_equivalent {
1367                lock.last_key_equivalent = Some(key_down_event.clone());
1368            } else if lock.last_key_equivalent.take().as_ref() == Some(&key_down_event) {
1369                return NO;
1370            }
1371
1372            drop(lock);
1373
1374            let is_composing =
1375                with_input_handler(this, |input_handler| input_handler.marked_text_range())
1376                    .flatten()
1377                    .is_some();
1378
1379            // If we're composing, send the key to the input handler first;
1380            // otherwise we only send to the input handler if we don't have a matching binding.
1381            // The input handler may call `do_command_by_selector` if it doesn't know how to handle
1382            // a key. If it does so, it will return YES so we won't send the key twice.
1383            // We also do this for non-printing keys (like arrow keys and escape) as the IME menu
1384            // may need them even if there is no marked text;
1385            // however we skip keys with control or the input handler adds control-characters to the buffer.
1386            // and keys with function, as the input handler swallows them.
1387            if is_composing
1388                || (key_down_event.keystroke.key_char.is_none()
1389                    && !key_down_event.keystroke.modifiers.control
1390                    && !key_down_event.keystroke.modifiers.function)
1391            {
1392                {
1393                    let mut lock = window_state.as_ref().lock();
1394                    lock.keystroke_for_do_command = Some(key_down_event.keystroke.clone());
1395                    lock.do_command_handled.take();
1396                    drop(lock);
1397                }
1398
1399                let handled: BOOL = unsafe {
1400                    let input_context: id = msg_send![this, inputContext];
1401                    msg_send![input_context, handleEvent: native_event]
1402                };
1403                window_state.as_ref().lock().keystroke_for_do_command.take();
1404                if let Some(handled) = window_state.as_ref().lock().do_command_handled.take() {
1405                    return handled as BOOL;
1406                } else if handled == YES {
1407                    return YES;
1408                }
1409
1410                let handled = run_callback(PlatformInput::KeyDown(key_down_event));
1411                return handled;
1412            }
1413
1414            let handled = run_callback(PlatformInput::KeyDown(key_down_event.clone()));
1415            if handled == YES {
1416                return YES;
1417            }
1418
1419            if key_down_event.is_held {
1420                if let Some(key_char) = key_down_event.keystroke.key_char.as_ref() {
1421                    let handled = with_input_handler(&this, |input_handler| {
1422                        if !input_handler.apple_press_and_hold_enabled() {
1423                            input_handler.replace_text_in_range(None, &key_char);
1424                            return YES;
1425                        }
1426                        NO
1427                    });
1428                    if handled == Some(YES) {
1429                        return YES;
1430                    }
1431                }
1432            }
1433
1434            // Don't send key equivalents to the input handler,
1435            // or macOS shortcuts like cmd-` will stop working.
1436            if key_equivalent {
1437                return NO;
1438            }
1439
1440            unsafe {
1441                let input_context: id = msg_send![this, inputContext];
1442                msg_send![input_context, handleEvent: native_event]
1443            }
1444        }
1445
1446        PlatformInput::KeyUp(_) => {
1447            drop(lock);
1448            run_callback(event)
1449        }
1450
1451        _ => NO,
1452    }
1453}
1454
1455extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
1456    let window_state = unsafe { get_window_state(this) };
1457    let weak_window_state = Arc::downgrade(&window_state);
1458    let mut lock = window_state.as_ref().lock();
1459    let window_height = lock.content_size().height;
1460    let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
1461
1462    if let Some(mut event) = event {
1463        match &mut event {
1464            PlatformInput::MouseDown(
1465                event @ MouseDownEvent {
1466                    button: MouseButton::Left,
1467                    modifiers: Modifiers { control: true, .. },
1468                    ..
1469                },
1470            ) => {
1471                // On mac, a ctrl-left click should be handled as a right click.
1472                *event = MouseDownEvent {
1473                    button: MouseButton::Right,
1474                    modifiers: Modifiers {
1475                        control: false,
1476                        ..event.modifiers
1477                    },
1478                    click_count: 1,
1479                    ..*event
1480                };
1481            }
1482
1483            // Handles focusing click.
1484            PlatformInput::MouseDown(
1485                event @ MouseDownEvent {
1486                    button: MouseButton::Left,
1487                    ..
1488                },
1489            ) if (lock.first_mouse) => {
1490                *event = MouseDownEvent {
1491                    first_mouse: true,
1492                    ..*event
1493                };
1494                lock.first_mouse = false;
1495            }
1496
1497            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
1498            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
1499            // user is still holding ctrl when releasing the left mouse button
1500            PlatformInput::MouseUp(
1501                event @ MouseUpEvent {
1502                    button: MouseButton::Left,
1503                    modifiers: Modifiers { control: true, .. },
1504                    ..
1505                },
1506            ) => {
1507                *event = MouseUpEvent {
1508                    button: MouseButton::Right,
1509                    modifiers: Modifiers {
1510                        control: false,
1511                        ..event.modifiers
1512                    },
1513                    click_count: 1,
1514                    ..*event
1515                };
1516            }
1517
1518            _ => {}
1519        };
1520
1521        match &event {
1522            PlatformInput::MouseDown(_) => {
1523                drop(lock);
1524                unsafe {
1525                    let input_context: id = msg_send![this, inputContext];
1526                    msg_send![input_context, handleEvent: native_event]
1527                }
1528                lock = window_state.as_ref().lock();
1529            }
1530            PlatformInput::MouseMove(
1531                event @ MouseMoveEvent {
1532                    pressed_button: Some(_),
1533                    ..
1534                },
1535            ) => {
1536                // Synthetic drag is used for selecting long buffer contents while buffer is being scrolled.
1537                // External file drag and drop is able to emit its own synthetic mouse events which will conflict
1538                // with these ones.
1539                if !lock.external_files_dragged {
1540                    lock.synthetic_drag_counter += 1;
1541                    let executor = lock.executor.clone();
1542                    executor
1543                        .spawn(synthetic_drag(
1544                            weak_window_state,
1545                            lock.synthetic_drag_counter,
1546                            event.clone(),
1547                        ))
1548                        .detach();
1549                }
1550            }
1551
1552            PlatformInput::MouseUp(MouseUpEvent { .. }) => {
1553                lock.synthetic_drag_counter += 1;
1554            }
1555
1556            PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1557                // Only raise modifiers changed event when they have actually changed
1558                if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1559                    modifiers: prev_modifiers,
1560                })) = &lock.previous_modifiers_changed_event
1561                {
1562                    if prev_modifiers == modifiers {
1563                        return;
1564                    }
1565                }
1566
1567                lock.previous_modifiers_changed_event = Some(event.clone());
1568            }
1569
1570            _ => {}
1571        }
1572
1573        if let Some(mut callback) = lock.event_callback.take() {
1574            drop(lock);
1575            callback(event);
1576            window_state.lock().event_callback = Some(callback);
1577        }
1578    }
1579}
1580
1581extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
1582    let window_state = unsafe { get_window_state(this) };
1583    let lock = &mut *window_state.lock();
1584    unsafe {
1585        if lock
1586            .native_window
1587            .occlusionState()
1588            .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible)
1589        {
1590            lock.start_display_link();
1591        } else {
1592            lock.stop_display_link();
1593        }
1594    }
1595}
1596
1597extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1598    let window_state = unsafe { get_window_state(this) };
1599    window_state.as_ref().lock().move_traffic_light();
1600}
1601
1602extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1603    let window_state = unsafe { get_window_state(this) };
1604    let mut lock = window_state.as_ref().lock();
1605    lock.fullscreen_restore_bounds = lock.bounds();
1606
1607    let min_version = NSOperatingSystemVersion::new(15, 3, 0);
1608
1609    if is_macos_version_at_least(min_version) {
1610        unsafe {
1611            lock.native_window.setTitlebarAppearsTransparent_(NO);
1612        }
1613    }
1614}
1615
1616extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1617    let window_state = unsafe { get_window_state(this) };
1618    let mut lock = window_state.as_ref().lock();
1619
1620    let min_version = NSOperatingSystemVersion::new(15, 3, 0);
1621
1622    if is_macos_version_at_least(min_version) && lock.transparent_titlebar {
1623        unsafe {
1624            lock.native_window.setTitlebarAppearsTransparent_(YES);
1625        }
1626    }
1627}
1628
1629pub(crate) fn is_macos_version_at_least(version: NSOperatingSystemVersion) -> bool {
1630    unsafe { NSProcessInfo::processInfo(nil).isOperatingSystemAtLeastVersion(version) }
1631}
1632
1633extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
1634    let window_state = unsafe { get_window_state(this) };
1635    let mut lock = window_state.as_ref().lock();
1636    if let Some(mut callback) = lock.moved_callback.take() {
1637        drop(lock);
1638        callback();
1639        window_state.lock().moved_callback = Some(callback);
1640    }
1641}
1642
1643extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
1644    let window_state = unsafe { get_window_state(this) };
1645    let mut lock = window_state.as_ref().lock();
1646    lock.start_display_link();
1647}
1648
1649extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1650    let window_state = unsafe { get_window_state(this) };
1651    let lock = window_state.lock();
1652    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
1653
1654    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1655    // `windowDidBecomeKey` message to the previous key window even though that window
1656    // isn't actually key. This causes a bug if the application is later activated while
1657    // the pop-up is still open, making it impossible to activate the previous key window
1658    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1659    // the app and re-activate it, which is a pretty bad UX.
1660    // The following code detects the spurious event and invokes `resignKeyWindow`:
1661    // in theory, we're not supposed to invoke this method manually but it balances out
1662    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1663    if selector == sel!(windowDidBecomeKey:) && !is_active {
1664        unsafe {
1665            let _: () = msg_send![lock.native_window, resignKeyWindow];
1666            return;
1667        }
1668    }
1669
1670    let executor = lock.executor.clone();
1671    drop(lock);
1672    executor
1673        .spawn(async move {
1674            let mut lock = window_state.as_ref().lock();
1675            if let Some(mut callback) = lock.activate_callback.take() {
1676                drop(lock);
1677                callback(is_active);
1678                window_state.lock().activate_callback = Some(callback);
1679            };
1680        })
1681        .detach();
1682}
1683
1684extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1685    let window_state = unsafe { get_window_state(this) };
1686    let mut lock = window_state.as_ref().lock();
1687    if let Some(mut callback) = lock.should_close_callback.take() {
1688        drop(lock);
1689        let should_close = callback();
1690        window_state.lock().should_close_callback = Some(callback);
1691        should_close as BOOL
1692    } else {
1693        YES
1694    }
1695}
1696
1697extern "C" fn close_window(this: &Object, _: Sel) {
1698    unsafe {
1699        let close_callback = {
1700            let window_state = get_window_state(this);
1701            let mut lock = window_state.as_ref().lock();
1702            lock.close_callback.take()
1703        };
1704
1705        if let Some(callback) = close_callback {
1706            callback();
1707        }
1708
1709        let _: () = msg_send![super(this, class!(NSWindow)), close];
1710    }
1711}
1712
1713extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1714    let window_state = unsafe { get_window_state(this) };
1715    let window_state = window_state.as_ref().lock();
1716    window_state.renderer.layer_ptr() as id
1717}
1718
1719extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1720    let window_state = unsafe { get_window_state(this) };
1721    let mut lock = window_state.as_ref().lock();
1722
1723    let scale_factor = lock.scale_factor();
1724    let size = lock.content_size();
1725    let drawable_size = size.to_device_pixels(scale_factor);
1726    unsafe {
1727        let _: () = msg_send![
1728            lock.renderer.layer(),
1729            setContentsScale: scale_factor as f64
1730        ];
1731    }
1732
1733    lock.renderer.update_drawable_size(drawable_size);
1734
1735    if let Some(mut callback) = lock.resize_callback.take() {
1736        let content_size = lock.content_size();
1737        let scale_factor = lock.scale_factor();
1738        drop(lock);
1739        callback(content_size, scale_factor);
1740        window_state.as_ref().lock().resize_callback = Some(callback);
1741    };
1742}
1743
1744extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1745    let window_state = unsafe { get_window_state(this) };
1746    let mut lock = window_state.as_ref().lock();
1747
1748    let new_size = Size::<Pixels>::from(size);
1749    if lock.content_size() == new_size {
1750        return;
1751    }
1752
1753    unsafe {
1754        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1755    }
1756
1757    let scale_factor = lock.scale_factor();
1758    let drawable_size = new_size.to_device_pixels(scale_factor);
1759    lock.renderer.update_drawable_size(drawable_size);
1760
1761    if let Some(mut callback) = lock.resize_callback.take() {
1762        let content_size = lock.content_size();
1763        let scale_factor = lock.scale_factor();
1764        drop(lock);
1765        callback(content_size, scale_factor);
1766        window_state.lock().resize_callback = Some(callback);
1767    };
1768}
1769
1770extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1771    let window_state = unsafe { get_window_state(this) };
1772    let mut lock = window_state.lock();
1773    if let Some(mut callback) = lock.request_frame_callback.take() {
1774        #[cfg(not(feature = "macos-blade"))]
1775        lock.renderer.set_presents_with_transaction(true);
1776        lock.stop_display_link();
1777        drop(lock);
1778        callback(Default::default());
1779
1780        let mut lock = window_state.lock();
1781        lock.request_frame_callback = Some(callback);
1782        #[cfg(not(feature = "macos-blade"))]
1783        lock.renderer.set_presents_with_transaction(false);
1784        lock.start_display_link();
1785    }
1786}
1787
1788unsafe extern "C" fn step(view: *mut c_void) {
1789    let view = view as id;
1790    let window_state = unsafe { get_window_state(&*view) };
1791    let mut lock = window_state.lock();
1792
1793    if let Some(mut callback) = lock.request_frame_callback.take() {
1794        drop(lock);
1795        callback(Default::default());
1796        window_state.lock().request_frame_callback = Some(callback);
1797    }
1798}
1799
1800extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1801    unsafe { msg_send![class!(NSArray), array] }
1802}
1803
1804extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1805    let has_marked_text_result =
1806        with_input_handler(this, |input_handler| input_handler.marked_text_range()).flatten();
1807
1808    has_marked_text_result.is_some() as BOOL
1809}
1810
1811extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1812    let marked_range_result =
1813        with_input_handler(this, |input_handler| input_handler.marked_text_range()).flatten();
1814
1815    marked_range_result.map_or(NSRange::invalid(), |range| range.into())
1816}
1817
1818extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1819    let selected_range_result = with_input_handler(this, |input_handler| {
1820        input_handler.selected_text_range(false)
1821    })
1822    .flatten();
1823
1824    selected_range_result.map_or(NSRange::invalid(), |selection| selection.range.into())
1825}
1826
1827extern "C" fn first_rect_for_character_range(
1828    this: &Object,
1829    _: Sel,
1830    range: NSRange,
1831    _: id,
1832) -> NSRect {
1833    let frame = get_frame(this);
1834    with_input_handler(this, |input_handler| {
1835        input_handler.bounds_for_range(range.to_range()?)
1836    })
1837    .flatten()
1838    .map_or(
1839        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1840        |bounds| {
1841            NSRect::new(
1842                NSPoint::new(
1843                    frame.origin.x + bounds.origin.x.0 as f64,
1844                    frame.origin.y + frame.size.height
1845                        - bounds.origin.y.0 as f64
1846                        - bounds.size.height.0 as f64,
1847                ),
1848                NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
1849            )
1850        },
1851    )
1852}
1853
1854fn get_frame(this: &Object) -> NSRect {
1855    unsafe {
1856        let state = get_window_state(this);
1857        let lock = state.lock();
1858        let mut frame = NSWindow::frame(lock.native_window);
1859        let content_layout_rect: CGRect = msg_send![lock.native_window, contentLayoutRect];
1860        let style_mask: NSWindowStyleMask = msg_send![lock.native_window, styleMask];
1861        if !style_mask.contains(NSWindowStyleMask::NSFullSizeContentViewWindowMask) {
1862            frame.origin.y -= frame.size.height - content_layout_rect.size.height;
1863        }
1864        frame
1865    }
1866}
1867
1868extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1869    unsafe {
1870        let is_attributed_string: BOOL =
1871            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1872        let text: id = if is_attributed_string == YES {
1873            msg_send![text, string]
1874        } else {
1875            text
1876        };
1877
1878        let text = text.to_str();
1879        let replacement_range = replacement_range.to_range();
1880        with_input_handler(this, |input_handler| {
1881            input_handler.replace_text_in_range(replacement_range, &text)
1882        });
1883    }
1884}
1885
1886extern "C" fn set_marked_text(
1887    this: &Object,
1888    _: Sel,
1889    text: id,
1890    selected_range: NSRange,
1891    replacement_range: NSRange,
1892) {
1893    unsafe {
1894        let is_attributed_string: BOOL =
1895            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1896        let text: id = if is_attributed_string == YES {
1897            msg_send![text, string]
1898        } else {
1899            text
1900        };
1901        let selected_range = selected_range.to_range();
1902        let replacement_range = replacement_range.to_range();
1903        let text = text.to_str();
1904        with_input_handler(this, |input_handler| {
1905            input_handler.replace_and_mark_text_in_range(replacement_range, &text, selected_range)
1906        });
1907    }
1908}
1909extern "C" fn unmark_text(this: &Object, _: Sel) {
1910    with_input_handler(this, |input_handler| input_handler.unmark_text());
1911}
1912
1913extern "C" fn attributed_substring_for_proposed_range(
1914    this: &Object,
1915    _: Sel,
1916    range: NSRange,
1917    actual_range: *mut c_void,
1918) -> id {
1919    with_input_handler(this, |input_handler| {
1920        let range = range.to_range()?;
1921        if range.is_empty() {
1922            return None;
1923        }
1924        let mut adjusted: Option<Range<usize>> = None;
1925
1926        let selected_text = input_handler.text_for_range(range.clone(), &mut adjusted)?;
1927        if let Some(adjusted) = adjusted {
1928            if adjusted != range {
1929                unsafe { (actual_range as *mut NSRange).write(NSRange::from(adjusted)) };
1930            }
1931        }
1932        unsafe {
1933            let string: id = msg_send![class!(NSAttributedString), alloc];
1934            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1935            Some(string)
1936        }
1937    })
1938    .flatten()
1939    .unwrap_or(nil)
1940}
1941
1942// We ignore which selector it asks us to do because the user may have
1943// bound the shortcut to something else.
1944extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1945    let state = unsafe { get_window_state(this) };
1946    let mut lock = state.as_ref().lock();
1947    let keystroke = lock.keystroke_for_do_command.take();
1948    let mut event_callback = lock.event_callback.take();
1949    drop(lock);
1950
1951    if let Some((keystroke, mut callback)) = keystroke.zip(event_callback.as_mut()) {
1952        let handled = (callback)(PlatformInput::KeyDown(KeyDownEvent {
1953            keystroke,
1954            is_held: false,
1955        }));
1956        state.as_ref().lock().do_command_handled = Some(!handled.propagate);
1957    }
1958
1959    state.as_ref().lock().event_callback = event_callback;
1960}
1961
1962extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1963    unsafe {
1964        let state = get_window_state(this);
1965        let mut lock = state.as_ref().lock();
1966        if let Some(mut callback) = lock.appearance_changed_callback.take() {
1967            drop(lock);
1968            callback();
1969            state.lock().appearance_changed_callback = Some(callback);
1970        }
1971    }
1972}
1973
1974extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1975    let window_state = unsafe { get_window_state(this) };
1976    let mut lock = window_state.as_ref().lock();
1977    lock.first_mouse = true;
1978    YES
1979}
1980
1981extern "C" fn character_index_for_point(this: &Object, _: Sel, position: NSPoint) -> u64 {
1982    let position = screen_point_to_gpui_point(this, position);
1983    with_input_handler(this, |input_handler| {
1984        input_handler.character_index_for_point(position)
1985    })
1986    .flatten()
1987    .map(|index| index as u64)
1988    .unwrap_or(NSNotFound as u64)
1989}
1990
1991fn screen_point_to_gpui_point(this: &Object, position: NSPoint) -> Point<Pixels> {
1992    let frame = get_frame(this);
1993    let window_x = position.x - frame.origin.x;
1994    let window_y = frame.size.height - (position.y - frame.origin.y);
1995    let position = point(px(window_x as f32), px(window_y as f32));
1996    position
1997}
1998
1999extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
2000    let window_state = unsafe { get_window_state(this) };
2001    let position = drag_event_position(&window_state, dragging_info);
2002    let paths = external_paths_from_event(dragging_info);
2003    if let Some(event) =
2004        paths.map(|paths| PlatformInput::FileDrop(FileDropEvent::Entered { position, paths }))
2005    {
2006        if send_new_event(&window_state, event) {
2007            window_state.lock().external_files_dragged = true;
2008            return NSDragOperationCopy;
2009        }
2010    }
2011    NSDragOperationNone
2012}
2013
2014extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDragOperation {
2015    let window_state = unsafe { get_window_state(this) };
2016    let position = drag_event_position(&window_state, dragging_info);
2017    if send_new_event(
2018        &window_state,
2019        PlatformInput::FileDrop(FileDropEvent::Pending { position }),
2020    ) {
2021        NSDragOperationCopy
2022    } else {
2023        NSDragOperationNone
2024    }
2025}
2026
2027extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
2028    let window_state = unsafe { get_window_state(this) };
2029    send_new_event(
2030        &window_state,
2031        PlatformInput::FileDrop(FileDropEvent::Exited),
2032    );
2033    window_state.lock().external_files_dragged = false;
2034}
2035
2036extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -> BOOL {
2037    let window_state = unsafe { get_window_state(this) };
2038    let position = drag_event_position(&window_state, dragging_info);
2039    send_new_event(
2040        &window_state,
2041        PlatformInput::FileDrop(FileDropEvent::Submit { position }),
2042    )
2043    .to_objc()
2044}
2045
2046fn external_paths_from_event(dragging_info: *mut Object) -> Option<ExternalPaths> {
2047    let mut paths = SmallVec::new();
2048    let pasteboard: id = unsafe { msg_send![dragging_info, draggingPasteboard] };
2049    let filenames = unsafe { NSPasteboard::propertyListForType(pasteboard, NSFilenamesPboardType) };
2050    if filenames == nil {
2051        return None;
2052    }
2053    for file in unsafe { filenames.iter() } {
2054        let path = unsafe {
2055            let f = NSString::UTF8String(file);
2056            CStr::from_ptr(f).to_string_lossy().into_owned()
2057        };
2058        paths.push(PathBuf::from(path))
2059    }
2060    Some(ExternalPaths(paths))
2061}
2062
2063extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
2064    let window_state = unsafe { get_window_state(this) };
2065    send_new_event(
2066        &window_state,
2067        PlatformInput::FileDrop(FileDropEvent::Exited),
2068    );
2069}
2070
2071async fn synthetic_drag(
2072    window_state: Weak<Mutex<MacWindowState>>,
2073    drag_id: usize,
2074    event: MouseMoveEvent,
2075) {
2076    loop {
2077        Timer::after(Duration::from_millis(16)).await;
2078        if let Some(window_state) = window_state.upgrade() {
2079            let mut lock = window_state.lock();
2080            if lock.synthetic_drag_counter == drag_id {
2081                if let Some(mut callback) = lock.event_callback.take() {
2082                    drop(lock);
2083                    callback(PlatformInput::MouseMove(event.clone()));
2084                    window_state.lock().event_callback = Some(callback);
2085                }
2086            } else {
2087                break;
2088            }
2089        }
2090    }
2091}
2092
2093fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: PlatformInput) -> bool {
2094    let window_state = window_state_lock.lock().event_callback.take();
2095    if let Some(mut callback) = window_state {
2096        callback(e);
2097        window_state_lock.lock().event_callback = Some(callback);
2098        true
2099    } else {
2100        false
2101    }
2102}
2103
2104fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id) -> Point<Pixels> {
2105    let drag_location: NSPoint = unsafe { msg_send![dragging_info, draggingLocation] };
2106    convert_mouse_position(drag_location, window_state.lock().content_size().height)
2107}
2108
2109fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
2110where
2111    F: FnOnce(&mut PlatformInputHandler) -> R,
2112{
2113    let window_state = unsafe { get_window_state(window) };
2114    let mut lock = window_state.as_ref().lock();
2115    if let Some(mut input_handler) = lock.input_handler.take() {
2116        drop(lock);
2117        let result = f(&mut input_handler);
2118        window_state.lock().input_handler = Some(input_handler);
2119        Some(result)
2120    } else {
2121        None
2122    }
2123}
2124
2125unsafe fn display_id_for_screen(screen: id) -> CGDirectDisplayID {
2126    unsafe {
2127        let device_description = NSScreen::deviceDescription(screen);
2128        let screen_number_key: id = NSString::alloc(nil).init_str("NSScreenNumber");
2129        let screen_number = device_description.objectForKey_(screen_number_key);
2130        let screen_number: NSUInteger = msg_send![screen_number, unsignedIntegerValue];
2131        screen_number as CGDirectDisplayID
2132    }
2133}