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