2021-03-29 15:59:14 +08:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
use cocoa::{
|
2022-04-28 21:32:44 +08:00
|
|
|
appkit::{NSApp, NSApplication, NSApplicationActivationPolicy::*, NSMenu, NSMenuItem},
|
2021-03-29 15:59:14 +08:00
|
|
|
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};
|
2022-08-10 11:33:50 +08:00
|
|
|
use dark_light;
|
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-29 16:21:18 +08:00
|
|
|
static mut LAUCHED: bool = false;
|
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 {
|
|
|
|
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>>) {
|
|
|
|
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,
|
|
|
|
);
|
|
|
|
|
2022-04-28 21:32:44 +08:00
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationDidBecomeActive:),
|
|
|
|
application_did_become_active as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationDidUnhide:),
|
|
|
|
application_did_become_unhide as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationShouldHandleReopen:),
|
|
|
|
application_should_handle_reopen as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
|
|
|
);
|
|
|
|
|
|
|
|
decl.add_method(
|
|
|
|
sel!(applicationWillTerminate:),
|
|
|
|
application_will_terminate as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
|
|
|
);
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
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) {
|
2022-04-29 16:21:18 +08:00
|
|
|
unsafe {
|
|
|
|
LAUCHED = true;
|
|
|
|
}
|
2021-03-29 15:59:14 +08:00
|
|
|
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 {
|
2022-04-29 16:21:18 +08:00
|
|
|
if !LAUCHED {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
hbb_common::log::debug!("icon clicked on finder");
|
|
|
|
if std::env::args().nth(1) == Some("--server".to_owned()) {
|
|
|
|
check_main_window();
|
|
|
|
}
|
2022-04-27 19:21:38 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-04-29 10:40:00 +08:00
|
|
|
extern "C" fn application_should_handle_reopen(_this: &mut Object, _: Sel, _sender: id) -> BOOL {
|
2022-04-28 21:32:44 +08:00
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
2022-04-29 10:40:00 +08:00
|
|
|
extern "C" fn application_did_become_active(_this: &mut Object, _: Sel, _sender: id) -> BOOL {
|
2022-04-28 21:32:44 +08:00
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
2022-04-29 10:40:00 +08:00
|
|
|
extern "C" fn application_did_become_unhide(_this: &mut Object, _: Sel, _sender: id) -> BOOL {
|
2022-04-28 21:32:44 +08:00
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn application_will_terminate(_this: &mut Object, _: Sel, _sender: id) -> BOOL {
|
|
|
|
YES
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:59:14 +08:00
|
|
|
/// 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.
|
2022-07-26 23:00:55 +08:00
|
|
|
let about_item = make_menu_item("About", "", SHOW_ABOUT_TAG);
|
2022-04-24 17:15:20 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2022-04-28 21:32:44 +08:00
|
|
|
|
2022-04-29 16:21:18 +08:00
|
|
|
pub fn show_dock() {
|
2022-04-28 21:32:44 +08:00
|
|
|
unsafe {
|
2022-04-29 16:21:18 +08:00
|
|
|
NSApp().setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
2022-04-28 21:32:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_tray() {
|
2022-04-29 16:21:18 +08:00
|
|
|
unsafe {
|
|
|
|
set_delegate(None);
|
|
|
|
}
|
2022-04-28 21:32:44 +08:00
|
|
|
use tray_item::TrayItem;
|
2022-08-10 11:33:50 +08:00
|
|
|
let mode = dark_light::detect();
|
|
|
|
let mut icon_path = "";
|
|
|
|
match mode {
|
|
|
|
dark_light::Mode::Dark => {
|
|
|
|
icon_path = "mac-tray-light.png";
|
|
|
|
},
|
|
|
|
dark_light::Mode::Light => {
|
|
|
|
icon_path = "mac-tray-dark.png";
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if let Ok(mut tray) = TrayItem::new(&crate::get_app_name(), icon_path) {
|
2022-04-28 21:32:44 +08:00
|
|
|
tray.add_label(&format!(
|
|
|
|
"{} {}",
|
|
|
|
crate::get_app_name(),
|
|
|
|
crate::lang::translate("Service is running".to_owned())
|
|
|
|
))
|
|
|
|
.ok();
|
|
|
|
|
|
|
|
let inner = tray.inner_mut();
|
|
|
|
inner.add_quit_item(&crate::lang::translate("Quit".to_owned()));
|
|
|
|
inner.display();
|
|
|
|
} else {
|
|
|
|
loop {
|
|
|
|
std::thread::sleep(std::time::Duration::from_secs(3));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-29 16:21:18 +08:00
|
|
|
|
|
|
|
pub fn check_main_window() {
|
|
|
|
use sysinfo::{ProcessExt, System, SystemExt};
|
|
|
|
let mut sys = System::new();
|
|
|
|
sys.refresh_processes();
|
|
|
|
let app = format!("/Applications/{}.app", crate::get_app_name());
|
|
|
|
let my_uid = sys
|
|
|
|
.process((std::process::id() as i32).into())
|
2022-07-18 13:33:12 +08:00
|
|
|
.map(|x| x.user_id())
|
2022-04-29 16:21:18 +08:00
|
|
|
.unwrap_or_default();
|
|
|
|
for (_, p) in sys.processes().iter() {
|
2022-07-18 13:33:12 +08:00
|
|
|
if p.cmd().len() == 1 && p.user_id() == my_uid && p.cmd()[0].contains(&app) {
|
2022-04-29 16:21:18 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::process::Command::new("open")
|
|
|
|
.args(["-n", &app])
|
|
|
|
.status()
|
|
|
|
.ok();
|
|
|
|
}
|