window.rs

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