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