rustdesk/src/ui/macos.rs

185 lines
6.0 KiB
Rust
Raw Normal View History

2021-03-29 15:59:14 +08:00
#[cfg(target_os = "macos")]
use cocoa::{
appkit::{NSApp, NSApplication, NSMenu, NSMenuItem},
base::{id, nil, YES},
foundation::{NSAutoreleasePool, NSString},
};
use objc::{
class,
declare::ClassDecl,
msg_send,
runtime::{Object, Sel, BOOL},
sel, sel_impl,
};
2022-04-22 23:41:12 +08:00
use sciter::{make_args, Host};
2022-04-27 19:21:38 +08:00
use std::{ffi::c_void, rc::Rc};
2021-03-29 15:59:14 +08:00
static APP_HANDLER_IVAR: &str = "GoDeskAppHandler";
2022-04-22 23:41:12 +08:00
const TERMINATE_TAG: u32 = 0;
const SHOW_ABOUT_TAG: u32 = 1;
const SHOW_SETTINGS_TAG: u32 = 2;
const RUN_ME_TAG: u32 = 3;
2022-04-27 19:21:38 +08:00
const AWAKE: u32 = 4;
2021-03-29 15:59:14 +08:00
trait AppHandler {
fn command(&mut self, cmd: u32);
}
struct DelegateState {
handler: Option<Box<dyn AppHandler>>,
}
impl DelegateState {
fn command(&mut self, command: u32) {
2022-04-22 23:41:12 +08:00
if command == TERMINATE_TAG {
2021-03-29 15:59:14 +08:00
unsafe {
let () = msg_send!(NSApp(), terminate: nil);
}
} else if let Some(inner) = self.handler.as_mut() {
inner.command(command)
}
}
}
2022-04-28 03:25:39 +08:00
lazy_static::lazy_static! {
static ref START_TM: std::sync::Mutex<std::time::Instant> = std::sync::Mutex::new(std::time::Instant::now());
2022-04-27 19:21:38 +08:00
}
2022-04-22 23:41:12 +08:00
impl AppHandler for Rc<Host> {
fn command(&mut self, cmd: u32) {
if cmd == SHOW_ABOUT_TAG {
2022-04-27 19:21:38 +08:00
let _ = self.call_function("awake", &make_args![]);
2022-04-22 23:41:12 +08:00
let _ = self.call_function("showAbout", &make_args![]);
} else if cmd == SHOW_SETTINGS_TAG {
2022-04-27 19:21:38 +08:00
let _ = self.call_function("awake", &make_args![]);
2022-04-22 23:41:12 +08:00
let _ = self.call_function("showSettings", &make_args![]);
2022-04-27 19:21:38 +08:00
} else if cmd == AWAKE {
2022-04-28 03:25:39 +08:00
if START_TM.lock().unwrap().elapsed().as_millis() < 1000 {
hbb_common::log::debug!("First click on docker icon {:?}", START_TM.lock().unwrap().elapsed());
return;
2022-04-27 19:21:38 +08:00
}
let _ = self.call_function("awake", &make_args![]);
2022-04-22 23:41:12 +08:00
}
}
}
2021-03-29 15:59:14 +08:00
// https://github.com/xi-editor/druid/blob/master/druid-shell/src/platform/mac/application.rs
unsafe fn set_delegate(handler: Option<Box<dyn AppHandler>>) {
2022-04-28 03:25:39 +08:00
*START_TM.lock().unwrap() = std::time::Instant::now();
2021-03-29 15:59:14 +08:00
let mut decl =
ClassDecl::new("AppDelegate", class!(NSObject)).expect("App Delegate definition failed");
decl.add_ivar::<*mut c_void>(APP_HANDLER_IVAR);
decl.add_method(
sel!(applicationDidFinishLaunching:),
application_did_finish_launching as extern "C" fn(&mut Object, Sel, id),
);
decl.add_method(
sel!(applicationShouldOpenUntitledFile:),
application_should_handle_open_untitled_file as extern "C" fn(&mut Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(handleMenuItem:),
handle_menu_item as extern "C" fn(&mut Object, Sel, id),
);
let decl = decl.register();
let delegate: id = msg_send![decl, alloc];
let () = msg_send![delegate, init];
let state = DelegateState { handler };
let handler_ptr = Box::into_raw(Box::new(state));
(*delegate).set_ivar(APP_HANDLER_IVAR, handler_ptr as *mut c_void);
let () = msg_send![NSApp(), setDelegate: delegate];
}
extern "C" fn application_did_finish_launching(_this: &mut Object, _: Sel, _notification: id) {
unsafe {
let () = msg_send![NSApp(), activateIgnoringOtherApps: YES];
}
}
extern "C" fn application_should_handle_open_untitled_file(
2022-04-27 19:21:38 +08:00
this: &mut Object,
2021-03-29 15:59:14 +08:00
_: Sel,
_sender: id,
) -> BOOL {
2022-04-27 19:21:38 +08:00
unsafe {
let inner: *mut c_void = *this.get_ivar(APP_HANDLER_IVAR);
let inner = &mut *(inner as *mut DelegateState);
(*inner).command(AWAKE);
2021-03-29 15:59:14 +08:00
}
YES
}
/// This handles menu items in the case that all windows are closed.
extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
unsafe {
let tag: isize = msg_send![item, tag];
2022-04-22 23:41:12 +08:00
let tag = tag as u32;
if tag == RUN_ME_TAG {
crate::run_me(Vec::<String>::new()).ok();
} else {
2021-03-29 15:59:14 +08:00
let inner: *mut c_void = *this.get_ivar(APP_HANDLER_IVAR);
let inner = &mut *(inner as *mut DelegateState);
(*inner).command(tag as u32);
}
}
}
2022-04-22 23:41:12 +08:00
unsafe fn make_menu_item(title: &str, key: &str, tag: u32) -> *mut Object {
let title = NSString::alloc(nil).init_str(title);
let action = sel!(handleMenuItem:);
let key = NSString::alloc(nil).init_str(key);
let object = NSMenuItem::alloc(nil)
.initWithTitle_action_keyEquivalent_(title, action, key)
.autorelease();
let () = msg_send![object, setTag: tag];
object
}
2022-04-27 19:21:38 +08:00
pub fn make_menubar(host: Rc<Host>, is_index: bool) {
2021-03-29 15:59:14 +08:00
unsafe {
let _pool = NSAutoreleasePool::new(nil);
2022-04-22 23:41:12 +08:00
set_delegate(Some(Box::new(host)));
2021-03-29 15:59:14 +08:00
let menubar = NSMenu::new(nil).autorelease();
let app_menu_item = NSMenuItem::new(nil).autorelease();
menubar.addItem_(app_menu_item);
let app_menu = NSMenu::new(nil).autorelease();
2022-04-22 23:41:12 +08:00
2022-04-27 19:21:38 +08:00
if !is_index {
2022-04-22 23:41:12 +08:00
let new_item = make_menu_item("New Window", "n", RUN_ME_TAG);
app_menu.addItem_(new_item);
2022-04-24 17:15:20 +08:00
} else {
// When app launched without argument, is the main panel.
let about_item = make_menu_item("About", "a", SHOW_ABOUT_TAG);
app_menu.addItem_(about_item);
let separator = NSMenuItem::separatorItem(nil).autorelease();
app_menu.addItem_(separator);
let settings_item = make_menu_item("Settings", "s", SHOW_SETTINGS_TAG);
app_menu.addItem_(settings_item);
2022-04-22 23:41:12 +08:00
}
let separator = NSMenuItem::separatorItem(nil).autorelease();
app_menu.addItem_(separator);
let quit_item = make_menu_item(
2022-04-27 19:21:38 +08:00
&format!("Quit {}", crate::get_app_name()),
2022-04-22 23:41:12 +08:00
"q",
TERMINATE_TAG,
);
app_menu_item.setSubmenu_(app_menu);
2021-03-29 15:59:14 +08:00
/*
if !enabled {
let () = msg_send![quit_item, setEnabled: NO];
}
if selected {
let () = msg_send![quit_item, setState: 1_isize];
}
let () = msg_send![item, setTag: id as isize];
*/
app_menu.addItem_(quit_item);
NSApp().setMainMenu_(menubar);
}
}