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