use std::ffi::CString;
use libc::pid_t;
use objc::runtime::{Class, Object};
use super::NSObject;
lazy_static! {
static ref NS_RUNNING_APPLICATION: &'static Class = {
Class::get("NSRunningApplication").unwrap()
};
static ref NS_STRING: &'static Class = {
Class::get("NSString").unwrap()
};
static ref NS_WORKSPACE_SHARED: &'static Object = {
let cls = Class::get("NSWorkspace").unwrap();
unsafe { msg_send![cls, sharedWorkspace] }
};
static ref CURRENT_APPLICATION: App = {
let cls: &Class = &NS_RUNNING_APPLICATION;
unsafe { msg_send![cls, currentApplication] }
};
}
fn str_to_ns_string(s: String) -> NSObject {
let ns_string: &Class = &NS_STRING;
let s = CString::new(s).unwrap();
let utf8 = s.as_ptr();
unsafe { msg_send![ns_string, stringWithUTF8String:utf8] }
}
pub fn open_file<'a, 'b, S>(path: &'a str, app_name: S)
where S: Into<Option<&'b str>>
{
let file = str_to_ns_string(path.into());
let app = app_name.into().map(|s| str_to_ns_string(s.into()));
let workspace: &Object = &NS_WORKSPACE_SHARED;
unsafe { msg_send![workspace, openFile:file withApplication:app] }
}
pub fn launch(app: &str) -> bool {
let app = str_to_ns_string(app.into());
let workspace: &Object = &NS_WORKSPACE_SHARED;
unsafe { msg_send![workspace, launchApplication:app] }
}
pub fn auto_terminate() {
let cls: &Class = &NS_RUNNING_APPLICATION;
unsafe { msg_send![cls, terminateAutomaticallyTerminableApplications] }
}
pub type Pid = pid_t;
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct App(NSObject);
impl App {
pub fn current() -> &'static App {
&CURRENT_APPLICATION
}
pub fn from_pid(pid: Pid) -> Option<App> {
let cls: &Class = &NS_RUNNING_APPLICATION;
unsafe { msg_send![cls, runningApplicationWithProcessIdentifier:pid] }
}
pub fn arch(&self) -> Arch {
unsafe { msg_send![self.0.inner(), executableArchitecture] }
}
pub fn pid(&self) -> Option<Pid> {
match unsafe { msg_send![self.0.inner(), processIdentifier] } {
-1 => None,
id => Some(id),
}
}
pub fn is_hidden(&self) -> bool {
unsafe { msg_send![self.0.inner(), isHidden] }
}
pub fn set_hidden(&self, hide: bool) -> bool {
let app = self.0.inner();
if hide {
unsafe { msg_send![app, hide] }
} else {
unsafe { msg_send![app, unhide] }
}
}
pub fn is_active(&self) -> bool {
unsafe { msg_send![self.0.inner(), isActive] }
}
pub fn activate(&self, options: ActivationOptions) -> bool {
unsafe { msg_send![self.0.inner(), activateWithOptions:options] }
}
pub fn bundle_identifier(&self) -> Option<String> {
unsafe {
let s = msg_send![self.0.inner(), bundleIdentifier];
super::ns_string_encode_utf8(s)
}
}
pub fn bundle_url(&self) -> Option<String> {
unsafe {
let url = msg_send![self.0.inner(), bundleURL];
super::ns_url_encode_utf8(url)
}
}
pub fn executable_url(&self) -> Option<String> {
unsafe {
let url = msg_send![self.0.inner(), executableURL];
super::ns_url_encode_utf8(url)
}
}
pub fn localized_name(&self) -> Option<String> {
unsafe {
let s = msg_send![self.0.inner(), localizedName];
super::ns_string_encode_utf8(s)
}
}
pub fn is_terminated(&self) -> bool {
unsafe { msg_send![self.0.inner(), isTerminated] }
}
pub fn terminate(&self, force: bool) -> bool {
let app = self.0.inner();
if force {
unsafe { msg_send![app, forceTerminate] }
} else {
unsafe { msg_send![app, terminate] }
}
}
pub fn owns_menu_bar(&self) -> bool {
unsafe { msg_send![self.0.inner(), ownsMenuBar] }
}
}
bitflags! {
#[repr(C)]
#[derive(Default)]
pub struct ActivationOptions: usize {
const ALL_WINDOWS = 1 << 0;
const IGNORING_OTHER_APPS = 1 << 1;
}
}
#[repr(usize)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Arch {
I386 = 0x00000007,
Ppc = 0x00000012,
X86_64 = 0x01000007,
Ppc64 = 0x01000012,
}
impl Arch {
#[inline]
pub fn is_32bit(self) -> bool {
!self.is_64bit()
}
#[inline]
pub fn is_64bit(self) -> bool {
self as usize & 0x01000000 != 0
}
}