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