window.rs

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