2133 lines · 76.9 KB · Rust
RawDownload
use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};use crate::{    AnyWindowHandle, Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor,    KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,    MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,    PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, ScaledPixels, Size,    Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowKind, WindowParams,    platform::PlatformInputHandler, point, px, size,};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,        NSOperatingSystemVersion, NSPoint, NSProcessInfo, NSRect, NSSize, NSString, NSUInteger,        NSUserDefaults,    },};use core_graphics::display::{CGDirectDisplayID, CGPoint, CGRect};use ctor::ctor;use futures::channel::oneshot;use objc::{    class,    declare::ClassDecl,    msg_send,    runtime::{BOOL, Class, NO, Object, Protocol, Sel, YES},    sel, sel_impl,};use parking_lot::Mutex;use raw_window_handle as rwh;use smallvec::SmallVec;use std::{    cell::Cell,    ffi::{CStr, c_void},    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")]unsafe 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() {    unsafe {        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);            unsafe {                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!(keyUp:),                    handle_key_up 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 {    unsafe {        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!(windowWillExitFullScreen:),            window_will_exit_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>>,    transparent_titlebar: bool,    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())        }    }

Lines 1–500 of 2133.