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