window.rs

  1use crate::{
  2    executor,
  3    geometry::{
  4        rect::RectF,
  5        vector::{vec2f, Vector2F},
  6    },
  7    keymap::Keystroke,
  8    platform::{self, Event, WindowBounds, WindowContext},
  9    KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseEvent, MouseMovedEvent, Scene,
 10};
 11use block::ConcreteBlock;
 12use cocoa::{
 13    appkit::{
 14        CGPoint, NSApplication, NSBackingStoreBuffered, NSModalResponse, NSScreen, NSView,
 15        NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowStyleMask,
 16    },
 17    base::{id, nil},
 18    foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString},
 19    quartzcore::AutoresizingMask,
 20};
 21use core_graphics::display::CGRect;
 22use ctor::ctor;
 23use foreign_types::ForeignType as _;
 24use objc::{
 25    class,
 26    declare::ClassDecl,
 27    msg_send,
 28    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
 29    sel, sel_impl,
 30};
 31use postage::oneshot;
 32use smol::Timer;
 33use std::{
 34    any::Any,
 35    cell::{Cell, RefCell},
 36    convert::TryInto,
 37    ffi::c_void,
 38    mem, ptr,
 39    rc::{Rc, Weak},
 40    sync::Arc,
 41    time::Duration,
 42};
 43
 44use super::{geometry::RectFExt, renderer::Renderer};
 45
 46const WINDOW_STATE_IVAR: &'static str = "windowState";
 47
 48static mut WINDOW_CLASS: *const Class = ptr::null();
 49static mut VIEW_CLASS: *const Class = ptr::null();
 50
 51#[allow(non_upper_case_globals)]
 52const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
 53
 54#[ctor]
 55unsafe fn build_classes() {
 56    WINDOW_CLASS = {
 57        let mut decl = ClassDecl::new("GPUIWindow", class!(NSWindow)).unwrap();
 58        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 59        decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
 60        decl.add_method(
 61            sel!(canBecomeMainWindow),
 62            yes as extern "C" fn(&Object, Sel) -> BOOL,
 63        );
 64        decl.add_method(
 65            sel!(canBecomeKeyWindow),
 66            yes as extern "C" fn(&Object, Sel) -> BOOL,
 67        );
 68        decl.add_method(
 69            sel!(sendEvent:),
 70            send_event as extern "C" fn(&Object, Sel, id),
 71        );
 72        decl.add_method(
 73            sel!(windowDidResize:),
 74            window_did_resize as extern "C" fn(&Object, Sel, id),
 75        );
 76        decl.add_method(
 77            sel!(windowDidBecomeKey:),
 78            window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 79        );
 80        decl.add_method(
 81            sel!(windowDidResignKey:),
 82            window_did_change_key_status as extern "C" fn(&Object, Sel, id),
 83        );
 84        decl.add_method(
 85            sel!(windowShouldClose:),
 86            window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
 87        );
 88        decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
 89        decl.register()
 90    };
 91
 92    VIEW_CLASS = {
 93        let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
 94        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
 95
 96        decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
 97
 98        decl.add_method(
 99            sel!(performKeyEquivalent:),
100            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
101        );
102        decl.add_method(
103            sel!(mouseDown:),
104            handle_view_event as extern "C" fn(&Object, Sel, id),
105        );
106        decl.add_method(
107            sel!(mouseUp:),
108            handle_view_event as extern "C" fn(&Object, Sel, id),
109        );
110        decl.add_method(
111            sel!(rightMouseDown:),
112            handle_view_event as extern "C" fn(&Object, Sel, id),
113        );
114        decl.add_method(
115            sel!(rightMouseUp:),
116            handle_view_event as extern "C" fn(&Object, Sel, id),
117        );
118        decl.add_method(
119            sel!(otherMouseDown:),
120            handle_view_event as extern "C" fn(&Object, Sel, id),
121        );
122        decl.add_method(
123            sel!(otherMouseUp:),
124            handle_view_event as extern "C" fn(&Object, Sel, id),
125        );
126        decl.add_method(
127            sel!(mouseMoved:),
128            handle_view_event as extern "C" fn(&Object, Sel, id),
129        );
130        decl.add_method(
131            sel!(mouseDragged:),
132            handle_view_event as extern "C" fn(&Object, Sel, id),
133        );
134        decl.add_method(
135            sel!(scrollWheel:),
136            handle_view_event as extern "C" fn(&Object, Sel, id),
137        );
138        decl.add_method(
139            sel!(flagsChanged:),
140            handle_view_event as extern "C" fn(&Object, Sel, id),
141        );
142        decl.add_method(
143            sel!(cancelOperation:),
144            cancel_operation as extern "C" fn(&Object, Sel, id),
145        );
146
147        decl.add_method(
148            sel!(makeBackingLayer),
149            make_backing_layer as extern "C" fn(&Object, Sel) -> id,
150        );
151
152        decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
153        decl.add_method(
154            sel!(viewDidChangeBackingProperties),
155            view_did_change_backing_properties as extern "C" fn(&Object, Sel),
156        );
157        decl.add_method(
158            sel!(setFrameSize:),
159            set_frame_size as extern "C" fn(&Object, Sel, NSSize),
160        );
161        decl.add_method(
162            sel!(displayLayer:),
163            display_layer as extern "C" fn(&Object, Sel, id),
164        );
165
166        decl.register()
167    };
168}
169
170pub struct Window(Rc<RefCell<WindowState>>);
171
172struct WindowState {
173    id: usize,
174    native_window: id,
175    event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
176    activate_callback: Option<Box<dyn FnMut(bool)>>,
177    resize_callback: Option<Box<dyn FnMut()>>,
178    should_close_callback: Option<Box<dyn FnMut() -> bool>>,
179    close_callback: Option<Box<dyn FnOnce()>>,
180    synthetic_drag_counter: usize,
181    executor: Rc<executor::Foreground>,
182    scene_to_render: Option<Scene>,
183    renderer: Renderer,
184    command_queue: metal::CommandQueue,
185    last_fresh_keydown: Option<(Keystroke, Option<String>)>,
186    layer: id,
187    traffic_light_position: Option<Vector2F>,
188    previous_modifiers_changed_event: Option<Event>,
189}
190
191impl Window {
192    pub fn open(
193        id: usize,
194        options: platform::WindowOptions,
195        executor: Rc<executor::Foreground>,
196        fonts: Arc<dyn platform::FontSystem>,
197    ) -> Self {
198        const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm;
199
200        unsafe {
201            let pool = NSAutoreleasePool::new(nil);
202
203            let frame = match options.bounds {
204                WindowBounds::Maximized => RectF::new(Default::default(), vec2f(1024., 768.)),
205                WindowBounds::Fixed(rect) => rect,
206            }
207            .to_ns_rect();
208            let mut style_mask = NSWindowStyleMask::NSClosableWindowMask
209                | NSWindowStyleMask::NSMiniaturizableWindowMask
210                | NSWindowStyleMask::NSResizableWindowMask
211                | NSWindowStyleMask::NSTitledWindowMask;
212
213            if options.titlebar_appears_transparent {
214                style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
215            }
216
217            let native_window: id = msg_send![WINDOW_CLASS, alloc];
218            let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
219                frame,
220                style_mask,
221                NSBackingStoreBuffered,
222                NO,
223            );
224            assert!(!native_window.is_null());
225
226            if matches!(options.bounds, WindowBounds::Maximized) {
227                let screen = native_window.screen();
228                native_window.setFrame_display_(screen.visibleFrame(), YES);
229            }
230
231            let device = if let Some(device) = metal::Device::system_default() {
232                device
233            } else {
234                let alert: id = msg_send![class!(NSAlert), alloc];
235                let _: () = msg_send![alert, init];
236                let _: () = msg_send![alert, setAlertStyle: 2];
237                let _: () = msg_send![alert, setMessageText: ns_string("Unable to access a compatible graphics device")];
238                let _: NSModalResponse = msg_send![alert, runModal];
239                std::process::exit(1);
240            };
241
242            let layer: id = msg_send![class!(CAMetalLayer), layer];
243            let _: () = msg_send![layer, setDevice: device.as_ptr()];
244            let _: () = msg_send![layer, setPixelFormat: PIXEL_FORMAT];
245            let _: () = msg_send![layer, setAllowsNextDrawableTimeout: NO];
246            let _: () = msg_send![layer, setNeedsDisplayOnBoundsChange: YES];
247            let _: () = msg_send![layer, setPresentsWithTransaction: YES];
248            let _: () = msg_send![
249                layer,
250                setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
251                    | AutoresizingMask::HEIGHT_SIZABLE
252            ];
253
254            let native_view: id = msg_send![VIEW_CLASS, alloc];
255            let native_view = NSView::init(native_view);
256            assert!(!native_view.is_null());
257
258            let window = Self(Rc::new(RefCell::new(WindowState {
259                id,
260                native_window,
261                event_callback: None,
262                resize_callback: None,
263                should_close_callback: None,
264                close_callback: None,
265                activate_callback: None,
266                synthetic_drag_counter: 0,
267                executor,
268                scene_to_render: Default::default(),
269                renderer: Renderer::new(
270                    device.clone(),
271                    PIXEL_FORMAT,
272                    get_scale_factor(native_window),
273                    fonts,
274                ),
275                command_queue: device.new_command_queue(),
276                last_fresh_keydown: None,
277                layer,
278                traffic_light_position: options.traffic_light_position,
279                previous_modifiers_changed_event: None,
280            })));
281
282            (*native_window).set_ivar(
283                WINDOW_STATE_IVAR,
284                Rc::into_raw(window.0.clone()) as *const c_void,
285            );
286            native_window.setDelegate_(native_window);
287            (*native_view).set_ivar(
288                WINDOW_STATE_IVAR,
289                Rc::into_raw(window.0.clone()) as *const c_void,
290            );
291
292            if let Some(title) = options.title.as_ref() {
293                native_window.setTitle_(NSString::alloc(nil).init_str(title));
294            }
295            if options.titlebar_appears_transparent {
296                native_window.setTitlebarAppearsTransparent_(YES);
297            }
298            native_window.setAcceptsMouseMovedEvents_(YES);
299
300            native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
301            native_view.setWantsBestResolutionOpenGLSurface_(YES);
302
303            // From winit crate: On Mojave, views automatically become layer-backed shortly after
304            // being added to a native_window. Changing the layer-backedness of a view breaks the
305            // association between the view and its associated OpenGL context. To work around this,
306            // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
307            // itself and break the association with its context.
308            native_view.setWantsLayer(YES);
309            let _: () = msg_send![
310                native_view,
311                setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
312            ];
313
314            native_window.setContentView_(native_view.autorelease());
315            native_window.makeFirstResponder_(native_view);
316
317            native_window.center();
318            native_window.makeKeyAndOrderFront_(nil);
319
320            window.0.borrow().move_traffic_light();
321            pool.drain();
322
323            window
324        }
325    }
326
327    pub fn key_window_id() -> Option<usize> {
328        unsafe {
329            let app = NSApplication::sharedApplication(nil);
330            let key_window: id = msg_send![app, keyWindow];
331            if msg_send![key_window, isKindOfClass: WINDOW_CLASS] {
332                let id = get_window_state(&*key_window).borrow().id;
333                Some(id)
334            } else {
335                None
336            }
337        }
338    }
339}
340
341impl Drop for Window {
342    fn drop(&mut self) {
343        unsafe {
344            self.0.as_ref().borrow().native_window.close();
345        }
346    }
347}
348
349impl platform::Window for Window {
350    fn as_any_mut(&mut self) -> &mut dyn Any {
351        self
352    }
353
354    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
355        self.0.as_ref().borrow_mut().event_callback = Some(callback);
356    }
357
358    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
359        self.0.as_ref().borrow_mut().resize_callback = Some(callback);
360    }
361
362    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
363        self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
364    }
365
366    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
367        self.0.as_ref().borrow_mut().close_callback = Some(callback);
368    }
369
370    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
371        self.0.as_ref().borrow_mut().activate_callback = Some(callback);
372    }
373
374    fn prompt(
375        &self,
376        level: platform::PromptLevel,
377        msg: &str,
378        answers: &[&str],
379    ) -> oneshot::Receiver<usize> {
380        unsafe {
381            let alert: id = msg_send![class!(NSAlert), alloc];
382            let alert: id = msg_send![alert, init];
383            let alert_style = match level {
384                platform::PromptLevel::Info => 1,
385                platform::PromptLevel::Warning => 0,
386                platform::PromptLevel::Critical => 2,
387            };
388            let _: () = msg_send![alert, setAlertStyle: alert_style];
389            let _: () = msg_send![alert, setMessageText: ns_string(msg)];
390            for (ix, answer) in answers.into_iter().enumerate() {
391                let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
392                let _: () = msg_send![button, setTag: ix as NSInteger];
393            }
394            let (done_tx, done_rx) = oneshot::channel();
395            let done_tx = Cell::new(Some(done_tx));
396            let block = ConcreteBlock::new(move |answer: NSInteger| {
397                if let Some(mut done_tx) = done_tx.take() {
398                    let _ = postage::sink::Sink::try_send(&mut done_tx, answer.try_into().unwrap());
399                }
400            });
401            let block = block.copy();
402            let native_window = self.0.borrow().native_window;
403            self.0
404                .borrow()
405                .executor
406                .spawn(async move {
407                    let _: () = msg_send![
408                        alert,
409                        beginSheetModalForWindow: native_window
410                        completionHandler: block
411                    ];
412                })
413                .detach();
414
415            done_rx
416        }
417    }
418
419    fn activate(&self) {
420        let window = self.0.borrow().native_window;
421        self.0
422            .borrow()
423            .executor
424            .spawn(async move {
425                unsafe {
426                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
427                }
428            })
429            .detach();
430    }
431
432    fn set_title(&mut self, title: &str) {
433        unsafe {
434            let app = NSApplication::sharedApplication(nil);
435            let window = self.0.borrow().native_window;
436            let title = ns_string(title);
437            msg_send![app, changeWindowsItem:window title:title filename:false]
438        }
439    }
440
441    fn set_edited(&mut self, edited: bool) {
442        unsafe {
443            let window = self.0.borrow().native_window;
444            msg_send![window, setDocumentEdited: edited as BOOL]
445        }
446
447        // Changing the document edited state resets the traffic light position,
448        // so we have to move it again.
449        self.0.borrow().move_traffic_light();
450    }
451}
452
453impl platform::WindowContext for Window {
454    fn size(&self) -> Vector2F {
455        self.0.as_ref().borrow().size()
456    }
457
458    fn scale_factor(&self) -> f32 {
459        self.0.as_ref().borrow().scale_factor()
460    }
461
462    fn present_scene(&mut self, scene: Scene) {
463        self.0.as_ref().borrow_mut().present_scene(scene);
464    }
465
466    fn titlebar_height(&self) -> f32 {
467        self.0.as_ref().borrow().titlebar_height()
468    }
469}
470
471impl WindowState {
472    fn move_traffic_light(&self) {
473        if let Some(traffic_light_position) = self.traffic_light_position {
474            let titlebar_height = self.titlebar_height();
475
476            unsafe {
477                let close_button: id = msg_send![
478                    self.native_window,
479                    standardWindowButton: NSWindowButton::NSWindowCloseButton
480                ];
481                let min_button: id = msg_send![
482                    self.native_window,
483                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
484                ];
485                let zoom_button: id = msg_send![
486                    self.native_window,
487                    standardWindowButton: NSWindowButton::NSWindowZoomButton
488                ];
489
490                let mut close_button_frame: CGRect = msg_send![close_button, frame];
491                let mut min_button_frame: CGRect = msg_send![min_button, frame];
492                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
493                let mut origin = vec2f(
494                    traffic_light_position.x(),
495                    titlebar_height
496                        - traffic_light_position.y()
497                        - close_button_frame.size.height as f32,
498                );
499                let button_spacing =
500                    (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
501
502                close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
503                let _: () = msg_send![close_button, setFrame: close_button_frame];
504                origin.set_x(origin.x() + button_spacing);
505
506                min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
507                let _: () = msg_send![min_button, setFrame: min_button_frame];
508                origin.set_x(origin.x() + button_spacing);
509
510                zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
511                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
512            }
513        }
514    }
515}
516
517impl platform::WindowContext for WindowState {
518    fn size(&self) -> Vector2F {
519        let NSSize { width, height, .. } =
520            unsafe { NSView::frame(self.native_window.contentView()) }.size;
521        vec2f(width as f32, height as f32)
522    }
523
524    fn scale_factor(&self) -> f32 {
525        get_scale_factor(self.native_window)
526    }
527
528    fn titlebar_height(&self) -> f32 {
529        unsafe {
530            let frame = NSWindow::frame(self.native_window);
531            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
532            (frame.size.height - content_layout_rect.size.height) as f32
533        }
534    }
535
536    fn present_scene(&mut self, scene: Scene) {
537        self.scene_to_render = Some(scene);
538        unsafe {
539            let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
540        }
541    }
542}
543
544fn get_scale_factor(native_window: id) -> f32 {
545    unsafe {
546        let screen: id = msg_send![native_window, screen];
547        NSScreen::backingScaleFactor(screen) as f32
548    }
549}
550
551unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
552    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
553    let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
554    let rc2 = rc1.clone();
555    mem::forget(rc1);
556    rc2
557}
558
559unsafe fn drop_window_state(object: &Object) {
560    let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
561    Rc::from_raw(raw as *mut RefCell<WindowState>);
562}
563
564extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
565    YES
566}
567
568extern "C" fn dealloc_window(this: &Object, _: Sel) {
569    unsafe {
570        drop_window_state(this);
571        let () = msg_send![super(this, class!(NSWindow)), dealloc];
572    }
573}
574
575extern "C" fn dealloc_view(this: &Object, _: Sel) {
576    unsafe {
577        drop_window_state(this);
578        let () = msg_send![super(this, class!(NSView)), dealloc];
579    }
580}
581
582extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
583    let window_state = unsafe { get_window_state(this) };
584    let mut window_state_borrow = window_state.as_ref().borrow_mut();
585
586    let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
587    if let Some(event) = event {
588        match &event {
589            Event::KeyDown(KeyDownEvent {
590                keystroke,
591                input,
592                is_held,
593            }) => {
594                let keydown = (keystroke.clone(), input.clone());
595                // Ignore events from held-down keys after some of the initially-pressed keys
596                // were released.
597                if *is_held {
598                    if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
599                        return YES;
600                    }
601                } else {
602                    window_state_borrow.last_fresh_keydown = Some(keydown);
603                }
604            }
605            _ => return NO,
606        }
607
608        if let Some(mut callback) = window_state_borrow.event_callback.take() {
609            drop(window_state_borrow);
610            let handled = callback(event);
611            window_state.borrow_mut().event_callback = Some(callback);
612            handled as BOOL
613        } else {
614            NO
615        }
616    } else {
617        NO
618    }
619}
620
621extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
622    let window_state = unsafe { get_window_state(this) };
623    let weak_window_state = Rc::downgrade(&window_state);
624    let mut window_state_borrow = window_state.as_ref().borrow_mut();
625
626    let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
627
628    if let Some(event) = event {
629        match &event {
630            Event::MouseMoved(
631                event @ MouseMovedEvent {
632                    pressed_button: Some(_),
633                    ..
634                },
635            ) => {
636                window_state_borrow.synthetic_drag_counter += 1;
637                window_state_borrow
638                    .executor
639                    .spawn(synthetic_drag(
640                        weak_window_state,
641                        window_state_borrow.synthetic_drag_counter,
642                        *event,
643                    ))
644                    .detach();
645            }
646            Event::MouseUp(MouseEvent {
647                button: MouseButton::Left,
648                ..
649            }) => {
650                window_state_borrow.synthetic_drag_counter += 1;
651            }
652            Event::ModifiersChanged(ModifiersChangedEvent {
653                ctrl,
654                alt,
655                shift,
656                cmd,
657            }) => {
658                // Only raise modifiers changed event when they have actually changed
659                if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
660                    ctrl: prev_ctrl,
661                    alt: prev_alt,
662                    shift: prev_shift,
663                    cmd: prev_cmd,
664                })) = &window_state_borrow.previous_modifiers_changed_event
665                {
666                    if prev_ctrl == ctrl
667                        && prev_alt == alt
668                        && prev_shift == shift
669                        && prev_cmd == cmd
670                    {
671                        return;
672                    }
673                }
674
675                window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
676            }
677            _ => {}
678        }
679
680        if let Some(mut callback) = window_state_borrow.event_callback.take() {
681            drop(window_state_borrow);
682            callback(event);
683            window_state.borrow_mut().event_callback = Some(callback);
684        }
685    }
686}
687
688// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
689// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
690extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
691    let window_state = unsafe { get_window_state(this) };
692    let mut window_state_borrow = window_state.as_ref().borrow_mut();
693
694    let chars = ".".to_string();
695    let keystroke = Keystroke {
696        cmd: true,
697        ctrl: false,
698        alt: false,
699        shift: false,
700        key: chars.clone(),
701    };
702    let event = Event::KeyDown(KeyDownEvent {
703        keystroke: keystroke.clone(),
704        input: Some(chars.clone()),
705        is_held: false,
706    });
707
708    window_state_borrow.last_fresh_keydown = Some((keystroke, Some(chars)));
709    if let Some(mut callback) = window_state_borrow.event_callback.take() {
710        drop(window_state_borrow);
711        callback(event);
712        window_state.borrow_mut().event_callback = Some(callback);
713    }
714}
715
716extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
717    unsafe {
718        let () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
719    }
720}
721
722extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
723    let window_state = unsafe { get_window_state(this) };
724    window_state.as_ref().borrow().move_traffic_light();
725}
726
727extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
728    let is_active = if selector == sel!(windowDidBecomeKey:) {
729        true
730    } else if selector == sel!(windowDidResignKey:) {
731        false
732    } else {
733        unreachable!();
734    };
735
736    let window_state = unsafe { get_window_state(this) };
737    let executor = window_state.as_ref().borrow().executor.clone();
738    executor
739        .spawn(async move {
740            let mut window_state_borrow = window_state.as_ref().borrow_mut();
741            if let Some(mut callback) = window_state_borrow.activate_callback.take() {
742                drop(window_state_borrow);
743                callback(is_active);
744                window_state.borrow_mut().activate_callback = Some(callback);
745            };
746        })
747        .detach();
748}
749
750extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
751    let window_state = unsafe { get_window_state(this) };
752    let mut window_state_borrow = window_state.as_ref().borrow_mut();
753    if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
754        drop(window_state_borrow);
755        let should_close = callback();
756        window_state.borrow_mut().should_close_callback = Some(callback);
757        should_close as BOOL
758    } else {
759        YES
760    }
761}
762
763extern "C" fn close_window(this: &Object, _: Sel) {
764    unsafe {
765        let close_callback = {
766            let window_state = get_window_state(this);
767            window_state
768                .as_ref()
769                .try_borrow_mut()
770                .ok()
771                .and_then(|mut window_state| window_state.close_callback.take())
772        };
773
774        if let Some(callback) = close_callback {
775            callback();
776        }
777
778        let () = msg_send![super(this, class!(NSWindow)), close];
779    }
780}
781
782extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
783    let window_state = unsafe { get_window_state(this) };
784    let window_state = window_state.as_ref().borrow();
785    window_state.layer
786}
787
788extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
789    let window_state = unsafe { get_window_state(this) };
790    let mut window_state_borrow = window_state.as_ref().borrow_mut();
791
792    unsafe {
793        let scale_factor = window_state_borrow.scale_factor() as f64;
794        let size = window_state_borrow.size();
795        let drawable_size: NSSize = NSSize {
796            width: size.x() as f64 * scale_factor,
797            height: size.y() as f64 * scale_factor,
798        };
799
800        let _: () = msg_send![window_state_borrow.layer, setContentsScale: scale_factor];
801        let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
802    }
803
804    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
805        drop(window_state_borrow);
806        callback();
807        window_state.as_ref().borrow_mut().resize_callback = Some(callback);
808    };
809}
810
811extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
812    let window_state = unsafe { get_window_state(this) };
813    let window_state_borrow = window_state.as_ref().borrow();
814
815    if window_state_borrow.size() == vec2f(size.width as f32, size.height as f32) {
816        return;
817    }
818
819    unsafe {
820        let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
821    }
822
823    let scale_factor = window_state_borrow.scale_factor() as f64;
824    let drawable_size: NSSize = NSSize {
825        width: size.width * scale_factor,
826        height: size.height * scale_factor,
827    };
828
829    unsafe {
830        let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
831    }
832
833    drop(window_state_borrow);
834    let mut window_state_borrow = window_state.borrow_mut();
835    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
836        drop(window_state_borrow);
837        callback();
838        window_state.borrow_mut().resize_callback = Some(callback);
839    };
840}
841
842extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
843    unsafe {
844        let window_state = get_window_state(this);
845        let mut window_state = window_state.as_ref().borrow_mut();
846
847        if let Some(scene) = window_state.scene_to_render.take() {
848            let drawable: &metal::MetalDrawableRef = msg_send![window_state.layer, nextDrawable];
849            let command_queue = window_state.command_queue.clone();
850            let command_buffer = command_queue.new_command_buffer();
851
852            let size = window_state.size();
853            let scale_factor = window_state.scale_factor();
854
855            window_state.renderer.render(
856                &scene,
857                size * scale_factor,
858                command_buffer,
859                drawable.texture(),
860            );
861
862            command_buffer.commit();
863            command_buffer.wait_until_completed();
864            drawable.present();
865        };
866    }
867}
868
869async fn synthetic_drag(
870    window_state: Weak<RefCell<WindowState>>,
871    drag_id: usize,
872    event: MouseMovedEvent,
873) {
874    loop {
875        Timer::after(Duration::from_millis(16)).await;
876        if let Some(window_state) = window_state.upgrade() {
877            let mut window_state_borrow = window_state.borrow_mut();
878            if window_state_borrow.synthetic_drag_counter == drag_id {
879                if let Some(mut callback) = window_state_borrow.event_callback.take() {
880                    drop(window_state_borrow);
881                    callback(Event::MouseMoved(event));
882                    window_state.borrow_mut().event_callback = Some(callback);
883                }
884            } else {
885                break;
886            }
887        }
888    }
889}
890
891unsafe fn ns_string(string: &str) -> id {
892    NSString::alloc(nil).init_str(string).autorelease()
893}