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