macosrustcore-foundation

Getting window owner names via CGWindowListCopyWindowInfo in Rust


I am trying to get the window owner names via CGWindowListCopyWindowInfo in Rust. So far I have managed to get the CFDictionaryRef but I am unable to use the right pointers for CFDictionaryGetValueIfPresent.

This is the method signature: https://docs.rs/CoreFoundation-sys/0.1.4/CoreFoundation_sys/dictionary/fn.CFDictionaryGetValueIfPresent.html

const options: CGWindowListOption = kCGWindowListOptionOnScreenOnly + kCGWindowListExcludeDesktopElements;
const ptr_window_list_info: CFArrayRef = unsafe { CGWindowListCopyWindowInfo(options, kCGNullWindowID) as CFArrayRef };

const count = unsafe { CFArrayGetCount(ptr_window_list_info) };

for i in 0..count-1 {
    let dic_ref = CFArrayGetValueAtIndex(ptr_window_list_info, i) as CFDictionaryRef;
    //let key = CFString::new("kCGWindowOwnerName");
    let b = CFDictionaryGetValueIfPresent(dic_ref, ?, ?);
    println!("window owner name: {}", value);
}

I am new to Rust and would appreciate any help.


Solution

  • CFDictionaryGetValueIfPresent looks like this:

    Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef theDict, const void *key, const void **value);

    In core_foundation, it looks like this:

    pub unsafe extern "C" fn CFDictionaryGetValueIfPresent(
        theDict: CFDictionaryRef, 
        key: *const c_void, 
        value: *mut *const c_void
    ) -> Boolean
    

    I believe the key wants to be CFStringRef; in C there is a macro to make that easier (CFSTR("cstring")), here we must use a slightly longer form:

    let c_key = CString::new("kCGWindowOwnerName").unwrap();
    let cf_key = unsafe { 
        CFStringCreateWithCString(std::ptr::null(), c_key.as_ptr(), kCFStringEncodingUTF8) 
    };
    

    That gives us CFStringRef, in core_foundation it's defined like this:

    pub type CFStringRef = *const __CFString;

    It's already a *const pointer, just not the type we need. You can transmute to c_void instead:

    let key_ptr: *const c_void = unsafe { std::mem::transmute(cf_key) };

    The value argument just wants a raw double pointer to store the result. Create a null pointer:

    let mut value: *const c_void = std::ptr::null();

    And pass a mutable reference to it (&mut value) to get *mut *const c_void.


    There are a few mistakes and oddities in your rust code. Here is a commented, working example:

    use core_graphics::display::*;
    use core_foundation::string::*;
    use std::ffi::{ CStr, CString, c_void };
    
    fn main() {
        // CGWindowListOption is a bitmask, combine the flags with bitwise OR
        const OPTIONS: CGWindowListOption = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
        // No need to specify the type or use 'as'; CFArrayRef is the return type from CGWindowListCopyWindowInfo
        let window_list_info = unsafe { CGWindowListCopyWindowInfo(OPTIONS, kCGNullWindowID) };
        // Don't use const here, CFArrayGetCount returns CFIndex (long)
        let count: i64 = unsafe { CFArrayGetCount(window_list_info) };
    
        for i in 0..count-1 {
            // Here we need the 'as', CFArrayGetValueAtIndex just returns void*
            let dic_ref = unsafe { CFArrayGetValueAtIndex(window_list_info, i) as CFDictionaryRef };
    
            // Create a CString from the key name we are interested in
            let c_key = CString::new("kCGWindowOwnerName").unwrap();
            // Create a CFString, needs to be released with `CFRelease`. I leave that as an exercise for the reader.
            let cf_key = unsafe { CFStringCreateWithCString(std::ptr::null(), c_key.as_ptr(), kCFStringEncodingUTF8) };
    
            // cf_key is a CFStringRef, which is a type alias to *const __CFString
            // We transmute it into *const c_void since that is what CFDictionaryGetValueIfPresent wants
            let key_ptr: *const c_void = unsafe { std::mem::transmute(cf_key) };
    
            // A raw void* to hold the result
            let mut value: *const c_void = std::ptr::null();
    
            if unsafe { CFDictionaryGetValueIfPresent(dic_ref, key_ptr, &mut value) != 0 } {
                // CFDictionaryGetValueIfPresent returned true; that means value must point to a CFStringRef
                let cf_ref = value as core_foundation::string::CFStringRef;
                // Get a pointer to a C-string buffer with the characters from the CFString
                let c_ptr = unsafe { CFStringGetCStringPtr(cf_ref, kCFStringEncodingUTF8) };
    
                // The value may be null; don't pass it to CStr::from_ptr
                if c_ptr.is_null() { continue; }
    
                // Wrap the pointer in a rust CStr so we can convert a str
                let c_value = unsafe { CStr::from_ptr(c_ptr) };
                println!("{}", c_value.to_str().unwrap());
            }
        }
    }
    

    For reference, here is the same example in C:

    #import <Foundation/Foundation.h>
    #import <CoreGraphics/CoreGraphics.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            CGWindowListOption options = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
            CFArrayRef windows = CGWindowListCopyWindowInfo(options, kCGNullWindowID);
    
            CFIndex count = CFArrayGetCount(windows);
    
            for (int i = 0; i < count; i++)
            {
                CFDictionaryRef windowDict = CFArrayGetValueAtIndex(windows, i);
                CFStringRef key = CFStringCreateWithCString(NULL, "kCGWindowOwnerName", kCFStringEncodingUTF8);
                const void* value = nil;
    
                if (CFDictionaryGetValueIfPresent(windowDict, key, &value) == YES)
                {
                    const char* c_value = CFStringGetCStringPtr(value, kCFStringEncodingUTF8);
    
                    NSLog(@"window: %s", c_value);
                }
    
                CFRelease(key);
            }
        }
        return 0;
    }
    
    

    Disclaimer I am also relatively new to rust and this may not be the idiomatic solution