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