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},
  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_(
 381                RectF::new(Default::default(), vec2f(1024., 768.)).to_ns_rect(),
 382                style_mask,
 383                NSBackingStoreBuffered,
 384                NO,
 385            );
 386            assert!(!native_window.is_null());
 387
 388            let screen = native_window.screen();
 389            match options.bounds {
 390                WindowBounds::Maximized => {
 391                    native_window.setFrame_display_(screen.visibleFrame(), YES);
 392                }
 393                WindowBounds::Fixed(top_left_bounds) => {
 394                    let frame = screen.visibleFrame();
 395                    let bottom_left_bounds = RectF::new(
 396                        vec2f(
 397                            top_left_bounds.origin_x(),
 398                            frame.size.height as f32
 399                                - top_left_bounds.origin_y()
 400                                - top_left_bounds.height(),
 401                        ),
 402                        top_left_bounds.size(),
 403                    );
 404                    native_window.setFrame_display_(bottom_left_bounds.to_ns_rect(), YES);
 405                }
 406            }
 407
 408            let native_view: id = msg_send![VIEW_CLASS, alloc];
 409            let native_view = NSView::init(native_view);
 410            assert!(!native_view.is_null());
 411
 412            let window = Self(Rc::new(RefCell::new(WindowState {
 413                id,
 414                native_window,
 415                event_callback: None,
 416                resize_callback: None,
 417                should_close_callback: None,
 418                close_callback: None,
 419                activate_callback: None,
 420                fullscreen_callback: None,
 421                appearance_changed_callback: None,
 422                input_handler: None,
 423                pending_key_down: None,
 424                performed_key_equivalent: false,
 425                synthetic_drag_counter: 0,
 426                executor,
 427                scene_to_render: Default::default(),
 428                renderer: Renderer::new(true, fonts),
 429                last_fresh_keydown: None,
 430                traffic_light_position: options
 431                    .titlebar
 432                    .as_ref()
 433                    .and_then(|titlebar| titlebar.traffic_light_position),
 434                previous_modifiers_changed_event: None,
 435                ime_state: ImeState::None,
 436                ime_text: None,
 437            })));
 438
 439            (*native_window).set_ivar(
 440                WINDOW_STATE_IVAR,
 441                Rc::into_raw(window.0.clone()) as *const c_void,
 442            );
 443            native_window.setDelegate_(native_window);
 444            (*native_view).set_ivar(
 445                WINDOW_STATE_IVAR,
 446                Rc::into_raw(window.0.clone()) as *const c_void,
 447            );
 448
 449            if let Some(title) = options.titlebar.as_ref().and_then(|t| t.title) {
 450                native_window.setTitle_(NSString::alloc(nil).init_str(title));
 451            }
 452
 453            native_window.setMovable_(options.is_movable as BOOL);
 454
 455            if options
 456                .titlebar
 457                .map_or(true, |titlebar| titlebar.appears_transparent)
 458            {
 459                native_window.setTitlebarAppearsTransparent_(YES);
 460            }
 461
 462            let tracking_area: id = msg_send![class!(NSTrackingArea), alloc];
 463            let _: () = msg_send![
 464                tracking_area,
 465                initWithRect: NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.))
 466                options: NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect
 467                owner: native_view
 468                userInfo: nil
 469            ];
 470            let _: () = msg_send![native_view, addTrackingArea: tracking_area.autorelease()];
 471
 472            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
 473            native_view.setWantsBestResolutionOpenGLSurface_(YES);
 474
 475            // From winit crate: On Mojave, views automatically become layer-backed shortly after
 476            // being added to a native_window. Changing the layer-backedness of a view breaks the
 477            // association between the view and its associated OpenGL context. To work around this,
 478            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
 479            // itself and break the association with its context.
 480            native_view.setWantsLayer(YES);
 481            let _: () = msg_send![
 482                native_view,
 483                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
 484            ];
 485
 486            native_window.setContentView_(native_view.autorelease());
 487            native_window.makeFirstResponder_(native_view);
 488
 489            if options.center {
 490                native_window.center();
 491            }
 492
 493            match options.kind {
 494                WindowKind::Normal => native_window.setLevel_(NSNormalWindowLevel),
 495                WindowKind::PopUp => {
 496                    native_window.setLevel_(NSPopUpWindowLevel);
 497                    let _: () = msg_send![
 498                        native_window,
 499                        setAnimationBehavior: NSWindowAnimationBehaviorUtilityWindow
 500                    ];
 501                    native_window.setCollectionBehavior_(
 502                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces |
 503                        NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary
 504                    );
 505                }
 506            }
 507            native_window.makeKeyAndOrderFront_(nil);
 508
 509            window.0.borrow().move_traffic_light();
 510            pool.drain();
 511
 512            window
 513        }
 514    }
 515
 516    pub fn key_window_id() -> Option<usize> {
 517        unsafe {
 518            let app = NSApplication::sharedApplication(nil);
 519            let key_window: id = msg_send![app, keyWindow];
 520            if msg_send![key_window, isKindOfClass: WINDOW_CLASS] {
 521                let id = get_window_state(&*key_window).borrow().id;
 522                Some(id)
 523            } else {
 524                None
 525            }
 526        }
 527    }
 528}
 529
 530impl Drop for Window {
 531    fn drop(&mut self) {
 532        let this = self.0.borrow();
 533        let window = this.native_window;
 534        this.executor
 535            .spawn(async move {
 536                unsafe {
 537                    window.close();
 538                }
 539            })
 540            .detach();
 541    }
 542}
 543
 544impl platform::Window for Window {
 545    fn as_any_mut(&mut self) -> &mut dyn Any {
 546        self
 547    }
 548
 549    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
 550        self.0.as_ref().borrow_mut().event_callback = Some(callback);
 551    }
 552
 553    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
 554        self.0.as_ref().borrow_mut().resize_callback = Some(callback);
 555    }
 556
 557    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
 558        self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
 559    }
 560
 561    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
 562        self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
 563    }
 564
 565    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
 566        self.0.as_ref().borrow_mut().close_callback = Some(callback);
 567    }
 568
 569    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
 570        self.0.as_ref().borrow_mut().activate_callback = Some(callback);
 571    }
 572
 573    fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
 574        self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
 575    }
 576
 577    fn prompt(
 578        &self,
 579        level: platform::PromptLevel,
 580        msg: &str,
 581        answers: &[&str],
 582    ) -> oneshot::Receiver<usize> {
 583        unsafe {
 584            let alert: id = msg_send![class!(NSAlert), alloc];
 585            let alert: id = msg_send![alert, init];
 586            let alert_style = match level {
 587                platform::PromptLevel::Info => 1,
 588                platform::PromptLevel::Warning => 0,
 589                platform::PromptLevel::Critical => 2,
 590            };
 591            let _: () = msg_send![alert, setAlertStyle: alert_style];
 592            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
 593            for (ix, answer) in answers.iter().enumerate() {
 594                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
 595                let _: () = msg_send![button, setTag: ix as NSInteger];
 596            }
 597            let (done_tx, done_rx) = oneshot::channel();
 598            let done_tx = Cell::new(Some(done_tx));
 599            let block = ConcreteBlock::new(move |answer: NSInteger| {
 600                if let Some(mut done_tx) = done_tx.take() {
 601                    let _ = postage::sink::Sink::try_send(&mut done_tx, answer.try_into().unwrap());
 602                }
 603            });
 604            let block = block.copy();
 605            let native_window = self.0.borrow().native_window;
 606            self.0
 607                .borrow()
 608                .executor
 609                .spawn(async move {
 610                    let _: () = msg_send![
 611                        alert,
 612                        beginSheetModalForWindow: native_window
 613                        completionHandler: block
 614                    ];
 615                })
 616                .detach();
 617
 618            done_rx
 619        }
 620    }
 621
 622    fn activate(&self) {
 623        let window = self.0.borrow().native_window;
 624        self.0
 625            .borrow()
 626            .executor
 627            .spawn(async move {
 628                unsafe {
 629                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
 630                }
 631            })
 632            .detach();
 633    }
 634
 635    fn set_title(&mut self, title: &str) {
 636        unsafe {
 637            let app = NSApplication::sharedApplication(nil);
 638            let window = self.0.borrow().native_window;
 639            let title = ns_string(title);
 640            msg_send![app, changeWindowsItem:window title:title filename:false]
 641        }
 642    }
 643
 644    fn set_edited(&mut self, edited: bool) {
 645        unsafe {
 646            let window = self.0.borrow().native_window;
 647            msg_send![window, setDocumentEdited: edited as BOOL]
 648        }
 649
 650        // Changing the document edited state resets the traffic light position,
 651        // so we have to move it again.
 652        self.0.borrow().move_traffic_light();
 653    }
 654
 655    fn show_character_palette(&self) {
 656        unsafe {
 657            let app = NSApplication::sharedApplication(nil);
 658            let window = self.0.borrow().native_window;
 659            let _: () = msg_send![app, orderFrontCharacterPalette: window];
 660        }
 661    }
 662
 663    fn minimize(&self) {
 664        let window = self.0.borrow().native_window;
 665        unsafe {
 666            window.miniaturize_(nil);
 667        }
 668    }
 669
 670    fn zoom(&self) {
 671        let this = self.0.borrow();
 672        let window = this.native_window;
 673        this.executor
 674            .spawn(async move {
 675                unsafe {
 676                    window.zoom_(nil);
 677                }
 678            })
 679            .detach();
 680    }
 681
 682    fn toggle_full_screen(&self) {
 683        let this = self.0.borrow();
 684        let window = this.native_window;
 685        this.executor
 686            .spawn(async move {
 687                unsafe {
 688                    window.toggleFullScreen_(nil);
 689                }
 690            })
 691            .detach();
 692    }
 693
 694    fn bounds(&self) -> RectF {
 695        self.0.as_ref().borrow().bounds()
 696    }
 697
 698    fn content_size(&self) -> Vector2F {
 699        self.0.as_ref().borrow().content_size()
 700    }
 701
 702    fn scale_factor(&self) -> f32 {
 703        self.0.as_ref().borrow().scale_factor()
 704    }
 705
 706    fn present_scene(&mut self, scene: Scene) {
 707        self.0.as_ref().borrow_mut().present_scene(scene);
 708    }
 709
 710    fn titlebar_height(&self) -> f32 {
 711        self.0.as_ref().borrow().titlebar_height()
 712    }
 713
 714    fn appearance(&self) -> crate::Appearance {
 715        unsafe {
 716            let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
 717            crate::Appearance::from_native(appearance)
 718        }
 719    }
 720
 721    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
 722        self.0.borrow_mut().appearance_changed_callback = Some(callback);
 723    }
 724}
 725
 726impl WindowState {
 727    fn move_traffic_light(&self) {
 728        if let Some(traffic_light_position) = self.traffic_light_position {
 729            let titlebar_height = self.titlebar_height();
 730
 731            unsafe {
 732                let close_button: id = msg_send![
 733                    self.native_window,
 734                    standardWindowButton: NSWindowButton::NSWindowCloseButton
 735                ];
 736                let min_button: id = msg_send![
 737                    self.native_window,
 738                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
 739                ];
 740                let zoom_button: id = msg_send![
 741                    self.native_window,
 742                    standardWindowButton: NSWindowButton::NSWindowZoomButton
 743                ];
 744
 745                let mut close_button_frame: CGRect = msg_send![close_button, frame];
 746                let mut min_button_frame: CGRect = msg_send![min_button, frame];
 747                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
 748                let mut origin = vec2f(
 749                    traffic_light_position.x(),
 750                    titlebar_height
 751                        - traffic_light_position.y()
 752                        - close_button_frame.size.height as f32,
 753                );
 754                let button_spacing =
 755                    (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
 756
 757                close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
 758                let _: () = msg_send![close_button, setFrame: close_button_frame];
 759                origin.set_x(origin.x() + button_spacing);
 760
 761                min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
 762                let _: () = msg_send![min_button, setFrame: min_button_frame];
 763                origin.set_x(origin.x() + button_spacing);
 764
 765                zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
 766                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
 767            }
 768        }
 769    }
 770
 771    fn bounds(&self) -> RectF {
 772        unsafe {
 773            let screen_frame = self.native_window.screen().visibleFrame();
 774            let window_frame = NSWindow::frame(self.native_window);
 775            let origin = vec2f(
 776                window_frame.origin.x as f32,
 777                (window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
 778                    as f32,
 779            );
 780            let size = vec2f(
 781                window_frame.size.width as f32,
 782                window_frame.size.height as f32,
 783            );
 784            RectF::new(origin, size)
 785        }
 786    }
 787
 788    fn content_size(&self) -> Vector2F {
 789        let NSSize { width, height, .. } =
 790            unsafe { NSView::frame(self.native_window.contentView()) }.size;
 791        vec2f(width as f32, height as f32)
 792    }
 793
 794    fn scale_factor(&self) -> f32 {
 795        get_scale_factor(self.native_window)
 796    }
 797
 798    fn titlebar_height(&self) -> f32 {
 799        unsafe {
 800            let frame = NSWindow::frame(self.native_window);
 801            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
 802            (frame.size.height - content_layout_rect.size.height) as f32
 803        }
 804    }
 805
 806    fn present_scene(&mut self, scene: Scene) {
 807        self.scene_to_render = Some(scene);
 808        unsafe {
 809            let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
 810        }
 811    }
 812}
 813
 814fn get_scale_factor(native_window: id) -> f32 {
 815    unsafe {
 816        let screen: id = msg_send![native_window, screen];
 817        NSScreen::backingScaleFactor(screen) as f32
 818    }
 819}
 820
 821unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
 822    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 823    let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
 824    let rc2 = rc1.clone();
 825    mem::forget(rc1);
 826    rc2
 827}
 828
 829unsafe fn drop_window_state(object: &Object) {
 830    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
 831    Rc::from_raw(raw as *mut RefCell<WindowState>);
 832}
 833
 834extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
 835    YES
 836}
 837
 838extern "C" fn dealloc_window(this: &Object, _: Sel) {
 839    unsafe {
 840        drop_window_state(this);
 841        let _: () = msg_send![super(this, class!(NSWindow)), dealloc];
 842    }
 843}
 844
 845extern "C" fn dealloc_view(this: &Object, _: Sel) {
 846    unsafe {
 847        drop_window_state(this);
 848        let _: () = msg_send![super(this, class!(NSView)), dealloc];
 849    }
 850}
 851
 852extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
 853    handle_key_event(this, native_event, true)
 854}
 855
 856extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
 857    handle_key_event(this, native_event, false);
 858}
 859
 860extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
 861    let window_state = unsafe { get_window_state(this) };
 862
 863    let mut window_state_borrow = window_state.as_ref().borrow_mut();
 864
 865    let event =
 866        unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) };
 867
 868    if let Some(event) = event {
 869        if key_equivalent {
 870            window_state_borrow.performed_key_equivalent = true;
 871        } else if window_state_borrow.performed_key_equivalent {
 872            return NO;
 873        }
 874
 875        let function_is_held;
 876        window_state_borrow.pending_key_down = match event {
 877            Event::KeyDown(event) => {
 878                let keydown = event.keystroke.clone();
 879                // Ignore events from held-down keys after some of the initially-pressed keys
 880                // were released.
 881                if event.is_held {
 882                    if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
 883                        return YES;
 884                    }
 885                } else {
 886                    window_state_borrow.last_fresh_keydown = Some(keydown);
 887                }
 888                function_is_held = event.keystroke.function;
 889                Some((event, None))
 890            }
 891            _ => return NO,
 892        };
 893
 894        drop(window_state_borrow);
 895
 896        if !function_is_held {
 897            unsafe {
 898                let input_context: id = msg_send![this, inputContext];
 899                let _: BOOL = msg_send![input_context, handleEvent: native_event];
 900            }
 901        }
 902
 903        let mut handled = false;
 904        let mut window_state_borrow = window_state.borrow_mut();
 905        let ime_text = window_state_borrow.ime_text.clone();
 906        if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
 907            let is_held = event.is_held;
 908            if let Some(mut callback) = window_state_borrow.event_callback.take() {
 909                drop(window_state_borrow);
 910
 911                let is_composing =
 912                    with_input_handler(this, |input_handler| input_handler.marked_text_range())
 913                        .flatten()
 914                        .is_some();
 915                if !is_composing {
 916                    handled = callback(Event::KeyDown(event));
 917                }
 918
 919                if !handled {
 920                    if let Some(insert) = insert_text {
 921                        handled = true;
 922                        with_input_handler(this, |input_handler| {
 923                            input_handler
 924                                .replace_text_in_range(insert.replacement_range, &insert.text)
 925                        });
 926                    } else if !is_composing && is_held {
 927                        if let Some(last_insert_text) = ime_text {
 928                            //MacOS IME is a bit funky, and even when you've told it there's nothing to
 929                            //inter it will still swallow certain keys (e.g. 'f', 'j') and not others
 930                            //(e.g. 'n'). This is a problem for certain kinds of views, like the terminal
 931                            with_input_handler(this, |input_handler| {
 932                                if input_handler.selected_text_range().is_none() {
 933                                    handled = true;
 934                                    input_handler.replace_text_in_range(None, &last_insert_text)
 935                                }
 936                            });
 937                        }
 938                    }
 939                }
 940
 941                window_state.borrow_mut().event_callback = Some(callback);
 942            }
 943        } else {
 944            handled = true;
 945        }
 946
 947        handled as BOOL
 948    } else {
 949        NO
 950    }
 951}
 952
 953extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
 954    let window_state = unsafe { get_window_state(this) };
 955    let weak_window_state = Rc::downgrade(&window_state);
 956    let mut window_state_borrow = window_state.as_ref().borrow_mut();
 957
 958    let event =
 959        unsafe { Event::from_native(native_event, Some(window_state_borrow.content_size().y())) };
 960    if let Some(event) = event {
 961        match &event {
 962            Event::MouseMoved(
 963                event @ MouseMovedEvent {
 964                    pressed_button: Some(_),
 965                    ..
 966                },
 967            ) => {
 968                window_state_borrow.synthetic_drag_counter += 1;
 969                window_state_borrow
 970                    .executor
 971                    .spawn(synthetic_drag(
 972                        weak_window_state,
 973                        window_state_borrow.synthetic_drag_counter,
 974                        *event,
 975                    ))
 976                    .detach();
 977            }
 978            Event::MouseUp(MouseButtonEvent {
 979                button: MouseButton::Left,
 980                ..
 981            }) => {
 982                window_state_borrow.synthetic_drag_counter += 1;
 983            }
 984            Event::ModifiersChanged(ModifiersChangedEvent {
 985                ctrl,
 986                alt,
 987                shift,
 988                cmd,
 989            }) => {
 990                // Only raise modifiers changed event when they have actually changed
 991                if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
 992                    ctrl: prev_ctrl,
 993                    alt: prev_alt,
 994                    shift: prev_shift,
 995                    cmd: prev_cmd,
 996                })) = &window_state_borrow.previous_modifiers_changed_event
 997                {
 998                    if prev_ctrl == ctrl
 999                        && prev_alt == alt
1000                        && prev_shift == shift
1001                        && prev_cmd == cmd
1002                    {
1003                        return;
1004                    }
1005                }
1006
1007                window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
1008            }
1009            _ => {}
1010        }
1011
1012        if let Some(mut callback) = window_state_borrow.event_callback.take() {
1013            drop(window_state_borrow);
1014            callback(event);
1015            window_state.borrow_mut().event_callback = Some(callback);
1016        }
1017    }
1018}
1019
1020// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
1021// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
1022extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
1023    let window_state = unsafe { get_window_state(this) };
1024    let mut window_state_borrow = window_state.as_ref().borrow_mut();
1025
1026    let keystroke = Keystroke {
1027        cmd: true,
1028        ctrl: false,
1029        alt: false,
1030        shift: false,
1031        function: false,
1032        key: ".".into(),
1033    };
1034    let event = Event::KeyDown(KeyDownEvent {
1035        keystroke: keystroke.clone(),
1036        is_held: false,
1037    });
1038
1039    window_state_borrow.last_fresh_keydown = Some(keystroke);
1040    if let Some(mut callback) = window_state_borrow.event_callback.take() {
1041        drop(window_state_borrow);
1042        callback(event);
1043        window_state.borrow_mut().event_callback = Some(callback);
1044    }
1045}
1046
1047extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
1048    unsafe {
1049        let _: () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
1050        get_window_state(this).borrow_mut().performed_key_equivalent = false;
1051    }
1052}
1053
1054extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
1055    let window_state = unsafe { get_window_state(this) };
1056    window_state.as_ref().borrow().move_traffic_light();
1057}
1058
1059extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
1060    window_fullscreen_changed(this, true);
1061}
1062
1063extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
1064    window_fullscreen_changed(this, false);
1065}
1066
1067fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
1068    let window_state = unsafe { get_window_state(this) };
1069    let mut window_state_borrow = window_state.as_ref().borrow_mut();
1070    if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
1071        drop(window_state_borrow);
1072        callback(is_fullscreen);
1073        window_state.borrow_mut().fullscreen_callback = Some(callback);
1074    }
1075}
1076
1077extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
1078    let window_state = unsafe { get_window_state(this) };
1079    let window_state_borrow = window_state.borrow();
1080    let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
1081
1082    // When opening a pop-up while the application isn't active, Cocoa sends a spurious
1083    // `windowDidBecomeKey` message to the previous key window even though that window
1084    // isn't actually key. This causes a bug if the application is later activated while
1085    // the pop-up is still open, making it impossible to activate the previous key window
1086    // even if the pop-up gets closed. The only way to activate it again is to de-activate
1087    // the app and re-activate it, which is a pretty bad UX.
1088    // The following code detects the spurious event and invokes `resignKeyWindow`:
1089    // in theory, we're not supposed to invoke this method manually but it balances out
1090    // the spurious `becomeKeyWindow` event and helps us work around that bug.
1091    if selector == sel!(windowDidBecomeKey:) {
1092        if !is_active {
1093            unsafe {
1094                let _: () = msg_send![window_state_borrow.native_window, resignKeyWindow];
1095                return;
1096            }
1097        }
1098    }
1099
1100    let executor = window_state_borrow.executor.clone();
1101    drop(window_state_borrow);
1102    executor
1103        .spawn(async move {
1104            let mut window_state_borrow = window_state.as_ref().borrow_mut();
1105            if let Some(mut callback) = window_state_borrow.activate_callback.take() {
1106                drop(window_state_borrow);
1107                callback(is_active);
1108                window_state.borrow_mut().activate_callback = Some(callback);
1109            };
1110        })
1111        .detach();
1112}
1113
1114extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
1115    let window_state = unsafe { get_window_state(this) };
1116    let mut window_state_borrow = window_state.as_ref().borrow_mut();
1117    if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
1118        drop(window_state_borrow);
1119        let should_close = callback();
1120        window_state.borrow_mut().should_close_callback = Some(callback);
1121        should_close as BOOL
1122    } else {
1123        YES
1124    }
1125}
1126
1127extern "C" fn close_window(this: &Object, _: Sel) {
1128    unsafe {
1129        let close_callback = {
1130            let window_state = get_window_state(this);
1131            window_state
1132                .as_ref()
1133                .try_borrow_mut()
1134                .ok()
1135                .and_then(|mut window_state| window_state.close_callback.take())
1136        };
1137
1138        if let Some(callback) = close_callback {
1139            callback();
1140        }
1141
1142        let _: () = msg_send![super(this, class!(NSWindow)), close];
1143    }
1144}
1145
1146extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
1147    let window_state = unsafe { get_window_state(this) };
1148    let window_state = window_state.as_ref().borrow();
1149    window_state.renderer.layer().as_ptr() as id
1150}
1151
1152extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
1153    let window_state = unsafe { get_window_state(this) };
1154    let mut window_state_borrow = window_state.as_ref().borrow_mut();
1155
1156    unsafe {
1157        let scale_factor = window_state_borrow.scale_factor() as f64;
1158        let size = window_state_borrow.content_size();
1159        let drawable_size: NSSize = NSSize {
1160            width: size.x() as f64 * scale_factor,
1161            height: size.y() as f64 * scale_factor,
1162        };
1163
1164        let _: () = msg_send![
1165            window_state_borrow.renderer.layer(),
1166            setContentsScale: scale_factor
1167        ];
1168        let _: () = msg_send![
1169            window_state_borrow.renderer.layer(),
1170            setDrawableSize: drawable_size
1171        ];
1172    }
1173
1174    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1175        drop(window_state_borrow);
1176        callback();
1177        window_state.as_ref().borrow_mut().resize_callback = Some(callback);
1178    };
1179}
1180
1181extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
1182    let window_state = unsafe { get_window_state(this) };
1183    let window_state_borrow = window_state.as_ref().borrow();
1184
1185    if window_state_borrow.content_size() == vec2f(size.width as f32, size.height as f32) {
1186        return;
1187    }
1188
1189    unsafe {
1190        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
1191    }
1192
1193    let scale_factor = window_state_borrow.scale_factor() as f64;
1194    let drawable_size: NSSize = NSSize {
1195        width: size.width * scale_factor,
1196        height: size.height * scale_factor,
1197    };
1198
1199    unsafe {
1200        let _: () = msg_send![
1201            window_state_borrow.renderer.layer(),
1202            setDrawableSize: drawable_size
1203        ];
1204    }
1205
1206    drop(window_state_borrow);
1207    let mut window_state_borrow = window_state.borrow_mut();
1208    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
1209        drop(window_state_borrow);
1210        callback();
1211        window_state.borrow_mut().resize_callback = Some(callback);
1212    };
1213}
1214
1215extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
1216    unsafe {
1217        let window_state = get_window_state(this);
1218        let mut window_state = window_state.as_ref().borrow_mut();
1219        if let Some(scene) = window_state.scene_to_render.take() {
1220            window_state.renderer.render(&scene);
1221        };
1222    }
1223}
1224
1225extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
1226    unsafe { msg_send![class!(NSArray), array] }
1227}
1228
1229extern "C" fn has_marked_text(this: &Object, _: Sel) -> BOOL {
1230    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1231        .flatten()
1232        .is_some() as BOOL
1233}
1234
1235extern "C" fn marked_range(this: &Object, _: Sel) -> NSRange {
1236    with_input_handler(this, |input_handler| input_handler.marked_text_range())
1237        .flatten()
1238        .map_or(NSRange::invalid(), |range| range.into())
1239}
1240
1241extern "C" fn selected_range(this: &Object, _: Sel) -> NSRange {
1242    with_input_handler(this, |input_handler| input_handler.selected_text_range())
1243        .flatten()
1244        .map_or(NSRange::invalid(), |range| range.into())
1245}
1246
1247extern "C" fn first_rect_for_character_range(
1248    this: &Object,
1249    _: Sel,
1250    range: NSRange,
1251    _: id,
1252) -> NSRect {
1253    let frame = unsafe {
1254        let window = get_window_state(this).borrow().native_window;
1255        NSView::frame(window)
1256    };
1257    with_input_handler(this, |input_handler| {
1258        input_handler.rect_for_range(range.to_range()?)
1259    })
1260    .flatten()
1261    .map_or(
1262        NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)),
1263        |rect| {
1264            NSRect::new(
1265                NSPoint::new(
1266                    frame.origin.x + rect.origin_x() as f64,
1267                    frame.origin.y + frame.size.height - rect.origin_y() as f64,
1268                ),
1269                NSSize::new(rect.width() as f64, rect.height() as f64),
1270            )
1271        },
1272    )
1273}
1274
1275extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
1276    unsafe {
1277        let window_state = get_window_state(this);
1278        let mut window_state_borrow = window_state.borrow_mut();
1279        let pending_key_down = window_state_borrow.pending_key_down.take();
1280        drop(window_state_borrow);
1281
1282        let is_attributed_string: BOOL =
1283            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1284        let text: id = if is_attributed_string == YES {
1285            msg_send![text, string]
1286        } else {
1287            text
1288        };
1289        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1290            .to_str()
1291            .unwrap();
1292        let replacement_range = replacement_range.to_range();
1293
1294        window_state.borrow_mut().ime_text = Some(text.to_string());
1295        window_state.borrow_mut().ime_state = ImeState::Acted;
1296
1297        let is_composing =
1298            with_input_handler(this, |input_handler| input_handler.marked_text_range())
1299                .flatten()
1300                .is_some();
1301
1302        if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
1303            with_input_handler(this, |input_handler| {
1304                input_handler.replace_text_in_range(replacement_range, text)
1305            });
1306        } else {
1307            let mut pending_key_down = pending_key_down.unwrap();
1308            pending_key_down.1 = Some(InsertText {
1309                replacement_range,
1310                text: text.to_string(),
1311            });
1312            window_state.borrow_mut().pending_key_down = Some(pending_key_down);
1313        }
1314    }
1315}
1316
1317extern "C" fn set_marked_text(
1318    this: &Object,
1319    _: Sel,
1320    text: id,
1321    selected_range: NSRange,
1322    replacement_range: NSRange,
1323) {
1324    unsafe {
1325        let window_state = get_window_state(this);
1326        window_state.borrow_mut().pending_key_down.take();
1327
1328        let is_attributed_string: BOOL =
1329            msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
1330        let text: id = if is_attributed_string == YES {
1331            msg_send![text, string]
1332        } else {
1333            text
1334        };
1335        let selected_range = selected_range.to_range();
1336        let replacement_range = replacement_range.to_range();
1337        let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
1338            .to_str()
1339            .unwrap();
1340
1341        window_state.borrow_mut().ime_state = ImeState::Acted;
1342        window_state.borrow_mut().ime_text = Some(text.to_string());
1343
1344        with_input_handler(this, |input_handler| {
1345            input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
1346        });
1347    }
1348}
1349
1350extern "C" fn unmark_text(this: &Object, _: Sel) {
1351    unsafe {
1352        let state = get_window_state(this);
1353        let mut borrow = state.borrow_mut();
1354        borrow.ime_state = ImeState::Acted;
1355        borrow.ime_text.take();
1356    }
1357
1358    with_input_handler(this, |input_handler| input_handler.unmark_text());
1359}
1360
1361extern "C" fn attributed_substring_for_proposed_range(
1362    this: &Object,
1363    _: Sel,
1364    range: NSRange,
1365    _actual_range: *mut c_void,
1366) -> id {
1367    with_input_handler(this, |input_handler| {
1368        let range = range.to_range()?;
1369        if range.is_empty() {
1370            return None;
1371        }
1372
1373        let selected_text = input_handler.text_for_range(range)?;
1374        unsafe {
1375            let string: id = msg_send![class!(NSAttributedString), alloc];
1376            let string: id = msg_send![string, initWithString: ns_string(&selected_text)];
1377            Some(string)
1378        }
1379    })
1380    .flatten()
1381    .unwrap_or(nil)
1382}
1383
1384extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
1385    unsafe {
1386        let state = get_window_state(this);
1387        let mut borrow = state.borrow_mut();
1388        borrow.ime_state = ImeState::Continue;
1389        borrow.ime_text.take();
1390    }
1391}
1392
1393extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
1394    unsafe {
1395        let state = get_window_state(this);
1396        let mut state_borrow = state.as_ref().borrow_mut();
1397        if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
1398            drop(state_borrow);
1399            callback();
1400            state.borrow_mut().appearance_changed_callback = Some(callback);
1401        }
1402    }
1403}
1404
1405async fn synthetic_drag(
1406    window_state: Weak<RefCell<WindowState>>,
1407    drag_id: usize,
1408    event: MouseMovedEvent,
1409) {
1410    loop {
1411        Timer::after(Duration::from_millis(16)).await;
1412        if let Some(window_state) = window_state.upgrade() {
1413            let mut window_state_borrow = window_state.borrow_mut();
1414            if window_state_borrow.synthetic_drag_counter == drag_id {
1415                if let Some(mut callback) = window_state_borrow.event_callback.take() {
1416                    drop(window_state_borrow);
1417                    callback(Event::MouseMoved(event));
1418                    window_state.borrow_mut().event_callback = Some(callback);
1419                }
1420            } else {
1421                break;
1422            }
1423        }
1424    }
1425}
1426
1427unsafe fn ns_string(string: &str) -> id {
1428    NSString::alloc(nil).init_str(string).autorelease()
1429}
1430
1431fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
1432where
1433    F: FnOnce(&mut dyn InputHandler) -> R,
1434{
1435    let window_state = unsafe { get_window_state(window) };
1436    let mut window_state_borrow = window_state.as_ref().borrow_mut();
1437    if let Some(mut input_handler) = window_state_borrow.input_handler.take() {
1438        drop(window_state_borrow);
1439        let result = f(input_handler.as_mut());
1440        window_state.borrow_mut().input_handler = Some(input_handler);
1441        Some(result)
1442    } else {
1443        None
1444    }
1445}