diff --git a/crates/gpui/src/platform/mac/status_item.rs b/crates/gpui/src/platform/mac/status_item.rs index 6a4d754966..81a8ad036f 100644 --- a/crates/gpui/src/platform/mac/status_item.rs +++ b/crates/gpui/src/platform/mac/status_item.rs @@ -5,15 +5,53 @@ use crate::{ }; use cocoa::{ appkit::{ - NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSViewHeightSizable, - NSViewWidthSizable, NSWindow, + NSApplication, NSButton, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, + NSViewHeightSizable, NSViewWidthSizable, NSWindow, }, base::{id, nil, YES}, - foundation::NSSize, + foundation::{NSSize, NSUInteger}, }; +use ctor::ctor; use foreign_types::ForeignTypeRef; -use objc::{msg_send, rc::StrongPtr, sel, sel_impl}; -use std::{cell::RefCell, rc::Rc, sync::Arc}; +use objc::{ + class, + declare::ClassDecl, + msg_send, + rc::StrongPtr, + runtime::{Class, Object, Sel}, + sel, sel_impl, +}; +use std::{ + cell::RefCell, + ffi::c_void, + ptr, + rc::{Rc, Weak}, + sync::Arc, +}; + +static mut HANDLER_CLASS: *const Class = ptr::null(); +const STATE_IVAR: &str = "state"; + +#[allow(non_upper_case_globals)] +const NSEventMaskAny: NSUInteger = NSUInteger::MAX; + +#[ctor] +unsafe fn build_classes() { + HANDLER_CLASS = { + let mut decl = ClassDecl::new("GPUIStatusItemEventHandler", class!(NSObject)).unwrap(); + decl.add_ivar::<*mut c_void>(STATE_IVAR); + decl.add_method( + sel!(dealloc), + dealloc_handler as extern "C" fn(&Object, Sel), + ); + decl.add_method( + sel!(handleEvent), + handle_event as extern "C" fn(&Object, Sel), + ); + + decl.register() + }; +} pub struct StatusItem(Rc>); @@ -21,6 +59,7 @@ struct StatusItemState { native_item: StrongPtr, renderer: Renderer, event_callback: Option bool>>, + _event_handler: StrongPtr, } impl StatusItem { @@ -36,11 +75,22 @@ impl StatusItem { button.setWantsBestResolutionOpenGLSurface_(YES); button.setLayer(renderer.layer().as_ptr() as id); - Self(Rc::new(RefCell::new(StatusItemState { - native_item, - renderer, - event_callback: None, - }))) + Self(Rc::new_cyclic(|state| { + let event_handler = StrongPtr::new(msg_send![HANDLER_CLASS, alloc]); + let _: () = msg_send![*event_handler, init]; + (**event_handler) + .set_ivar(STATE_IVAR, Weak::into_raw(state.clone()) as *const c_void); + button.setTarget_(*event_handler); + button.setAction_(sel!(handleEvent)); + let _: () = msg_send![button, sendActionOn: NSEventMaskAny]; + + RefCell::new(StatusItemState { + native_item, + renderer, + event_callback: None, + _event_handler: event_handler, + }) + })) } } } @@ -54,17 +104,29 @@ impl platform::Window for StatusItem { self.0.borrow_mut().event_callback = Some(callback); } - fn on_active_status_change(&mut self, _: Box) {} + fn on_active_status_change(&mut self, _: Box) { + unimplemented!() + } - fn on_resize(&mut self, _: Box) {} + fn on_resize(&mut self, _: Box) { + unimplemented!() + } - fn on_fullscreen(&mut self, _: Box) {} + fn on_fullscreen(&mut self, _: Box) { + unimplemented!() + } - fn on_should_close(&mut self, _: Box bool>) {} + fn on_should_close(&mut self, _: Box bool>) { + unimplemented!() + } - fn on_close(&mut self, _: Box) {} + fn on_close(&mut self, _: Box) { + unimplemented!() + } - fn set_input_handler(&mut self, _: Box) {} + fn set_input_handler(&mut self, _: Box) { + unimplemented!() + } fn prompt( &self, @@ -72,22 +134,36 @@ impl platform::Window for StatusItem { _: &str, _: &[&str], ) -> postage::oneshot::Receiver { - panic!() + unimplemented!() } - fn activate(&self) {} + fn activate(&self) { + unimplemented!() + } - fn set_title(&mut self, _: &str) {} + fn set_title(&mut self, _: &str) { + unimplemented!() + } - fn set_edited(&mut self, _: bool) {} + fn set_edited(&mut self, _: bool) { + unimplemented!() + } - fn show_character_palette(&self) {} + fn show_character_palette(&self) { + unimplemented!() + } - fn minimize(&self) {} + fn minimize(&self) { + unimplemented!() + } - fn zoom(&self) {} + fn zoom(&self) { + unimplemented!() + } - fn toggle_full_screen(&self) {} + fn toggle_full_screen(&self) { + unimplemented!() + } fn size(&self) -> Vector2F { self.0.borrow().size() @@ -119,3 +195,40 @@ impl StatusItemState { } } } + +extern "C" fn dealloc_handler(this: &Object, _: Sel) { + unsafe { + drop_state(this); + let _: () = msg_send![super(this, class!(NSObject)), dealloc]; + } +} + +extern "C" fn handle_event(this: &Object, _: Sel) { + unsafe { + if let Some(state) = get_state(this).upgrade() { + let mut state_borrow = state.as_ref().borrow_mut(); + let app = NSApplication::sharedApplication(nil); + let native_event: id = msg_send![app, currentEvent]; + if let Some(event) = Event::from_native(native_event, Some(state_borrow.size().y())) { + if let Some(mut callback) = state_borrow.event_callback.take() { + drop(state_borrow); + callback(event); + state.borrow_mut().event_callback = Some(callback); + } + } + } + } +} + +unsafe fn get_state(object: &Object) -> Weak> { + let raw: *mut c_void = *object.get_ivar(STATE_IVAR); + let weak1 = Weak::from_raw(raw as *mut RefCell); + let weak2 = weak1.clone(); + let _ = Weak::into_raw(weak1); + weak2 +} + +unsafe fn drop_state(object: &Object) { + let raw: *const c_void = *object.get_ivar(STATE_IVAR); + Weak::from_raw(raw as *const RefCell); +}