window.rs

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