use std::net::IpAddr;
use std::process::Command;
use std::ptr::NonNull;
use std::sync::{Arc, Mutex};
use block2::RcBlock;
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2_app_kit::{NSWorkspace, NSWorkspaceDidWakeNotification, NSWorkspaceWillPowerOffNotification, NSWorkspaceWillSleepNotification};
use objc2_foundation::{
ns_string, NSActivityOptions, NSDistributedNotificationCenter, NSNotification, NSNotificationCenter, NSObjectProtocol, NSProcessInfo, NSString,
};
use tauri::{AppHandle, Runtime};
use crate::Shared;
pub(super) fn start<R: Runtime>(app: &AppHandle<R>, shared: &Arc<Shared>) {
let workspace = NSWorkspace::sharedWorkspace().notificationCenter();
let state = shared.clone();
let (will_sleep, did_wake, power_off) = unsafe { (NSWorkspaceWillSleepNotification, NSWorkspaceDidWakeNotification, NSWorkspaceWillPowerOffNotification) };
observe(&workspace, will_sleep, move || super::on_sleep(&state, "NSWorkspace"));
let state = shared.clone();
observe(&workspace, did_wake, move || super::on_resume(&state, None, "NSWorkspace"));
let (handle, state) = (app.clone(), shared.clone());
observe(&workspace, power_off, move || super::on_shutdown(&handle, &state, "NSWorkspace"));
let distributed = NSDistributedNotificationCenter::defaultCenter();
let (handle, state) = (app.clone(), shared.clone());
observe(&distributed, ns_string!("com.apple.screenIsLocked"), move || super::on_lock(&handle, &state, true, "分布式通知"));
let (handle, state) = (app.clone(), shared.clone());
observe(&distributed, ns_string!("com.apple.screenIsUnlocked"), move || super::on_lock(&handle, &state, false, "分布式通知"));
}
fn observe(center: &NSNotificationCenter, name: &NSString, f: impl Fn() + 'static) {
let block = RcBlock::new(move |_notification: NonNull<NSNotification>| f());
let _observer = unsafe { center.addObserverForName_object_queue_usingBlock(Some(name), None, None, &block) };
}
struct Activity(Retained<ProtocolObject<dyn NSObjectProtocol>>);
unsafe impl Send for Activity {}
static ACTIVITY: Mutex<Option<Activity>> = Mutex::new(None);
pub(super) fn app_nap(hold: bool) {
let mut slot = ACTIVITY.lock().unwrap();
let info = NSProcessInfo::processInfo();
match (hold, slot.is_some()) {
(true, false) => {
let reason = NSString::from_str("深应智能 IM:登录期间保持连接与心跳");
let token = info.beginActivityWithOptions_reason(NSActivityOptions::UserInitiatedAllowingIdleSystemSleep, &reason);
*slot = Some(Activity(token));
}
(false, true) => {
if let Some(activity) = slot.take() {
unsafe { info.endActivity(&activity.0) };
}
}
_ => {}
}
}
pub(super) fn interface_addrs() -> Vec<(String, IpAddr)> {
super::unix_interface_addrs()
}
pub(super) fn default_routes() -> Vec<String> {
Vec::new()
}
pub(super) fn os_version() -> String {
let v = NSProcessInfo::processInfo().operatingSystemVersion();
if v.patchVersion > 0 {
format!("macOS {}.{}.{}", v.majorVersion, v.minorVersion, v.patchVersion)
} else {
format!("macOS {}.{}", v.majorVersion, v.minorVersion)
}
}
pub(super) fn computer_name() -> String {
let scutil = Command::new("/usr/sbin/scutil").args(["--get", "ComputerName"]).output().ok().filter(|o| o.status.success());
if let Some(name) = scutil.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).filter(|s| !s.is_empty()) {
return name;
}
let host = NSProcessInfo::processInfo().hostName().to_string();
host.strip_suffix(".local").unwrap_or(&host).to_string()
}