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, 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, },};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 2075. Load more
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, 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, }, }; 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/nsdragoperation type 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()) } } }