1996 lines · 70.5 KB · Rust
RawDownload
use super::{ns_string, renderer, MacDisplay, NSRange, NSStringExt};use crate::{    platform::PlatformInputHandler, point, px, size, AnyWindowHandle, Bounds, DisplayLink,    ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers,    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,    PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel,    RequestFrameOptions, ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance,    WindowBounds, WindowKind, WindowParams,};use block::ConcreteBlock;use cocoa::{    appkit::{        NSApplication, NSBackingStoreBuffered, NSColor, NSEvent, NSEventModifierFlags,        NSFilenamesPboardType, NSPasteboard, NSScreen, NSView, NSViewHeightSizable,        NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowCollectionBehavior,        NSWindowOcclusionState, NSWindowStyleMask, NSWindowTitleVisibility,    },    base::{id, nil},    foundation::{        NSArray, NSAutoreleasePool, NSDictionary, NSFastEnumeration, NSInteger, NSNotFound,        NSPoint, NSRect, NSSize, NSString, NSUInteger,    },};use core_graphics::display::{CGDirectDisplayID, CGPoint, CGRect};use ctor::ctor;use futures::channel::oneshot;use objc::{    class,    declare::ClassDecl,    msg_send,    runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},    sel, sel_impl,};use parking_lot::Mutex;use raw_window_handle as rwh;use smallvec::SmallVec;use std::{    cell::Cell,    ffi::{c_void, CStr},    mem,    ops::Range,    path::PathBuf,    ptr::{self, NonNull},    rc::Rc,    sync::{Arc, Weak},    time::Duration,};use util::ResultExt; const WINDOW_STATE_IVAR: &str = "windowState"; static mut WINDOW_CLASS: *const Class = ptr::null();static mut PANEL_CLASS: *const Class = ptr::null();static mut VIEW_CLASS: *const Class = ptr::null(); #[allow(non_upper_case_globals)]const NSWindowStyleMaskNonactivatingPanel: NSWindowStyleMask =    NSWindowStyleMask::from_bits_retain(1 << 7);#[allow(non_upper_case_globals)]const NSNormalWindowLevel: NSInteger = 0;#[allow(non_upper_case_globals)]const NSPopUpWindowLevel: NSInteger = 101;#[allow(non_upper_case_globals)]const NSTrackingMouseEnteredAndExited: NSUInteger = 0x01;#[allow(non_upper_case_globals)]const NSTrackingMouseMoved: NSUInteger = 0x02;#[allow(non_upper_case_globals)]const NSTrackingActiveAlways: NSUInteger = 0x80;#[allow(non_upper_case_globals)]const NSTrackingInVisibleRect: NSUInteger = 0x200;#[allow(non_upper_case_globals)]const NSWindowAnimationBehaviorUtilityWindow: NSInteger = 4;#[allow(non_upper_case_globals)]const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;// https://developer.apple.com/documentation/appkit/nsdragoperationtype NSDragOperation = NSUInteger;#[allow(non_upper_case_globals)]const NSDragOperationNone: NSDragOperation = 0;#[allow(non_upper_case_globals)]const NSDragOperationCopy: NSDragOperation = 1; #[link(name = "CoreGraphics", kind = "framework")]extern "C" {    // Widely used private APIs; Apple uses them for their Terminal.app.    fn CGSMainConnectionID() -> id;    fn CGSSetWindowBackgroundBlurRadius(        connection_id: id,        window_id: NSInteger,        radius: i64,    ) -> i32;} #[ctor]unsafe fn build_classes() {    WINDOW_CLASS = build_window_class("GPUIWindow", class!(NSWindow));    PANEL_CLASS = build_window_class("GPUIPanel", class!(NSPanel));    VIEW_CLASS = {        let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();        decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);         decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));         decl.add_method(            sel!(performKeyEquivalent:),            handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,        );        decl.add_method(            sel!(keyDown:),            handle_key_down as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(rightMouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(rightMouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(otherMouseDown:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(otherMouseUp:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseMoved:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseExited:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(mouseDragged:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(scrollWheel:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(swipeWithEvent:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );        decl.add_method(            sel!(flagsChanged:),            handle_view_event as extern "C" fn(&Object, Sel, id),        );         decl.add_method(            sel!(makeBackingLayer),            make_backing_layer as extern "C" fn(&Object, Sel) -> id,        );         decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());        decl.add_method(            sel!(viewDidChangeBackingProperties),            view_did_change_backing_properties as extern "C" fn(&Object, Sel),        );        decl.add_method(            sel!(setFrameSize:),            set_frame_size as extern "C" fn(&Object, Sel, NSSize),        );        decl.add_method(            sel!(displayLayer:),            display_layer as extern "C" fn(&Object, Sel, id),        );         decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());        decl.add_method(            sel!(validAttributesForMarkedText),            valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,        );        decl.add_method(            sel!(hasMarkedText),            has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,        );        decl.add_method(            sel!(markedRange),            marked_range as extern "C" fn(&Object, Sel) -> NSRange,        );        decl.add_method(            sel!(selectedRange),            selected_range as extern "C" fn(&Object, Sel) -> NSRange,        );        decl.add_method(            sel!(firstRectForCharacterRange:actualRange:),            first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,        );        decl.add_method(            sel!(insertText:replacementRange:),            insert_text as extern "C" fn(&Object, Sel, id, NSRange),        );        decl.add_method(            sel!(setMarkedText:selectedRange:replacementRange:),            set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),        );        decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));        decl.add_method(            sel!(attributedSubstringForProposedRange:actualRange:),            attributed_substring_for_proposed_range                as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,        );        decl.add_method(            sel!(viewDidChangeEffectiveAppearance),            view_did_change_effective_appearance as extern "C" fn(&Object, Sel),        );         // Suppress beep on keystrokes with modifier keys.        decl.add_method(            sel!(doCommandBySelector:),            do_command_by_selector as extern "C" fn(&Object, Sel, Sel),        );         decl.add_method(            sel!(acceptsFirstMouse:),            accepts_first_mouse as extern "C" fn(&Object, Sel, id) -> BOOL,        );         decl.add_method(            sel!(characterIndexForPoint:),            character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> u64,        );         decl.register()    };} pub(crate) fn convert_mouse_position(position: NSPoint, window_height: Pixels) -> Point<Pixels> {    point(        px(position.x as f32),        // macOS screen coordinates are relative to bottom left        window_height - px(position.y as f32),    )} unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {    let mut decl = ClassDecl::new(name, superclass).unwrap();    decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);    decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));    decl.add_method(        sel!(canBecomeMainWindow),        yes as extern "C" fn(&Object, Sel) -> BOOL,    );    decl.add_method(        sel!(canBecomeKeyWindow),        yes as extern "C" fn(&Object, Sel) -> BOOL,    );    decl.add_method(        sel!(windowDidResize:),        window_did_resize as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowDidChangeOcclusionState:),        window_did_change_occlusion_state as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowWillEnterFullScreen:),        window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowDidMove:),        window_did_move as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowDidChangeScreen:),        window_did_change_screen as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowDidBecomeKey:),        window_did_change_key_status as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowDidResignKey:),        window_did_change_key_status as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(windowShouldClose:),        window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,    );     decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));     decl.add_method(        sel!(draggingEntered:),        dragging_entered as extern "C" fn(&Object, Sel, id) -> NSDragOperation,    );    decl.add_method(        sel!(draggingUpdated:),        dragging_updated as extern "C" fn(&Object, Sel, id) -> NSDragOperation,    );    decl.add_method(        sel!(draggingExited:),        dragging_exited as extern "C" fn(&Object, Sel, id),    );    decl.add_method(        sel!(performDragOperation:),        perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,    );    decl.add_method(        sel!(concludeDragOperation:),        conclude_drag_operation as extern "C" fn(&Object, Sel, id),    );     decl.register()} struct MacWindowState {    handle: AnyWindowHandle,    executor: ForegroundExecutor,    native_window: id,    native_view: NonNull<Object>,    display_link: Option<DisplayLink>,    renderer: renderer::Renderer,    request_frame_callback: Option<Box<dyn FnMut(RequestFrameOptions)>>,    event_callback: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,    activate_callback: Option<Box<dyn FnMut(bool)>>,    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,    moved_callback: Option<Box<dyn FnMut()>>,    should_close_callback: Option<Box<dyn FnMut() -> bool>>,    close_callback: Option<Box<dyn FnOnce()>>,    appearance_changed_callback: Option<Box<dyn FnMut()>>,    input_handler: Option<PlatformInputHandler>,    last_key_equivalent: Option<KeyDownEvent>,    synthetic_drag_counter: usize,    traffic_light_position: Option<Point<Pixels>>,    previous_modifiers_changed_event: Option<PlatformInput>,    keystroke_for_do_command: Option<Keystroke>,    do_command_handled: Option<bool>,    external_files_dragged: bool,    // Whether the next left-mouse click is also the focusing click.    first_mouse: bool,    fullscreen_restore_bounds: Bounds<Pixels>,} impl MacWindowState {    fn move_traffic_light(&self) {        if let Some(traffic_light_position) = self.traffic_light_position {            if self.is_fullscreen() {                // Moving traffic lights while fullscreen doesn't work,                // see https://github.com/zed-industries/zed/issues/4712                return;            }             let titlebar_height = self.titlebar_height();             unsafe {                let close_button: id = msg_send![                    self.native_window,                    standardWindowButton: NSWindowButton::NSWindowCloseButton                ];                let min_button: id = msg_send![                    self.native_window,                    standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton                ];                let zoom_button: id = msg_send![                    self.native_window,                    standardWindowButton: NSWindowButton::NSWindowZoomButton                ];                 let mut close_button_frame: CGRect = msg_send![close_button, frame];                let mut min_button_frame: CGRect = msg_send![min_button, frame];                let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];                let mut origin = point(                    traffic_light_position.x,                    titlebar_height                        - traffic_light_position.y                        - px(close_button_frame.size.height as f32),                );                let button_spacing =                    px((min_button_frame.origin.x - close_button_frame.origin.x) as f32);                 close_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());                let _: () = msg_send![close_button, setFrame: close_button_frame];                origin.x += button_spacing;                 min_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());                let _: () = msg_send![min_button, setFrame: min_button_frame];                origin.x += button_spacing;                 zoom_button_frame.origin = CGPoint::new(origin.x.into(), origin.y.into());                let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];                origin.x += button_spacing;            }        }    }     fn start_display_link(&mut self) {        self.stop_display_link();        unsafe {            if !self                .native_window                .occlusionState()                .contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible)            {                return;            }        }        let display_id = unsafe { display_id_for_screen(self.native_window.screen()) };        if let Some(mut display_link) =            DisplayLink::new(display_id, self.native_view.as_ptr() as *mut c_void, step).log_err()        {            display_link.start().log_err();            self.display_link = Some(display_link);        }    }     fn stop_display_link(&mut self) {        self.display_link = None;    }     fn is_maximized(&self) -> bool {        unsafe {            let bounds = self.bounds();            let screen_size = self.native_window.screen().visibleFrame().into();            bounds.size == screen_size        }    }     fn is_fullscreen(&self) -> bool {        unsafe {            let style_mask = self.native_window.styleMask();            style_mask.contains(NSWindowStyleMask::NSFullScreenWindowMask)        }    }     fn bounds(&self) -> Bounds<Pixels> {        let mut window_frame = unsafe { NSWindow::frame(self.native_window) };        let screen_frame = unsafe {            let screen = NSWindow::screen(self.native_window);            NSScreen::frame(screen)        };         // Flip the y coordinate to be top-left origin        window_frame.origin.y =            screen_frame.size.height - window_frame.origin.y - window_frame.size.height;         Bounds::new(            point(                px((window_frame.origin.x - screen_frame.origin.x) as f32),                px((window_frame.origin.y + screen_frame.origin.y) as f32),            ),            size(                px(window_frame.size.width as f32),                px(window_frame.size.height as f32),            ),        )    }     fn content_size(&self) -> Size<Pixels> {        let NSSize { width, height, .. } =            unsafe { NSView::frame(self.native_window.contentView()) }.size;        size(px(width as f32), px(height as f32))    }     fn scale_factor(&self) -> f32 {        get_scale_factor(self.native_window)    }     fn titlebar_height(&self) -> Pixels {        unsafe {            let frame = NSWindow::frame(self.native_window);            let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];            px((frame.size.height - content_layout_rect.size.height) as f32)        }    }     fn window_bounds(&self) -> WindowBounds {        if self.is_fullscreen() {            WindowBounds::Fullscreen(self.fullscreen_restore_bounds)        } else {            WindowBounds::Windowed(self.bounds())        }    }} unsafe impl Send for MacWindowState {} pub(crate) struct MacWindow(Arc<Mutex<MacWindowState>>); impl MacWindow {    pub fn open(        handle: AnyWindowHandle,        WindowParams {            bounds,            titlebar,            kind,            is_movable,            focus,            show,

Lines 1–500 of 1996.