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