#![allow(dead_code)]
use crate::{
event::{KeyCode, KeyMods},
native::apple::frameworks::*,
CursorIcon,
};
pub fn nsstring_to_string(string: ObjcId) -> String {
unsafe {
let utf8_string: *const core::ffi::c_uchar = msg_send![string, UTF8String];
let utf8_len: usize = msg_send![string, lengthOfBytesUsingEncoding: UTF8_ENCODING];
let slice = std::slice::from_raw_parts(utf8_string, utf8_len);
std::str::from_utf8_unchecked(slice).to_owned()
}
}
pub fn str_to_nsstring(str: &str) -> ObjcId {
unsafe {
let ns_string: ObjcId = msg_send![class!(NSString), alloc];
let ns_string: ObjcId = msg_send![
ns_string,
initWithBytes: str.as_ptr()
length: str.len()
encoding: UTF8_ENCODING as ObjcId
];
let _: () = msg_send![ns_string, autorelease];
ns_string
}
}
pub fn load_native_cursor(cursor_name: &str) -> ObjcId {
let sel = Sel::register(cursor_name);
let id: ObjcId = unsafe { msg_send![class!(NSCursor), performSelector: sel] };
id
}
pub fn load_undocumented_cursor(cursor_name: &str) -> ObjcId {
unsafe {
let class = class!(NSCursor);
let sel = Sel::register(cursor_name);
let sel: ObjcId = msg_send![class, respondsToSelector: sel];
let id: ObjcId = msg_send![class, performSelector: sel];
id
}
}
pub unsafe fn ccfstr_from_str(inp: &str) -> CFStringRef {
let null = format!("{}\0", inp);
__CFStringMakeConstantString(null.as_ptr() as *const ::core::ffi::c_char)
}
pub unsafe fn cfstring_ref_to_string(cfstring: CFStringRef) -> String {
let length = CFStringGetLength(cfstring);
let range = CFRange {
location: 0,
length,
};
let mut num_bytes = 0u64;
let converted = CFStringGetBytes(
cfstring,
range,
kCFStringEncodingUTF8,
0,
false,
std::ptr::null_mut::<u8>(),
0,
&mut num_bytes,
);
if converted == 0 || num_bytes == 0 {
return String::new();
}
let mut buffer = vec![0u8; num_bytes as usize];
CFStringGetBytes(
cfstring,
range,
kCFStringEncodingUTF8,
0,
false,
buffer.as_mut_ptr(),
num_bytes,
std::ptr::null_mut::<u64>(),
);
String::from_utf8(buffer).unwrap_or_default()
}
pub fn load_webkit_cursor(cursor_name_str: &str) -> ObjcId {
unsafe {
static CURSOR_ROOT: &str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors";
let cursor_root = str_to_nsstring(CURSOR_ROOT);
let cursor_name = str_to_nsstring(cursor_name_str);
let cursor_pdf = str_to_nsstring("cursor.pdf");
let cursor_plist = str_to_nsstring("info.plist");
let key_x = str_to_nsstring("hotx");
let key_y = str_to_nsstring("hoty");
let cursor_path: ObjcId =
msg_send![cursor_root, stringByAppendingPathComponent: cursor_name];
let pdf_path: ObjcId = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf];
let info_path: ObjcId =
msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist];
let ns_image: ObjcId = msg_send![class!(NSImage), alloc];
let () = msg_send![ns_image, initByReferencingFile: pdf_path];
let info: ObjcId = msg_send![
class!(NSDictionary),
dictionaryWithContentsOfFile: info_path
];
let x: ObjcId = msg_send![info, valueForKey: key_x]; let y: ObjcId = msg_send![info, valueForKey: key_y]; let point = NSPoint {
x: msg_send![x, doubleValue],
y: msg_send![y, doubleValue],
};
let cursor: ObjcId = msg_send![class!(NSCursor), alloc];
msg_send![cursor, initWithImage: ns_image hotSpot: point]
}
}
pub fn get_event_char(event: ObjcId) -> Option<char> {
unsafe {
let characters: ObjcId = msg_send![event, characters];
if characters == nil {
return None;
}
let chars = nsstring_to_string(characters);
if chars.is_empty() {
return None;
}
Some(chars.chars().next().unwrap())
}
}
pub fn get_event_key_modifier(event: ObjcId) -> KeyMods {
let flags: u64 = unsafe { msg_send![event, modifierFlags] };
KeyMods {
shift: flags & NSEventModifierFlags::NSShiftKeyMask as u64 != 0,
ctrl: flags & NSEventModifierFlags::NSControlKeyMask as u64 != 0,
alt: flags & NSEventModifierFlags::NSAlternateKeyMask as u64 != 0,
logo: flags & NSEventModifierFlags::NSCommandKeyMask as u64 != 0,
}
}
pub fn get_event_keycode(event: ObjcId) -> Option<KeyCode> {
let scan_code: core::ffi::c_ushort = unsafe { msg_send![event, keyCode] };
Some(match scan_code {
0x00 => KeyCode::A,
0x01 => KeyCode::S,
0x02 => KeyCode::D,
0x03 => KeyCode::F,
0x04 => KeyCode::H,
0x05 => KeyCode::G,
0x06 => KeyCode::Z,
0x07 => KeyCode::X,
0x08 => KeyCode::C,
0x09 => KeyCode::V,
0x0b => KeyCode::B,
0x0c => KeyCode::Q,
0x0d => KeyCode::W,
0x0e => KeyCode::E,
0x0f => KeyCode::R,
0x10 => KeyCode::Y,
0x11 => KeyCode::T,
0x12 => KeyCode::Key1,
0x13 => KeyCode::Key2,
0x14 => KeyCode::Key3,
0x15 => KeyCode::Key4,
0x16 => KeyCode::Key6,
0x17 => KeyCode::Key5,
0x18 => KeyCode::Equal,
0x19 => KeyCode::Key9,
0x1a => KeyCode::Key7,
0x1b => KeyCode::Minus,
0x1c => KeyCode::Key8,
0x1d => KeyCode::Key0,
0x1e => KeyCode::RightBracket,
0x1f => KeyCode::O,
0x20 => KeyCode::U,
0x21 => KeyCode::LeftBracket,
0x22 => KeyCode::I,
0x23 => KeyCode::P,
0x24 => KeyCode::Enter,
0x25 => KeyCode::L,
0x26 => KeyCode::J,
0x27 => KeyCode::Apostrophe,
0x28 => KeyCode::K,
0x29 => KeyCode::Semicolon,
0x2a => KeyCode::Backslash,
0x2b => KeyCode::Comma,
0x2c => KeyCode::Slash,
0x2d => KeyCode::N,
0x2e => KeyCode::M,
0x2f => KeyCode::Period,
0x30 => KeyCode::Tab,
0x31 => KeyCode::Space,
0x32 => KeyCode::GraveAccent,
0x33 => KeyCode::Backspace,
0x35 => KeyCode::Escape,
0x39 => KeyCode::CapsLock,
0x41 => KeyCode::KpDecimal,
0x43 => KeyCode::KpMultiply,
0x45 => KeyCode::KpAdd,
0x47 => KeyCode::NumLock,
0x4b => KeyCode::KpDivide,
0x4c => KeyCode::KpEnter,
0x4e => KeyCode::KpSubtract,
0x51 => KeyCode::KpEqual,
0x52 => KeyCode::Kp0,
0x53 => KeyCode::Kp1,
0x54 => KeyCode::Kp2,
0x55 => KeyCode::Kp3,
0x56 => KeyCode::Kp4,
0x57 => KeyCode::Kp5,
0x58 => KeyCode::Kp6,
0x59 => KeyCode::Kp7,
0x5b => KeyCode::Kp8,
0x5c => KeyCode::Kp9,
0x60 => KeyCode::F5,
0x61 => KeyCode::F6,
0x62 => KeyCode::F7,
0x63 => KeyCode::F3,
0x64 => KeyCode::F8,
0x65 => KeyCode::F9,
0x67 => KeyCode::F11,
0x69 => KeyCode::PrintScreen,
0x6d => KeyCode::F10,
0x6f => KeyCode::F12,
0x72 => KeyCode::Insert,
0x73 => KeyCode::Home,
0x74 => KeyCode::PageUp,
0x75 => KeyCode::Delete,
0x76 => KeyCode::F4,
0x77 => KeyCode::End,
0x78 => KeyCode::F2,
0x79 => KeyCode::PageDown,
0x7a => KeyCode::F1,
0x7b => KeyCode::Left,
0x7c => KeyCode::Right,
0x7d => KeyCode::Down,
0x7e => KeyCode::Up,
_ => return None,
})
}
pub fn keycode_to_menu_key(keycode: KeyCode, shift: bool) -> &'static str {
if !shift {
match keycode {
KeyCode::GraveAccent => "`",
KeyCode::Apostrophe => "'",
KeyCode::Key0 => "0",
KeyCode::Key1 => "1",
KeyCode::Key2 => "2",
KeyCode::Key3 => "3",
KeyCode::Key4 => "4",
KeyCode::Key5 => "5",
KeyCode::Key6 => "6",
KeyCode::Key7 => "7",
KeyCode::Key8 => "8",
KeyCode::Key9 => "9",
KeyCode::Minus => "-",
KeyCode::Equal => "=",
KeyCode::Q => "q",
KeyCode::W => "w",
KeyCode::E => "e",
KeyCode::R => "r",
KeyCode::T => "t",
KeyCode::Y => "y",
KeyCode::U => "u",
KeyCode::I => "i",
KeyCode::O => "o",
KeyCode::P => "p",
KeyCode::LeftBracket => "[",
KeyCode::RightBracket => "]",
KeyCode::A => "a",
KeyCode::S => "s",
KeyCode::D => "d",
KeyCode::F => "f",
KeyCode::G => "g",
KeyCode::H => "h",
KeyCode::J => "j",
KeyCode::K => "l",
KeyCode::L => "l",
KeyCode::Semicolon => ";",
KeyCode::Backslash => "\\",
KeyCode::Z => "z",
KeyCode::X => "x",
KeyCode::C => "c",
KeyCode::V => "v",
KeyCode::B => "b",
KeyCode::N => "n",
KeyCode::M => "m",
KeyCode::Comma => ",",
KeyCode::Period => ".",
KeyCode::Slash => "/",
_ => "",
}
} else {
match keycode {
KeyCode::GraveAccent => "~",
KeyCode::Apostrophe => "\"",
KeyCode::Key0 => ")",
KeyCode::Key1 => "!",
KeyCode::Key2 => "@",
KeyCode::Key3 => "#",
KeyCode::Key4 => "$",
KeyCode::Key5 => "%",
KeyCode::Key6 => "^",
KeyCode::Key7 => "&",
KeyCode::Key8 => "*",
KeyCode::Key9 => "(",
KeyCode::Minus => "_",
KeyCode::Equal => "+",
KeyCode::Q => "Q",
KeyCode::W => "W",
KeyCode::E => "E",
KeyCode::R => "R",
KeyCode::T => "T",
KeyCode::Y => "Y",
KeyCode::U => "U",
KeyCode::I => "I",
KeyCode::O => "O",
KeyCode::P => "P",
KeyCode::LeftBracket => "{",
KeyCode::RightBracket => "}",
KeyCode::A => "A",
KeyCode::S => "S",
KeyCode::D => "D",
KeyCode::F => "F",
KeyCode::G => "G",
KeyCode::H => "H",
KeyCode::J => "J",
KeyCode::K => "K",
KeyCode::L => "L",
KeyCode::Semicolon => ":",
KeyCode::Slash => "?",
KeyCode::Backslash => "|",
KeyCode::Z => "Z",
KeyCode::X => "X",
KeyCode::C => "C",
KeyCode::V => "V",
KeyCode::B => "B",
KeyCode::N => "N",
KeyCode::M => "M",
KeyCode::Comma => "<",
KeyCode::Period => ">",
_ => "",
}
}
}
pub unsafe fn superclass(this: &Object) -> &Class {
let superclass: ObjcId = msg_send![this, superclass];
&*(superclass as *const _)
}
#[cfg(target_os = "macos")]
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
let height = unsafe { CGDisplayPixelsHigh(CGMainDisplayID()) };
height as f64 - (rect.origin.y + rect.size.height)
}
pub fn load_mouse_cursor(cursor: CursorIcon) -> ObjcId {
match cursor {
CursorIcon::Default => load_native_cursor("arrowCursor"),
CursorIcon::Pointer => load_native_cursor("pointingHandCursor"),
CursorIcon::Text => load_native_cursor("IBeamCursor"),
CursorIcon::NotAllowed => load_native_cursor("operationNotAllowedCursor"),
CursorIcon::Crosshair => load_native_cursor("crosshairCursor"),
CursorIcon::EWResize => load_native_cursor("resizeLeftRightCursor"),
CursorIcon::NSResize => load_native_cursor("resizeUpDownCursor"),
_ => load_native_cursor("arrowCursor"),
}
}
macro_rules! msg_send_ {
($obj:expr, $name:ident) => ({
let res: ObjcId = msg_send!($obj, $name);
res
});
($obj:expr, $($name:ident : $arg:expr)+) => ({
let res: ObjcId = msg_send!($obj, $($name: $arg)*);
res
});
}
pub(crate) use msg_send_;
pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
YES
}
pub extern "C" fn yes1(_: &Object, _: Sel, _: ObjcId) -> BOOL {
YES
}