Convert *const __CFData to String in Rust

211 views Asked by At

I am trying to convert *const __CFData to String in rust.

I am trying to get Keyboard language and checking if it is a particular language.

let current_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
let current_language = ffi::TISGetInputSourceProperty(current_source, ffi::kTISPropertyLocalizedName);
let language = CFDataGetBytePtr(current_language);
CStr::from_ptr(language).to_str().unwrap().to_string().contains("English")

I get this runtime error - -[__NSCFConstantString bytes]: unrecognized selector sent to instance 0x7ff85dfed1d8

TISGetInputSourceProperty returns a *const __CFData and CFDataGetBytePtr should return a *const u8, but it fails at that function with the mentioned error.

How do I get the language and check what it is in rust? TIA.

1

There are 1 answers

5
fresskoma On

So I played around with this a little, and came up with the following, which seems to work. Note that, as far as I could tell, this API is definitely deprecated though.

(the following needs libc as a dependency)

use libc::{size_t, c_void, c_char};

// A lot of these types taken from https://github.com/servo/core-foundation-rs/blob/0876315e2e434c2b5e5f406e7d540360c24b8c2e/core-foundation-sys/src/base.rs#L24
pub type CFStringEncoding = u32;
pub type Boolean = u8;
pub type CFIndex = isize;
pub static kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;

#[repr(C)]
pub struct __CFString(c_void);
pub type CFStringRef = *const __CFString;

#[link(name = "Carbon", kind = "framework")]
extern "C" {
    fn TISCopyCurrentKeyboardInputSource() -> *const c_void;
    fn TISGetInputSourceProperty(a: *const c_void, b: *const c_void) -> CFStringRef;
    static kTISPropertyLocalizedName: *const c_void;
}

#[link(name = "CoreFoundation", kind = "framework")]
extern {
    pub fn CFStringGetCString(theString: CFStringRef,
        buffer: *mut c_char,
        bufferSize: CFIndex,
        encoding: CFStringEncoding)
        -> Boolean;
}

fn main() {
    unsafe {
        let v = vec![0; 256];
        let s = std::ffi::CString::from_vec_unchecked(v);
        let ptr = s.into_raw();

        let source = TISCopyCurrentKeyboardInputSource();
        let y = TISGetInputSourceProperty(source, kTISPropertyLocalizedName);
        CFStringGetCString(y, ptr, 256, kCFStringEncodingUTF8);

        println!("{:?}", std::ffi::CString::from_raw(ptr));
        // Prints "ABC" on my machine
    }
}

Note that CFStringGetCStringPtr did not work for me, as it returned a null-pointer. I'm guessing this is because (docs):

[Returns] NULL if the internal storage of theString does not allow [the pointer] to be returned efficiently.