1#![allow(unsafe_op_in_unsafe_fn)]
2#![allow(non_camel_case_types)]
25
26use core::ffi::c_void;
27
28pub type Id = *mut c_void;
32pub type Class = *const c_void;
34pub type Sel = *const c_void;
36pub const NIL: Id = core::ptr::null_mut();
38
39unsafe extern "C" {
42 pub fn objc_getClass(name: *const u8) -> Class;
43 pub fn sel_registerName(name: *const u8) -> Sel;
44 pub fn objc_msgSend(receiver: Id, sel: Sel, ...) -> Id;
45 #[cfg(target_arch = "x86_64")]
47 pub fn objc_msgSend_stret(stret: *mut c_void, receiver: Id, sel: Sel, ...);
48}
49
50pub type CFTypeRef = *const c_void;
53pub type CFStringRef = *const c_void;
54pub type CFDataRef = *const c_void;
55pub type CFDictionaryRef = *const c_void;
56pub type CFAllocatorRef = *const c_void;
57pub type CFIndex = isize;
58
59pub const K_CF_STRING_ENCODING_UTF8: u32 = 0x08000100;
60pub const K_CF_ALLOCATOR_DEFAULT: CFAllocatorRef = core::ptr::null();
61
62unsafe extern "C" {
63 pub fn CFRelease(cf: CFTypeRef);
64 pub fn CFRetain(cf: CFTypeRef) -> CFTypeRef;
65 pub fn CFStringCreateWithBytes(
66 alloc: CFAllocatorRef, bytes: *const u8, num_bytes: CFIndex,
67 encoding: u32, is_external: bool,
68 ) -> CFStringRef;
69 pub fn CFStringGetLength(s: CFStringRef) -> CFIndex;
70 pub fn CFStringGetCStringPtr(s: CFStringRef, encoding: u32) -> *const u8;
71 pub fn CFStringGetCString(
72 s: CFStringRef, buffer: *mut u8, buffer_size: CFIndex, encoding: u32,
73 ) -> bool;
74 pub fn CFDataCreate(alloc: CFAllocatorRef, bytes: *const u8, length: CFIndex) -> CFDataRef;
75 pub fn CFDataGetLength(data: CFDataRef) -> CFIndex;
76 pub fn CFDataGetBytePtr(data: CFDataRef) -> *const u8;
77 pub fn CFDictionaryCreate(
78 alloc: CFAllocatorRef,
79 keys: *const CFTypeRef, values: *const CFTypeRef, num_values: CFIndex,
80 key_cbs: *const c_void, value_cbs: *const c_void,
81 ) -> CFDictionaryRef;
82
83 pub static kCFTypeDictionaryKeyCallBacks: c_void;
84 pub static kCFTypeDictionaryValueCallBacks: c_void;
85 pub static kCFBooleanTrue: CFTypeRef;
86 pub static kCFBooleanFalse: CFTypeRef;
87}
88
89unsafe extern "C" {
92 fn dlsym(handle: *mut c_void, symbol: *const u8) -> *mut c_void;
93}
94
95#[inline]
97pub unsafe fn dlsym_global(name: &[u8]) -> *const c_void {
98 dlsym((-2isize) as *mut c_void, name.as_ptr()) as *const c_void
99}
100
101#[inline]
105pub unsafe fn global_string_const(name: &[u8]) -> CFStringRef {
106 let ptr = dlsym_global(name);
107 if ptr.is_null() { return core::ptr::null(); }
108 *(ptr as *const CFStringRef)
109}
110
111#[inline]
118pub unsafe fn nsstring(s: &str) -> Id {
119 CFStringCreateWithBytes(
120 K_CF_ALLOCATOR_DEFAULT,
121 s.as_ptr(), s.len() as CFIndex,
122 K_CF_STRING_ENCODING_UTF8, false,
123 ) as Id
124}
125
126pub unsafe fn nsstring_to_string(s: Id) -> Option<String> {
128 if s.is_null() { return None; }
129 let cfstr = s as CFStringRef;
130 let cptr = CFStringGetCStringPtr(cfstr, K_CF_STRING_ENCODING_UTF8);
132 if !cptr.is_null() {
133 let cstr = core::ffi::CStr::from_ptr(cptr as *const core::ffi::c_char);
134 return Some(cstr.to_string_lossy().into_owned());
135 }
136 let len = CFStringGetLength(cfstr);
138 let buf_size = len * 4 + 1; let mut buf = vec![0u8; buf_size as usize];
140 if CFStringGetCString(cfstr, buf.as_mut_ptr(), buf_size, K_CF_STRING_ENCODING_UTF8) {
141 let cstr = core::ffi::CStr::from_ptr(buf.as_ptr() as *const core::ffi::c_char);
142 Some(cstr.to_string_lossy().into_owned())
143 } else {
144 None
145 }
146}
147
148pub unsafe fn nsstring_to_buf(s: Id, buf: *mut u8, buf_len: usize) -> isize {
150 if s.is_null() { return -1; }
151 match nsstring_to_string(s) {
152 Some(string) => {
153 let bytes = string.as_bytes();
154 let n = bytes.len().min(buf_len);
155 core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, n);
156 n as isize
157 }
158 None => -1,
159 }
160}
161
162#[inline]
164pub unsafe fn cfdict(keys: &[CFTypeRef], vals: &[CFTypeRef]) -> CFDictionaryRef {
165 CFDictionaryCreate(
166 K_CF_ALLOCATOR_DEFAULT,
167 keys.as_ptr(), vals.as_ptr(), keys.len() as CFIndex,
168 &kCFTypeDictionaryKeyCallBacks as *const _ as *const c_void,
169 &kCFTypeDictionaryValueCallBacks as *const _ as *const c_void,
170 )
171}
172
173#[macro_export]
181macro_rules! class {
182 ($name:expr) => {
183 $crate::objc_getClass($name.as_ptr())
184 };
185}
186
187#[macro_export]
193macro_rules! sel {
194 ($name:expr) => {
195 $crate::sel_registerName($name.as_ptr())
196 };
197}
198
199#[macro_export]
208macro_rules! msg_send {
209 [$obj:expr, $sel:ident] => {{
211 let f: unsafe extern "C" fn($crate::Id, $crate::Sel) -> $crate::Id =
212 core::mem::transmute($crate::objc_msgSend as *const ());
213 f($obj as $crate::Id, $crate::sel!(concat!(stringify!($sel), "\0").as_bytes()))
214 }};
215
216 [$obj:expr, $($sel:ident : $arg:expr),+ $(,)?] => {{
218 let sel_name = concat!($(stringify!($sel), ":",)+ "\0");
220 let sel = $crate::sel_registerName(sel_name.as_ptr());
221 msg_send!(@call $obj, sel, $($arg),+)
223 }};
224
225 (@call $obj:expr, $sel:expr, $a1:expr) => {{
227 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _) -> $crate::Id =
228 core::mem::transmute($crate::objc_msgSend as *const ());
229 f($obj as $crate::Id, $sel, $a1)
230 }};
231 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr) => {{
233 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _) -> $crate::Id =
234 core::mem::transmute($crate::objc_msgSend as *const ());
235 f($obj as $crate::Id, $sel, $a1, $a2)
236 }};
237 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr) => {{
239 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _) -> $crate::Id =
240 core::mem::transmute($crate::objc_msgSend as *const ());
241 f($obj as $crate::Id, $sel, $a1, $a2, $a3)
242 }};
243 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
245 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _, _) -> $crate::Id =
246 core::mem::transmute($crate::objc_msgSend as *const ());
247 f($obj as $crate::Id, $sel, $a1, $a2, $a3, $a4)
248 }};
249 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => {{
251 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _, _, _) -> $crate::Id =
252 core::mem::transmute($crate::objc_msgSend as *const ());
253 f($obj as $crate::Id, $sel, $a1, $a2, $a3, $a4, $a5)
254 }};
255 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => {{
257 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _, _, _, _) -> $crate::Id =
258 core::mem::transmute($crate::objc_msgSend as *const ());
259 f($obj as $crate::Id, $sel, $a1, $a2, $a3, $a4, $a5, $a6)
260 }};
261}
262
263#[macro_export]
270macro_rules! msg_send_t {
271 [$t:ty; $obj:expr, $sel:ident] => {{
273 let f: unsafe extern "C" fn($crate::Id, $crate::Sel) -> $t =
274 core::mem::transmute($crate::objc_msgSend as *const ());
275 f($obj as $crate::Id, $crate::sel!(concat!(stringify!($sel), "\0").as_bytes()))
276 }};
277 [$t:ty; $obj:expr, $($sel:ident : $arg:expr),+ $(,)?] => {{
279 let sel_name = concat!($(stringify!($sel), ":",)+ "\0");
280 let sel = $crate::sel_registerName(sel_name.as_ptr());
281 msg_send_t!(@call $t; $obj, sel, $($arg),+)
282 }};
283
284 (@call $t:ty; $obj:expr, $sel:expr, $a1:expr) => {{
285 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _) -> $t =
286 core::mem::transmute($crate::objc_msgSend as *const ());
287 f($obj as $crate::Id, $sel, $a1)
288 }};
289 (@call $t:ty; $obj:expr, $sel:expr, $a1:expr, $a2:expr) => {{
290 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _) -> $t =
291 core::mem::transmute($crate::objc_msgSend as *const ());
292 f($obj as $crate::Id, $sel, $a1, $a2)
293 }};
294 (@call $t:ty; $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr) => {{
295 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _) -> $t =
296 core::mem::transmute($crate::objc_msgSend as *const ());
297 f($obj as $crate::Id, $sel, $a1, $a2, $a3)
298 }};
299 (@call $t:ty; $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {{
300 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _, _) -> $t =
301 core::mem::transmute($crate::objc_msgSend as *const ());
302 f($obj as $crate::Id, $sel, $a1, $a2, $a3, $a4)
303 }};
304}
305
306#[macro_export]
308macro_rules! msg_send_void {
309 [$obj:expr, $sel:ident] => {{
310 let f: unsafe extern "C" fn($crate::Id, $crate::Sel) =
311 core::mem::transmute($crate::objc_msgSend as *const ());
312 f($obj as $crate::Id, $crate::sel!(concat!(stringify!($sel), "\0").as_bytes()))
313 }};
314 [$obj:expr, $($sel:ident : $arg:expr),+ $(,)?] => {{
315 let sel_name = concat!($(stringify!($sel), ":",)+ "\0");
316 let sel = $crate::sel_registerName(sel_name.as_ptr());
317 msg_send_void!(@call $obj, sel, $($arg),+)
318 }};
319
320 (@call $obj:expr, $sel:expr, $a1:expr) => {{
321 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _) =
322 core::mem::transmute($crate::objc_msgSend as *const ());
323 f($obj as $crate::Id, $sel, $a1)
324 }};
325 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr) => {{
326 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _) =
327 core::mem::transmute($crate::objc_msgSend as *const ());
328 f($obj as $crate::Id, $sel, $a1, $a2)
329 }};
330 (@call $obj:expr, $sel:expr, $a1:expr, $a2:expr, $a3:expr) => {{
331 let f: unsafe extern "C" fn($crate::Id, $crate::Sel, _, _, _) =
332 core::mem::transmute($crate::objc_msgSend as *const ());
333 f($obj as $crate::Id, $sel, $a1, $a2, $a3)
334 }};
335}