window.rs

   1use crate::{
   2    point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke,
   3    MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
   4    MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow,
   5    Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
   6    WindowPromptLevel,
   7};
   8use block::ConcreteBlock;
   9use cocoa::{
  10    appkit::{
  11        CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
  12        NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,
  13        NSWindowStyleMask, NSWindowTitleVisibility,
  14    },
  15    base::{id, nil},
  16    foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
  17};
  18use core_graphics::display::CGRect;
  19use ctor::ctor;
  20use futures::channel::oneshot;
  21use objc::{
  22    class,
  23    declare::ClassDecl,
  24    msg_send,
  25    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
  26    sel, sel_impl,
  27};
  28use parking_lot::Mutex;
  29use raw_window_handle::{
  30    AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle,
  31    RawDisplayHandle, RawWindowHandle,
  32};
  33use std::{
  34    any::Any,
  35    cell::{Cell, RefCell},
  36    ffi::{c_void, CStr},
  37    mem,
  38    ops::Range,
  39    os::raw::c_char,
  40    ptr,
  41    rc::Rc,
  42    sync::{Arc, Weak},
  43    time::Duration,
  44};
  45
  46use super::{ns_string, NSRange};
  47
  48const WINDOW_STATE_IVAR: &str = "windowState";
  49
  50static mut WINDOW_CLASS: *const Class = ptr::null();
  51static mut PANEL_CLASS: *const Class = ptr::null();
  52static mut VIEW_CLASS: *const Class = ptr::null();
  53
  54#[allow(non_upper_case_globals)]
  55const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =
  56    unsafe { NSWindowStyleMask::from_bits_unchecked(1 << 7) };
  57#[allow(non_upper_case_globals)]
  58const NSNormalWindowLevel: NSInteger = 0;
  59#[allow(non_upper_case_globals)]
  60const NSPopUpWindowLevel: NSInteger = 101;
  61#[allow(non_upper_case_globals)]
  62const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;
  63#[allow(non_upper_case_globals)]
  64const NSTrackingMouseMoved: NSUInteger = 0x02;
  65#[allow(non_upper_case_globals)]
  66const NSTrackingActiveAlways: NSUInteger = 0x80;
  67#[allow(non_upper_case_globals)]
  68const NSTrackingInVisibleRect: NSUInteger = 0x200;
  69#[allow(non_upper_case_globals)]
  70const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;
  71#[allow(non_upper_case_globals)]
  72const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
  73
  74#[ctor]
  75unsafe fn build_classes() {
  76    WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));
  77    PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));
  78    VIEW_CLASS = {
  79        let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
  80        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
  81
  82        decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
  83
  84        decl.add_method(
  85            sel!(performKeyEquivalent:),
  86            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
  87        );
  88        decl.add_method(
  89            sel!(keyDown:),
  90            handle_key_down as extern "C" fn(&Object, Sel, id),
  91        );
  92        decl.add_method(
  93            sel!(mouseDown:),
  94            handle_view_event as extern "C" fn(&Object, Sel, id),
  95        );
  96        decl.add_method(
  97            sel!(mouseUp:),
  98            handle_view_event as extern "C" fn(&Object, Sel, id),
  99        );
 100        decl.add_method(
 101            sel!(rightMouseDown:),
 102            handle_view_event as extern "C" fn(&Object, Sel, id),
 103        );
 104        decl.add_method(
 105            sel!(rightMouseUp:),
 106            handle_view_event as extern "C" fn(&Object, Sel, id),
 107        );
 108        decl.add_method(
 109            sel!(otherMouseDown:),
 110            handle_view_event as extern "C" fn(&Object, Sel, id),
 111        );
 112        decl.add_method(
 113            sel!(otherMouseUp:),
 114            handle_view_event as extern "C" fn(&Object, Sel, id),
 115        );
 116        decl.add_method(
 117            sel!(mouseMoved:),
 118            handle_view_event as extern "C" fn(&Object, Sel, id),
 119        );
 120        decl.add_method(
 121            sel!(mouseExited:),
 122            handle_view_event as extern "C" fn(&Object, Sel, id),
 123        );
 124        decl.add_method(
 125            sel!(mouseDragged:),
 126            handle_view_event as extern "C" fn(&Object, Sel, id),
 127        );
 128        decl.add_method(
 129            sel!(scrollWheel:),
 130            handle_view_event as extern "C" fn(&Object, Sel, id),
 131        );
 132        decl.add_method(
 133            sel!(flagsChanged:),
 134            handle_view_event as extern "C" fn(&Object, Sel, id),
 135        );
 136        decl.add_method(
 137            sel!(cancelOperation:),
 138            cancel_operation as extern "C" fn(&Object, Sel, id),
 139        );
 140
 141        // decl.add_method(
 142        //     sel!(makeBackingLayer),
 143        //     make_backing_layer as extern "C" fn(&Object, Sel) -> id,
 144        // );
 145
 146        decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
 147        decl.add_method(
 148            sel!(viewDidChangeBackingProperties),
 149            view_did_change_backing_properties as extern "C" fn(&Object, Sel),
 150        );
 151        decl.add_method(
 152            sel!(setFrameSize:),
 153            set_frame_size as extern "C" fn(&Object, Sel, NSSize),
 154        );
 155        decl.add_method(
 156            sel!(displayLayer:),
 157            display_layer as extern "C" fn(&Object, Sel, id),
 158        );
 159
 160        decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
 161        decl.add_method(
 162            sel!(validAttributesForMarkedText),
 163            valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
 164        );
 165        decl.add_method(
 166            sel!(hasMarkedText),
 167            has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
 168        );
 169        decl.add_method(
 170            sel!(markedRange),
 171            marked_range as extern "C" fn(&Object, Sel) -> NSRange,
 172        );
 173        decl.add_method(
 174            sel!(selectedRange),
 175            selected_range as extern "C" fn(&Object, Sel) -> NSRange,
 176        );
 177        decl.add_method(
 178            sel!(firstRectForCharacterRange:actualRange:),
 179            first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
 180        );
 181        decl.add_method(
 182            sel!(insertText:replacementRange:),
 183            insert_text as extern "C" fn(&Object, Sel, id, NSRange),
 184        );
 185        decl.add_method(
 186            sel!(setMarkedText:selectedRange:replacementRange:),
 187            set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
 188        );
 189        decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
 190        decl.add_method(
 191            sel!(attributedSubstringForProposedRange:actualRange:),
 192            attributed_substring_for_proposed_range
 193                as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
 194        );
 195        decl.add_method(
 196            sel!(viewDidChangeEffectiveAppearance),
 197            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
 198        );
 199
 200        // Suppress beep on keystrokes with modifier keys.
 201        decl.add_method(
 202            sel!(doCommandBySelector:),
 203            do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
 204        );
 205
 206        decl.add_method(
 207            sel!(acceptsFirstMouse:),
 208            accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,
 209        );
 210
 211        decl.register()
 212    };
 213}
 214
 215pub fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {
 216    point(
 217        px(position.x as f32),
 218        // MacOS screen coordinates are relative to bottom left
 219        window_height - px(position.y as f32),
 220    )
 221}
 222
 223unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
 224    let mut decl = ClassDecl::new(name, superclass).unwrap();
 225    decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 226    decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
 227    decl.add_method(
 228        sel!(canBecomeMainWindow),
 229        yes as extern "C" fn(&Object, Sel) -> BOOL,
 230    );
 231    decl.add_method(
 232        sel!(canBecomeKeyWindow),
 233        yes as extern "C" fn(&Object, Sel) -> BOOL,
 234    );
 235    decl.add_method(
 236        sel!(windowDidResize:),
 237        window_did_resize as extern "C" fn(&Object, Sel, id),
 238    );
 239    decl.add_method(
 240        sel!(windowWillEnterFullScreen:),
 241        window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
 242    );
 243    decl.add_method(
 244        sel!(windowWillExitFullScreen:),
 245        window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
 246    );
 247    decl.add_method(
 248        sel!(windowDidMove:),
 249        window_did_move as extern "C" fn(&Object, Sel, id),
 250    );
 251    decl.add_method(
 252        sel!(windowDidBecomeKey:),
 253        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 254    );
 255    decl.add_method(
 256        sel!(windowDidResignKey:),
 257        window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 258    );
 259    decl.add_method(
 260        sel!(windowShouldClose:),
 261        window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
 262    );
 263    decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
 264    decl.register()
 265}
 266
 267///Used to track what the IME does when we send it a keystroke.
 268///This is only used to handle the case where the IME mysteriously
 269///swallows certain keys.
 270///
 271///Basically a direct copy of the approach that WezTerm uses in:
 272///github.com/wez/wezterm : d5755f3e : window/src/os/macos/window.rs
 273enum ImeState {
 274    Continue,
 275    Acted,
 276    None,
 277}
 278
 279struct InsertText {
 280    replacement_range: Option<Range<usize>>,
 281    text: String,
 282}
 283
 284struct WindowState {
 285    handle: AnyWindowHandle,
 286    dispatcher: Arc<dyn PlatformDispatcher>,
 287    native_window: id,
 288    kind: WindowKind,
 289    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
 290    activate_callback: Option<Box<dyn FnMut(bool)>>,
 291    resize_callback: Option<Box<dyn FnMut()>>,
 292    fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
 293    moved_callback: Option<Box<dyn FnMut()>>,
 294    should_close_callback: Option<Box<dyn FnMut() -> bool>>,
 295    close_callback: Option<Box<dyn FnOnce()>>,
 296    appearance_changed_callback: Option<Box<dyn FnMut()>>,
 297    input_handler: Option<Box<dyn InputHandler>>,
 298    pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
 299    last_key_equivalent: Option<KeyDownEvent>,
 300    synthetic_drag_counter: usize,
 301    last_fresh_keydown: Option<Keystroke>,
 302    traffic_light_position: Option<Point<Pixels>>,
 303    previous_modifiers_changed_event: Option<Event>,
 304    // State tracking what the IME did after the last request
 305    ime_state: ImeState,
 306    // Retains the last IME Text
 307    ime_text: Option<String>,
 308}
 309
 310impl WindowState {
 311    fn move_traffic_light(&self) {
 312        if let Some(traffic_light_position) = self.traffic_light_position {
 313            let titlebar_height = self.titlebar_height();
 314
 315            unsafe {
 316                let close_button: id = msg_send![
 317                    self.native_window,
 318                    standardWindowButton: NSWindowButton::NSWindowCloseButton
 319                ];
 320                let min_button: id = msg_send![
 321                    self.native_window,
 322                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
 323                ];
 324                let zoom_button: id = msg_send![
 325                    self.native_window,
 326                    standardWindowButton: NSWindowButton::NSWindowZoomButton
 327                ];
 328
 329                let mut close_button_frame: CGRect = msg_send![close_button, frame];
 330                let mut min_button_frame: CGRect = msg_send![min_button, frame];
 331                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
 332                let mut origin = point(
 333                    traffic_light_position.x,
 334                    titlebar_height
 335                        - traffic_light_position.y
 336                        - px(close_button_frame.size.height as f32),
 337                );
 338                let button_spacing =
 339                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);
 340
 341                close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 342                let _: () = msg_send![close_button, setFrame: close_button_frame];
 343                origin.x += button_spacing;
 344
 345                min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 346                let _: () = msg_send![min_button, setFrame: min_button_frame];
 347                origin.x += button_spacing;
 348
 349                zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());
 350                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
 351                origin.x += button_spacing;
 352            }
 353        }
 354    }
 355
 356    fn is_fullscreen(&self) -> bool {
 357        unsafe {
 358            let style_mask = self.native_window.styleMask();
 359            style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)
 360        }
 361    }
 362
 363    fn bounds(&self) -> WindowBounds {
 364        unsafe {
 365            if self.is_fullscreen() {
 366                return WindowBounds::Fullscreen;
 367            }
 368
 369            let frame = self.frame();
 370            let screen_size = self.native_window.screen().visibleFrame().size();
 371            if frame.size == screen_size {
 372                WindowBounds::Maximized
 373            } else {
 374                WindowBounds::Fixed(frame)
 375            }
 376        }
 377    }
 378
 379    fn frame(&self) -> Bounds<Pixels> {
 380        unsafe {
 381            let frame = NSWindow::frame(self.native_window);
 382            MacScreen::screen_bounds_from_native(frame)
 383        }
 384    }
 385
 386    fn content_size(&self) -> Size<Pixels> {
 387        let NSSize { width, height, .. } =
 388            unsafe { NSView::frame(self.native_window.contentView()) }.size;
 389        size(px(width as f32), px(height as f32))
 390    }
 391
 392    fn scale_factor(&self) -> f32 {
 393        get_scale_factor(self.native_window)
 394    }
 395
 396    fn titlebar_height(&self) -> Pixels {
 397        unsafe {
 398            let frame = NSWindow::frame(self.native_window);
 399            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
 400            px((frame.size.height - content_layout_rect.size.height) as f32)
 401        }
 402    }
 403
 404    fn to_screen_ns_point(&self, point: Point<Pixels>) -> NSPoint {
 405        unsafe {
 406            let point = NSPoint::new(
 407                point.x.into(),
 408                (self.content_size().height - point.y).into(),
 409            );
 410            msg_send![self.native_window, convertPointToScreen: point]
 411        }
 412    }
 413}
 414
 415unsafe impl Send for WindowState {}
 416
 417pub struct MacWindow(Arc<Mutex<WindowState>>);
 418
 419impl MacWindow {
 420    pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
 421        unsafe {
 422            let pool = NSAutoreleasePool::new(nil);
 423
 424            let mut style_mask;
 425            if let Some(titlebar) = options.titlebar.as_ref() {
 426                style_mask = NSWindowStyleMask::NSClosableWindowMask
 427                    | NSWindowStyleMask::NSMiniaturizableWindowMask
 428                    | NSWindowStyleMask::NSResizableWindowMask
 429                    | NSWindowStyleMask::NSTitledWindowMask;
 430
 431                if titlebar.appears_transparent {
 432                    style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 433                }
 434            } else {
 435                style_mask = NSWindowStyleMask::NSTitledWindowMask
 436                    | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
 437            }
 438
 439            let native_window: id = match options.kind {
 440                WindowKind::Normal => msg_send![WINDOW_CLASS, alloc],
 441                WindowKind::PopUp => {
 442                    style_mask |= NSWindowStyleMaskNonactivatingPanel;
 443                    msg_send![PANEL_CLASS, alloc]
 444                }
 445            };
 446            let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
 447                NSRect::new(NSPoint::new(0., 0.), NSSize::new(1024., 768.)),
 448                style_mask,
 449                NSBackingStoreBuffered,
 450                NO,
 451                options
 452                    .screen
 453                    .map(|screen| MacScreen::from_handle(screen).native_screen)
 454                    .unwrap_or(nil),
 455            );
 456            assert!(!native_window.is_null());
 457
 458            let screen = native_window.screen();
 459            match options.bounds {
 460                WindowBounds::Fullscreen => {
 461                    native_window.toggleFullScreen_(nil);
 462                }
 463                WindowBounds::Maximized => {
 464                    native_window.setFrame_display_(screen.visibleFrame(), YES);
 465                }
 466                WindowBounds::Fixed(bounds) => {
 467                    let bounds = MacScreen::screen_bounds_to_native(bounds);
 468                    let screen_bounds = screen.visibleFrame();
 469                    if bounds.intersects(screen_bounds) {
 470                        native_window.setFrame_display_(bounds, YES);
 471                    } else {
 472                        native_window.setFrame_display_(screen_bounds, YES);
 473                    }
 474                }
 475            }
 476
 477            let native_view: id = msg_send![VIEW_CLASS, alloc];
 478            let native_view = NSView::init(native_view);
 479
 480            assert!(!native_view.is_null());
 481
 482            let window = Self(Arc::new(Mutex::new(WindowState {
 483                handle,
 484                dispatcher: platform.dispatcher(),
 485                native_window,
 486                kind: options.kind,
 487                event_callback: None,
 488                resize_callback: None,
 489                should_close_callback: None,
 490                close_callback: None,
 491                activate_callback: None,
 492                fullscreen_callback: None,
 493                moved_callback: None,
 494                appearance_changed_callback: None,
 495                input_handler: None,
 496                pending_key_down: None,
 497                last_key_equivalent: None,
 498                synthetic_drag_counter: 0,
 499                last_fresh_keydown: None,
 500                traffic_light_position: options
 501                    .titlebar
 502                    .as_ref()
 503                    .and_then(|titlebar| titlebar.traffic_light_position),
 504                previous_modifiers_changed_event: None,
 505                ime_state: ImeState::None,
 506                ime_text: None,
 507            })));
 508
 509            (*native_window).set_ivar(
 510                WINDOW_STATE_IVAR,
 511                Arc::into_raw(window.0.clone()) as *const c_void,
 512            );
 513            native_window.setDelegate_(native_window);
 514            (*native_view).set_ivar(
 515                WINDOW_STATE_IVAR,
 516                Arc::into_raw(window.0.clone()) as *const c_void,
 517            );
 518
 519            if let Some(title) = options
 520                .titlebar
 521                .as_ref()
 522                .and_then(|t| t.title.as_ref().map(AsRef::as_ref))
 523            {
 524                native_window.setTitle_(NSString::alloc(nil).init_str(title));
 525            }
 526
 527            native_window.setMovable_(options.is_movable as BOOL);
 528
 529            if options
 530                .titlebar
 531                .map_or(true, |titlebar| titlebar.appears_transparent)
 532            {
 533                native_window.setTitlebarAppearsTransparent_(YES);
 534                native_window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden);
 535            }
 536
 537            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
 538            native_view.setWantsBestResolutionOpenGLSurface_(YES);
 539
 540            // From winit crate: On Mojave, views automatically become layer-backed shortly after
 541            // being added to a native_window. Changing the layer-backedness of a view breaks the
 542            // association between the view and its associated OpenGL context. To work around this,
 543            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
 544            // itself and break the association with its context.
 545            native_view.setWantsLayer(YES);
 546            let _: () = msg_send![
 547                native_view,
 548                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
 549            ];
 550
 551            native_window.setContentView_(native_view.autorelease());
 552            native_window.makeFirstResponder_(native_view);
 553
 554            if options.center {
 555                native_window.center();
 556            }
 557
 558            match options.kind {
 559                WindowKind::Normal => {
 560                    native_window.setLevel_(NSNormalWindowLevel);
 561                    native_window.setAcceptsMouseMovedEvents_(YES);
 562                }
 563                WindowKind::PopUp => {
 564                    // Use a tracking area to allow receiving MouseMoved events even when
 565                    // the window or application aren't active, which is often the case
 566                    // e.g. for notification windows.
 567                    let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
 568                    let _: () = msg_send![
 569                        tracking_area,
 570                        initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
 571                        options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
 572                        owner: native_view
 573                        userInfo: nil
 574                    ];
 575                    let _: () =
 576                        msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
 577
 578                    native_window.setLevel_(NSPopUpWindowLevel);
 579                    let _: () = msg_send![
 580                        native_window,
 581                        setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
 582                    ];
 583                    native_window.setCollectionBehavior_(
 584                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
 585                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
 586                    );
 587                }
 588            }
 589            if options.focus {
 590                native_window.makeKeyAndOrderFront_(nil);
 591            } else if options.show {
 592                native_window.orderFront_(nil);
 593            }
 594
 595            window.0.lock().move_traffic_light();
 596            pool.drain();
 597
 598            window
 599        }
 600    }
 601
 602    pub fn main_window() -> Option<AnyWindowHandle> {
 603        unsafe {
 604            let app = NSApplication::sharedApplication(nil);
 605            let main_window: id = msg_send![app, mainWindow];
 606            if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
 607                let handle = get_window_state(&*main_window).lock().handle;
 608                Some(handle)
 609            } else {
 610                None
 611            }
 612        }
 613    }
 614}
 615
 616impl Drop for MacWindow {
 617    fn drop(&mut self) {
 618        let this = self.0.clone();
 619        let dispatcher = self.0.lock().dispatcher.clone();
 620        let _ = crate::spawn_on_main(dispatcher, async move {
 621            unsafe {
 622                this.lock().native_window.close();
 623            }
 624        });
 625    }
 626}
 627
 628unsafe impl HasRawWindowHandle for MacWindow {
 629    fn raw_window_handle(&self) -> RawWindowHandle {
 630        let ns_window = self.0.lock().native_window;
 631        let ns_view = unsafe { ns_window.contentView() };
 632        let mut handle = AppKitWindowHandle::empty();
 633        handle.ns_window = ns_window as *mut c_void;
 634        handle.ns_view = ns_view as *mut c_void;
 635        handle.into()
 636    }
 637}
 638
 639unsafe impl HasRawDisplayHandle for MacWindow {
 640    fn raw_display_handle(&self) -> RawDisplayHandle {
 641        AppKitDisplayHandle::empty().into()
 642    }
 643}
 644
 645impl PlatformWindow for MacWindow {
 646    fn bounds(&self) -> WindowBounds {
 647        self.0.as_ref().lock().bounds()
 648    }
 649
 650    fn content_size(&self) -> Size<Pixels> {
 651        self.0.as_ref().lock().content_size().into()
 652    }
 653
 654    fn scale_factor(&self) -> f32 {
 655        self.0.as_ref().lock().scale_factor()
 656    }
 657
 658    fn titlebar_height(&self) -> Pixels {
 659        self.0.as_ref().lock().titlebar_height()
 660    }
 661
 662    fn appearance(&self) -> WindowAppearance {
 663        unsafe {
 664            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
 665            WindowAppearance::from_native(appearance)
 666        }
 667    }
 668
 669    fn screen(&self) -> Rc<dyn PlatformScreen> {
 670        unsafe {
 671            Rc::new(MacScreen {
 672                native_screen: self.0.as_ref().lock().native_window.screen(),
 673            })
 674        }
 675    }
 676
 677    fn mouse_position(&self) -> Point<Pixels> {
 678        let position = unsafe {
 679            self.0
 680                .lock()
 681                .native_window
 682                .mouseLocationOutsideOfEventStream()
 683        };
 684        convert_mouse_position(position, self.content_size().height)
 685    }
 686
 687    fn as_any_mut(&mut self) -> &mut dyn Any {
 688        self
 689    }
 690
 691    fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
 692        self.0.as_ref().lock().input_handler = Some(input_handler);
 693    }
 694
 695    fn prompt(
 696        &self,
 697        level: WindowPromptLevel,
 698        msg: &str,
 699        answers: &[&str],
 700    ) -> oneshot::Receiver<usize> {
 701        // macOs applies overrides to modal window buttons after they are added.
 702        // Two most important for this logic are:
 703        // * Buttons with "Cancel" title will be displayed as the last buttons in the modal
 704        // * Last button added to the modal via `addButtonWithTitle` stays focused
 705        // * Focused buttons react on "space"/" " keypresses
 706        // * Usage of `keyEquivalent`, `makeFirstResponder` or `setInitialFirstResponder` does not change the focus
 707        //
 708        // See also https://developer.apple.com/documentation/appkit/nsalert/1524532-addbuttonwithtitle#discussion
 709        // ```
 710        // By default, the first button has a key equivalent of Return,
 711        // any button with a title of “Cancel” has a key equivalent of Escape,
 712        // 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).
 713        // ```
 714        //
 715        // To avoid situations when the last element added is "Cancel" and it gets the focus
 716        // (hence stealing both ESC and Space shortcuts), we find and add one non-Cancel button
 717        // last, so it gets focus and a Space shortcut.
 718        // This way, "Save this file? Yes/No/Cancel"-ish modals will get all three buttons mapped with a key.
 719        let latest_non_cancel_label = answers
 720            .iter()
 721            .enumerate()
 722            .rev()
 723            .find(|(_, &label)| label != "Cancel")
 724            .filter(|&(label_index, _)| label_index > 0);
 725
 726        unsafe {
 727            let alert: id = msg_send![class!(NSAlert), alloc];
 728            let alert: id = msg_send![alert, init];
 729            let alert_style = match level {
 730                WindowPromptLevel::Info => 1,
 731                WindowPromptLevel::Warning => 0,
 732                WindowPromptLevel::Critical => 2,
 733            };
 734            let _: () = msg_send![alert, setAlertStyle: alert_style];
 735            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
 736
 737            for (ix, answer) in answers
 738                .iter()
 739                .enumerate()
 740                .filter(|&(ix, _)| Some(ix) != latest_non_cancel_label.map(|(ix, _)| ix))
 741            {
 742                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 743                let _: () = msg_send![button, setTag: ix as NSInteger];
 744            }
 745            if let Some((ix, answer)) = latest_non_cancel_label {
 746                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 747                let _: () = msg_send![button, setTag: ix as NSInteger];
 748            }
 749
 750            let (done_tx, done_rx) = oneshot::channel();
 751            let done_tx = Cell::new(Some(done_tx));
 752            let block = ConcreteBlock::new(move |answer: NSInteger| {
 753                if let Some(done_tx) = done_tx.take() {
 754                    let _ = done_tx.send(answer.try_into().unwrap());
 755                }
 756            });
 757            let block = block.copy();
 758            let native_window = self.0.lock().native_window;
 759            let dispatcher = self.0.lock().dispatcher.clone();
 760            let _ = crate::spawn_on_main_local(dispatcher, async move {
 761                let _: () = msg_send![
 762                    alert,
 763                    beginSheetModalForWindow: native_window
 764                    completionHandler: block
 765                ];
 766            });
 767
 768            done_rx
 769        }
 770    }
 771
 772    fn activate(&self) {
 773        let window = self.0.lock().native_window;
 774        let dispatcher = self.0.lock().dispatcher.clone();
 775        let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
 776            unsafe {
 777                let _: () = msg_send![window, makeKeyAndOrderFront: nil];
 778            }
 779        });
 780    }
 781
 782    fn set_title(&mut self, title: &str) {
 783        unsafe {
 784            let app = NSApplication::sharedApplication(nil);
 785            let window = self.0.lock().native_window;
 786            let title = ns_string(title);
 787            let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
 788            let _: () = msg_send![window, setTitle: title];
 789            self.0.lock().move_traffic_light();
 790        }
 791    }
 792
 793    fn set_edited(&mut self, edited: bool) {
 794        unsafe {
 795            let window = self.0.lock().native_window;
 796            msg_send![window, setDocumentEdited: edited as BOOL]
 797        }
 798
 799        // Changing the document edited state resets the traffic light position,
 800        // so we have to move it again.
 801        self.0.lock().move_traffic_light();
 802    }
 803
 804    fn show_character_palette(&self) {
 805        unsafe {
 806            let app = NSApplication::sharedApplication(nil);
 807            let window = self.0.lock().native_window;
 808            let _: () = msg_send![app, orderFrontCharacterPalette: window];
 809        }
 810    }
 811
 812    fn minimize(&self) {
 813        let window = self.0.lock().native_window;
 814        unsafe {
 815            window.miniaturize_(nil);
 816        }
 817    }
 818
 819    fn zoom(&self) {
 820        let this = self.0.lock();
 821        let window = this.native_window;
 822        let dispatcher = this.dispatcher.clone();
 823        let _ = crate::spawn_on_main_local(dispatcher, async move {
 824            unsafe {
 825                window.zoom_(nil);
 826            }
 827        });
 828    }
 829
 830    fn toggle_full_screen(&self) {
 831        let this = self.0.lock();
 832        let window = this.native_window;
 833        let dispatcher = this.dispatcher.clone();
 834        let _ = crate::spawn_on_main_local(dispatcher, async move {
 835            unsafe {
 836                window.toggleFullScreen_(nil);
 837            }
 838        });
 839    }
 840
 841    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
 842        self.0.as_ref().lock().event_callback = Some(callback);
 843    }
 844
 845    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
 846        self.0.as_ref().lock().activate_callback = Some(callback);
 847    }
 848
 849    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
 850        self.0.as_ref().lock().resize_callback = Some(callback);
 851    }
 852
 853    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
 854        self.0.as_ref().lock().fullscreen_callback = Some(callback);
 855    }
 856
 857    fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
 858        self.0.as_ref().lock().moved_callback = Some(callback);
 859    }
 860
 861    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
 862        self.0.as_ref().lock().should_close_callback = Some(callback);
 863    }
 864
 865    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
 866        self.0.as_ref().lock().close_callback = Some(callback);
 867    }
 868
 869    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
 870        self.0.lock().appearance_changed_callback = Some(callback);
 871    }
 872
 873    fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
 874        let self_borrow = self.0.lock();
 875        let self_handle = self_borrow.handle;
 876
 877        unsafe {
 878            let app = NSApplication::sharedApplication(nil);
 879
 880            // Convert back to screen coordinates
 881            let screen_point = self_borrow.to_screen_ns_point(position);
 882
 883            let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
 884            let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
 885
 886            let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
 887            let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
 888            if is_panel == YES || is_window == YES {
 889                let topmost_window = get_window_state(&*top_most_window).lock().handle;
 890                topmost_window == self_handle
 891            } else {
 892                // Someone else's window is on top
 893                false
 894            }
 895        }
 896    }
 897}
 898
 899fn get_scale_factor(native_window: id) -> f32 {
 900    unsafe {
 901        let screen: id = msg_send![native_window, screen];
 902        NSScreen::backingScaleFactor(screen) as f32
 903    }
 904}
 905
 906unsafe fn get_window_state(object: &Object) -> Arc<Mutex<WindowState>> {
 907    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 908    let rc1 = Arc::from_raw(raw as *mut Mutex<WindowState>);
 909    let rc2 = rc1.clone();
 910    mem::forget(rc1);
 911    rc2
 912}
 913
 914unsafe fn drop_window_state(object: &Object) {
 915    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 916    Rc::from_raw(raw as *mut RefCell<WindowState>);
 917}
 918
 919extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
 920    YES
 921}
 922
 923extern "C" fn dealloc_window(this: &Object, _: Sel) {
 924    unsafe {
 925        drop_window_state(this);
 926        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
 927    }
 928}
 929
 930extern "C" fn dealloc_view(this: &Object, _: Sel) {
 931    unsafe {
 932        drop_window_state(this);
 933        let _: () = msg_send![super(this, class!(NSView)), dealloc];
 934    }
 935}
 936
 937extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
 938    handle_key_event(this, native_event, true)
 939}
 940
 941extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
 942    handle_key_event(this, native_event, false);
 943}
 944
 945extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
 946    let window_state = unsafe { get_window_state(this) };
 947    let mut window_state_borrow = window_state.as_ref().lock();
 948
 949    let window_height = window_state_borrow.content_size().height;
 950    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
 951
 952    if let Some(Event::KeyDown(event)) = event {
 953        // For certain keystrokes, macOS will first dispatch a "key equivalent" event.
 954        // If that event isn't handled, it will then dispatch a "key down" event. GPUI
 955        // makes no distinction between these two types of events, so we need to ignore
 956        // the "key down" event if we've already just processed its "key equivalent" version.
 957        if key_equivalent {
 958            window_state_borrow.last_key_equivalent = Some(event.clone());
 959        } else if window_state_borrow.last_key_equivalent.take().as_ref() == Some(&event) {
 960            return NO;
 961        }
 962
 963        let keydown = event.keystroke.clone();
 964        let fn_modifier = keydown.modifiers.function;
 965        // Ignore events from held-down keys after some of the initially-pressed keys
 966        // were released.
 967        if event.is_held {
 968            if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
 969                return YES;
 970            }
 971        } else {
 972            window_state_borrow.last_fresh_keydown = Some(keydown);
 973        }
 974        window_state_borrow.pending_key_down = Some((event, None));
 975        drop(window_state_borrow);
 976
 977        // Send the event to the input context for IME handling, unless the `fn` modifier is
 978        // being pressed.
 979        if !fn_modifier {
 980            unsafe {
 981                let input_context: id = msg_send![this, inputContext];
 982                let _: BOOL = msg_send![input_context, handleEvent: native_event];
 983            }
 984        }
 985
 986        let mut handled = false;
 987        let mut window_state_borrow = window_state.lock();
 988        let ime_text = window_state_borrow.ime_text.clone();
 989        if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
 990            let is_held = event.is_held;
 991            if let Some(mut callback) = window_state_borrow.event_callback.take() {
 992                drop(window_state_borrow);
 993
 994                let is_composing =
 995                    with_input_handler(this, |input_handler| input_handler.marked_text_range())
 996                        .flatten()
 997                        .is_some();
 998                if !is_composing {
 999                    // if the IME has changed the key, we'll first emit an event with the character
1000                    // generated by the IME system; then fallback to the keystroke if that is not
1001                    // handled.
1002                    // cases that we have working:
1003                    // - " on a brazillian layout by typing <quote><space>
1004                    // - ctrl-` on a brazillian layout by typing <ctrl-`>
1005                    // - $ on a czech QWERTY layout by typing <alt-4>
1006                    // - 4 on a czech QWERTY layout by typing <shift-4>
1007                    // - ctrl-4 on a czech QWERTY layout by typing <ctrl-alt-4> (or <ctrl-shift-4>)
1008                    if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) {
1009                        let event_with_ime_text = KeyDownEvent {
1010                            is_held: false,
1011                            keystroke: Keystroke {
1012                                // we match ctrl because some use-cases need it.
1013                                // we don't match alt because it's often used to generate the optional character
1014                                // we don't match shift because we're not here with letters (usually)
1015                                // we don't match cmd/fn because they don't seem to use IME
1016                                modifiers: Default::default(),
1017                                key: ime_text.clone().unwrap(),
1018                            },
1019                        };
1020                        handled = callback(Event::KeyDown(event_with_ime_text));
1021                    }
1022                    if !handled {
1023                        // empty key happens when you type a deadkey in input composition.
1024                        // (e.g. on a brazillian keyboard typing quote is a deadkey)
1025                        if !event.keystroke.key.is_empty() {
1026                            handled = callback(Event::KeyDown(event));
1027                        }
1028                    }
1029                }
1030
1031                if !handled {
1032                    if let Some(insert) = insert_text {
1033                        handled = true;
1034                        with_input_handler(this, |input_handler| {
1035                            input_handler
1036                                .replace_text_in_range(insert.replacement_range, &insert.text)
1037                        });
1038                    } else if !is_composing && is_held {
1039                        if let Some(last_insert_text) = ime_text {
1040                            //MacOS IME is a bit funky, and even when you've told it there's nothing to
1041                            //inter it will still swallow certain keys (e.g. 'f', 'j') and not others
1042                            //(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
1043                            with_input_handler(this, |input_handler| {
1044                                if input_handler.selected_text_range().is_none() {
1045                                    handled = true;
1046                                    input_handler.replace_text_in_range(None, &last_insert_text)
1047                                }
1048                            });
1049                        }
1050                    }
1051                }
1052
1053                window_state.lock().event_callback = Some(callback);
1054            }
1055        } else {
1056            handled = true;
1057        }
1058
1059        handled as BOOL
1060    } else {
1061        NO
1062    }
1063}
1064
1065extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
1066    let window_state = unsafe { get_window_state(this) };
1067    let weak_window_state = Arc::downgrade(&window_state);
1068    let mut window_state_borrow = window_state.as_ref().lock();
1069    let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
1070
1071    let window_height = window_state_borrow.content_size().height;
1072    let event = unsafe { Event::from_native(native_event, Some(window_height)) };
1073
1074    if let Some(mut event) = event {
1075        let synthesized_second_event = match &mut event {
1076            Event::MouseDown(
1077                event @ MouseDownEvent {
1078                    button: MouseButton::Left,
1079                    modifiers: Modifiers { control: true, .. },
1080                    ..
1081                },
1082            ) => {
1083                *event = MouseDownEvent {
1084                    button: MouseButton::Right,
1085                    modifiers: Modifiers {
1086                        control: false,
1087                        ..event.modifiers
1088                    },
1089                    click_count: 1,
1090                    ..*event
1091                };
1092
1093                Some(Event::MouseDown(MouseDownEvent {
1094                    button: MouseButton::Right,
1095                    ..*event
1096                }))
1097            }
1098
1099            // Because we map a ctrl-left_down to a right_down -> right_up let's ignore
1100            // the ctrl-left_up to avoid having a mismatch in button down/up events if the
1101            // user is still holding ctrl when releasing the left mouse button
1102            Event::MouseUp(MouseUpEvent {
1103                button: MouseButton::Left,
1104                modifiers: Modifiers { control: true, .. },
1105                ..
1106            }) => {
1107                window_state_borrow.synthetic_drag_counter += 1;
1108                return;
1109            }
1110
1111            _ => None,
1112        };
1113
1114        match &event {
1115            Event::MouseMoved(
1116                event @ MouseMovedEvent {
1117                    pressed_button: Some(_),
1118                    ..
1119                },
1120            ) => {
1121                window_state_borrow.synthetic_drag_counter += 1;
1122                let dispatcher = window_state_borrow.dispatcher.clone();
1123                let _ = crate::spawn_on_main_local(
1124                    dispatcher,
1125                    synthetic_drag(
1126                        weak_window_state,
1127                        window_state_borrow.synthetic_drag_counter,
1128                        event.clone(),
1129                    ),
1130                );
1131            }
1132
1133            Event::MouseMoved(_)
1134                if !(is_active || window_state_borrow.kind == WindowKind::PopUp) =>
1135            {
1136                return
1137            }
1138
1139            Event::MouseUp(MouseUpEvent {
1140                button: MouseButton::Left,
1141                ..
1142            }) => {
1143                window_state_borrow.synthetic_drag_counter += 1;
1144            }
1145
1146            Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1147                // Only raise modifiers changed event when they have actually changed
1148                if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
1149                    modifiers: prev_modifiers,
1150                })) = &window_state_borrow.previous_modifiers_changed_event
1151                {
1152                    if prev_modifiers == modifiers {
1153                        return;
1154                    }
1155                }
1156
1157                window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
1158            }
1159
1160            _ => {}
1161        }
1162
1163        if let Some(mut callback) = window_state_borrow.event_callback.take() {
1164            drop(window_state_borrow);
1165            callback(event);
1166            if let Some(event) = synthesized_second_event {
1167                callback(event);
1168            }
1169            window_state.lock().event_callback = Some(callback);
1170        }
1171    }
1172}
1173
1174// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
1175// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
1176extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
1177    let window_state = unsafe { get_window_state(this) };
1178    let mut window_state_borrow = window_state.as_ref().lock();
1179
1180    let keystroke = Keystroke {
1181        modifiers: Default::default(),
1182        key: ".".into(),
1183    };
1184    let event = Event::KeyDown(KeyDownEvent {
1185        keystroke: keystroke.clone(),
1186        is_held: false,
1187    });
1188
1189    window_state_borrow.last_fresh_keydown = Some(keystroke);
1190    if let Some(mut callback) = window_state_borrow.event_callback.take() {
1191        drop(window_state_borrow);
1192        callback(event);
1193        window_state.lock().event_callback = Some(callback);
1194    }
1195}
1196
1197extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1198    let window_state = unsafe { get_window_state(this) };
1199    window_state.as_ref().lock().move_traffic_light();
1200}
1201
1202extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1203    window_fullscreen_changed(this, true);
1204}
1205
1206extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1207    window_fullscreen_changed(this, false);
1208}
1209
1210fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
1211    let window_state = unsafe { get_window_state(this) };
1212    let mut window_state_borrow = window_state.as_ref().lock();
1213    if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
1214        drop(window_state_borrow);
1215        callback(is_fullscreen);
1216        window_state.lock().fullscreen_callback = Some(callback);
1217    }
1218}
1219
1220extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
1221    let window_state = unsafe { get_window_state(this) };
1222    let mut window_state_borrow = window_state.as_ref().lock();
1223    if let Some(mut callback) = window_state_borrow.moved_callback.take() {
1224        drop(window_state_borrow);
1225        callback();
1226        window_state.lock().moved_callback = Some(callback);
1227    }
1228}
1229
1230extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1231    let window_state = unsafe { get_window_state(this) };
1232    let window_state_borrow = window_state.lock();
1233    let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
1234
1235    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1236    // `windowDidBecomeKey` message to the previous key window even though that window
1237    // isn't actually key. This causes a bug if the application is later activated while
1238    // the pop-up is still open, making it impossible to activate the previous key window
1239    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1240    // the app and re-activate it, which is a pretty bad UX.
1241    // The following code detects the spurious event and invokes `resignKeyWindow`:
1242    // in theory, we're not supposed to invoke this method manually but it balances out
1243    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1244    if selector == sel!(windowDidBecomeKey:) {
1245        if !is_active {
1246            unsafe {
1247                let _: () = msg_send![window_state_borrow.native_window, resignKeyWindow];
1248                return;
1249            }
1250        }
1251    }
1252
1253    let dispatcher = window_state_borrow.dispatcher.clone();
1254    drop(window_state_borrow);
1255    let _ = crate::spawn_on_main_local(dispatcher, async move {
1256        let mut window_state_borrow = window_state.as_ref().lock();
1257        if let Some(mut callback) = window_state_borrow.activate_callback.take() {
1258            drop(window_state_borrow);
1259            callback(is_active);
1260            window_state.lock().activate_callback = Some(callback);
1261        };
1262    });
1263}
1264
1265extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1266    let window_state = unsafe { get_window_state(this) };
1267    let mut window_state_borrow = window_state.as_ref().lock();
1268    if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
1269        drop(window_state_borrow);
1270        let should_close = callback();
1271        window_state.lock().should_close_callback = Some(callback);
1272        should_close as BOOL
1273    } else {
1274        YES
1275    }
1276}
1277
1278extern "C" fn close_window(this: &Object, _: Sel) {
1279    unsafe {
1280        let close_callback = {
1281            let window_state = get_window_state(this);
1282            window_state
1283                .as_ref()
1284                .try_lock()
1285                .and_then(|mut window_state| window_state.close_callback.take())
1286        };
1287
1288        if let Some(callback) = close_callback {
1289            callback();
1290        }
1291
1292        let _: () = msg_send![super(this, class!(NSWindow)), close];
1293    }
1294}
1295
1296// extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1297//     let window_state = unsafe { get_window_state(this) };
1298//     let window_state = window_state.as_ref().lock();
1299//     window_state.renderer.layer().as_ptr() as id
1300// }
1301
1302extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1303    let window_state = unsafe { get_window_state(this) };
1304    let mut window_state_borrow = window_state.as_ref().lock();
1305
1306    // unsafe {
1307    //     let scale_factor = window_state_borrow.scale_factor() as f64;
1308    //     let size = window_state_borrow.content_size();
1309    //     let drawable_size: NSSize = NSSize {
1310    //         width: f64::from(size.width) * scale_factor,
1311    //         height: f64::from(size.height) * scale_factor,
1312    //     };
1313
1314    //     // let _: () = msg_send![
1315    //     //     window_state_borrow.renderer.layer(),
1316    //     //     setContentsScale: scale_factor
1317    //     // ];
1318    //     // let _: () = msg_send![
1319    //     //     window_state_borrow.renderer.layer(),
1320    //     //     setDrawableSize: drawable_size
1321    //     // ];
1322    // }
1323
1324    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1325        drop(window_state_borrow);
1326        callback();
1327        window_state.as_ref().lock().resize_callback = Some(callback);
1328    };
1329}
1330
1331extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1332    let window_state = unsafe { get_window_state(this) };
1333    let window_state_borrow = window_state.as_ref().lock();
1334
1335    if window_state_borrow.content_size() == size.into() {
1336        return;
1337    }
1338
1339    unsafe {
1340        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1341    }
1342
1343    // let scale_factor = window_state_borrow.scale_factor() as f64;
1344    // let drawable_size: NSSize = NSSize {
1345    //     width: size.width * scale_factor,
1346    //     height: size.height * scale_factor,
1347    // };
1348    //
1349    // unsafe {
1350    //     let _: () = msg_send![
1351    //         window_state_borrow.renderer.layer(),
1352    //         setDrawableSize: drawable_size
1353    //     ];
1354    // }
1355
1356    drop(window_state_borrow);
1357    let mut window_state_borrow = window_state.lock();
1358    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1359        drop(window_state_borrow);
1360        callback();
1361        window_state.lock().resize_callback = Some(callback);
1362    };
1363}
1364
1365extern "C" fn display_layer(_this: &Object, _: Sel, _: id) {
1366    // unsafe {
1367    // let window_state = get_window_state(this);
1368    // let mut window_state = window_state.as_ref().lock();
1369    // if let Some(scene) = window_state.scene_to_render.take() {
1370    //     window_state.renderer.render(&scene);
1371    // };
1372    // }
1373}
1374
1375extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1376    unsafe { msg_send![class!(NSArray), array] }
1377}
1378
1379extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1380    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1381        .flatten()
1382        .is_some() as BOOL
1383}
1384
1385extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1386    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1387        .flatten()
1388        .map_or(NSRange::invalid(), |range| range.into())
1389}
1390
1391extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1392    with_input_handler(this, |input_handler| input_handler.selected_text_range())
1393        .flatten()
1394        .map_or(NSRange::invalid(), |range| range.into())
1395}
1396
1397extern "C" fn first_rect_for_character_range(
1398    this: &Object,
1399    _: Sel,
1400    range: NSRange,
1401    _: id,
1402) -> NSRect {
1403    let frame = unsafe {
1404        let window = get_window_state(this).lock().native_window;
1405        NSView::frame(window)
1406    };
1407    with_input_handler(this, |input_handler| {
1408        input_handler.bounds_for_range(range.to_range()?)
1409    })
1410    .flatten()
1411    .map_or(
1412        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1413        |bounds| {
1414            NSRect::new(
1415                NSPoint::new(
1416                    frame.origin.x + bounds.origin.x as f64,
1417                    frame.origin.y + frame.size.height - bounds.origin.y as f64,
1418                ),
1419                NSSize::new(bounds.size.width as f64, bounds.size.height as f64),
1420            )
1421        },
1422    )
1423}
1424
1425extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1426    unsafe {
1427        let window_state = get_window_state(this);
1428        let mut window_state_borrow = window_state.lock();
1429        let pending_key_down = window_state_borrow.pending_key_down.take();
1430        drop(window_state_borrow);
1431
1432        let is_attributed_string: BOOL =
1433            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1434        let text: id = if is_attributed_string == YES {
1435            msg_send![text, string]
1436        } else {
1437            text
1438        };
1439        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1440            .to_str()
1441            .unwrap();
1442        let replacement_range = replacement_range.to_range();
1443
1444        window_state.lock().ime_text = Some(text.to_string());
1445        window_state.lock().ime_state = ImeState::Acted;
1446
1447        let is_composing =
1448            with_input_handler(this, |input_handler| input_handler.marked_text_range())
1449                .flatten()
1450                .is_some();
1451
1452        if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
1453            with_input_handler(this, |input_handler| {
1454                input_handler.replace_text_in_range(replacement_range, text)
1455            });
1456        } else {
1457            let mut pending_key_down = pending_key_down.unwrap();
1458            pending_key_down.1 = Some(InsertText {
1459                replacement_range,
1460                text: text.to_string(),
1461            });
1462            window_state.lock().pending_key_down = Some(pending_key_down);
1463        }
1464    }
1465}
1466
1467extern "C" fn set_marked_text(
1468    this: &Object,
1469    _: Sel,
1470    text: id,
1471    selected_range: NSRange,
1472    replacement_range: NSRange,
1473) {
1474    unsafe {
1475        let window_state = get_window_state(this);
1476        window_state.lock().pending_key_down.take();
1477
1478        let is_attributed_string: BOOL =
1479            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1480        let text: id = if is_attributed_string == YES {
1481            msg_send![text, string]
1482        } else {
1483            text
1484        };
1485        let selected_range = selected_range.to_range();
1486        let replacement_range = replacement_range.to_range();
1487        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1488            .to_str()
1489            .unwrap();
1490
1491        window_state.lock().ime_state = ImeState::Acted;
1492        window_state.lock().ime_text = Some(text.to_string());
1493
1494        with_input_handler(this, |input_handler| {
1495            input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
1496        });
1497    }
1498}
1499
1500extern "C" fn unmark_text(this: &Object, _: Sel) {
1501    unsafe {
1502        let state = get_window_state(this);
1503        let mut borrow = state.lock();
1504        borrow.ime_state = ImeState::Acted;
1505        borrow.ime_text.take();
1506    }
1507
1508    with_input_handler(this, |input_handler| input_handler.unmark_text());
1509}
1510
1511extern "C" fn attributed_substring_for_proposed_range(
1512    this: &Object,
1513    _: Sel,
1514    range: NSRange,
1515    _actual_range: *mut c_void,
1516) -> id {
1517    with_input_handler(this, |input_handler| {
1518        let range = range.to_range()?;
1519        if range.is_empty() {
1520            return None;
1521        }
1522
1523        let selected_text = input_handler.text_for_range(range)?;
1524        unsafe {
1525            let string: id = msg_send![class!(NSAttributedString), alloc];
1526            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1527            Some(string)
1528        }
1529    })
1530    .flatten()
1531    .unwrap_or(nil)
1532}
1533
1534extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1535    unsafe {
1536        let state = get_window_state(this);
1537        let mut borrow = state.lock();
1538        borrow.ime_state = ImeState::Continue;
1539        borrow.ime_text.take();
1540    }
1541}
1542
1543extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1544    unsafe {
1545        let state = get_window_state(this);
1546        let mut state_borrow = state.as_ref().lock();
1547        if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
1548            drop(state_borrow);
1549            callback();
1550            state.lock().appearance_changed_callback = Some(callback);
1551        }
1552    }
1553}
1554
1555extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
1556    unsafe {
1557        let state = get_window_state(this);
1558        let state_borrow = state.as_ref().lock();
1559        return if state_borrow.kind == WindowKind::PopUp {
1560            YES
1561        } else {
1562            NO
1563        };
1564    }
1565}
1566
1567async fn synthetic_drag(
1568    window_state: Weak<Mutex<WindowState>>,
1569    drag_id: usize,
1570    event: MouseMovedEvent,
1571) {
1572    loop {
1573        Timer::after(Duration::from_millis(16)).await;
1574        if let Some(window_state) = window_state.upgrade() {
1575            let mut window_state_borrow = window_state.lock();
1576            if window_state_borrow.synthetic_drag_counter == drag_id {
1577                if let Some(mut callback) = window_state_borrow.event_callback.take() {
1578                    drop(window_state_borrow);
1579                    callback(Event::MouseMoved(event.clone()));
1580                    window_state.lock().event_callback = Some(callback);
1581                }
1582            } else {
1583                break;
1584            }
1585        }
1586    }
1587}
1588
1589fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
1590where
1591    F: FnOnce(&mut dyn InputHandler) -> R,
1592{
1593    let window_state = unsafe { get_window_state(window) };
1594    let mut window_state_borrow = window_state.as_ref().lock();
1595    if let Some(mut input_handler) = window_state_borrow.input_handler.take() {
1596        drop(window_state_borrow);
1597        let result = f(input_handler.as_mut());
1598        window_state.lock().input_handler = Some(input_handler);
1599        Some(result)
1600    } else {
1601        None
1602    }
1603}