cacao/appkit/bundle.rs

100 lines
3.2 KiB
Rust
Raw Normal View History

2020-02-28 13:34:34 +11:00
//! Implements some stuff to handle dynamically setting the `NSBundle` identifier.
//! This is not currently in use, but does have places where it's useful... and to be honest I'm
//! kinda happy this is done as a swizzling implementation in pure Rust, which I couldn't find
//! examples of anywhere else.
//!
//! Disregard until you can't, I guess.
use std::ffi::CString;
use std::mem;
use objc::{class, msg_send, sel, sel_impl, Encode, Encoding, EncodeArguments, Message};
use objc::runtime::{Class, Sel, Method, Object, Imp};
use objc::runtime::{
objc_getClass,
class_addMethod,
class_getInstanceMethod,
method_exchangeImplementations
};
use crate::foundation::{id, nil, BOOL, YES, NSString};
2020-02-28 13:34:34 +11:00
/// Types that can be used as the implementation of an Objective-C method.
pub trait MethodImplementation {
/// The callee type of the method.
type Callee: Message;
/// The return type of the method.
type Ret: Encode;
/// The argument types of the method.
type Args: EncodeArguments;
/// Returns self as an `Imp` of a method.
fn imp(self) -> Imp;
}
macro_rules! method_decl_impl {
(-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
impl<$s, $r $(, $t)*> MethodImplementation for $f
where $s: Message, $r: Encode $(, $t: Encode)* {
type Callee = $s;
type Ret = $r;
type Args = ($($t,)*);
fn imp(self) -> Imp {
unsafe { mem::transmute(self) }
}
}
);
($($t:ident),*) => (
method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
);
}
method_decl_impl!();
method_decl_impl!(A);
extern fn get_bundle_id(this: &Object, s: Sel, v: id) -> id {
unsafe {
let bundle = class!(NSBundle);
let main_bundle: id = msg_send![bundle, mainBundle];
let e: BOOL = msg_send![this, isEqual:main_bundle];
if e == YES {
let url: id = msg_send![main_bundle, bundleURL];
let x: id = msg_send![url, absoluteString];
println!("Got here? {:?}", x);
unsafe {
NSString::alloc(nil).init_str("com.secretkeys.subatomic")
}
} else {
msg_send![this, __bundleIdentifier]
}
}
}
unsafe fn swizzle_bundle_id<F>(bundle_id: &str, func: F) where F: MethodImplementation<Callee=Object> {
let name = CString::new("NSBundle").unwrap();
let cls = objc_getClass(name.as_ptr());
// let mut cls = class!(NSBundle) as *mut Class;
// Class::get("NSBundle").unwrap();
// let types = format!("{}{}{}", Encoding::String, <*mut Object>::ENCODING, Sel::ENCODING);
let added = class_addMethod(
cls as *mut Class,
sel!(__bundleIdentifier),
func.imp(),
CString::new("*@:").unwrap().as_ptr()
);
let method1 = class_getInstanceMethod(cls, sel!(bundleIdentifier)) as *mut Method;
let method2 = class_getInstanceMethod(cls, sel!(__bundleIdentifier)) as *mut Method;
method_exchangeImplementations(method1, method2);
}
pub fn set_bundle_id(bundle_id: &str) {
unsafe {
swizzle_bundle_id(bundle_id, get_bundle_id as extern fn(&Object, _, _) -> id);
}
}