kael 0.2.0

GPU-accelerated native UI framework for Rust — build desktop apps with Metal, DirectX, and Vulkan rendering
Documentation
use crate::platform::FocusedWindowInfo;
use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use objc2_app_kit::NSWorkspace;
use std::ffi::c_void;

#[link(name = "ApplicationServices", kind = "framework")]
unsafe extern "C" {
    fn AXUIElementCreateApplication(pid: i32) -> *mut c_void;
    fn AXUIElementCopyAttributeValue(
        element: *mut c_void,
        attribute: core_foundation::string::CFStringRef,
        value: *mut *mut c_void,
    ) -> i32;
}

pub fn get_focused_window_info() -> Option<FocusedWindowInfo> {
    let workspace = NSWorkspace::sharedWorkspace();
    let frontmost_app = workspace.frontmostApplication()?;
    let app_name = frontmost_app.localizedName()?.to_string();
    let bundle_id = frontmost_app.bundleIdentifier().map(|id| id.to_string());
    let pid = frontmost_app.processIdentifier();
    let window_title = get_window_title_via_accessibility(pid).unwrap_or_default();

    Some(FocusedWindowInfo {
        app_name,
        window_title,
        bundle_id,
        pid: Some(pid as u32),
    })
}

fn get_window_title_via_accessibility(pid: i32) -> Option<String> {
    unsafe {
        let app_element = AXUIElementCreateApplication(pid);
        if app_element.is_null() {
            return None;
        }

        let focused_window_attr = CFString::new("AXFocusedWindow");
        let mut window_value: *mut c_void = std::ptr::null_mut();
        let result = AXUIElementCopyAttributeValue(
            app_element,
            focused_window_attr.as_concrete_TypeRef(),
            &mut window_value,
        );
        core_foundation::base::CFRelease(app_element as _);

        if result != 0 || window_value.is_null() {
            return None;
        }

        let title_attr = CFString::new("AXTitle");
        let mut title_value: *mut c_void = std::ptr::null_mut();
        let result = AXUIElementCopyAttributeValue(
            window_value,
            title_attr.as_concrete_TypeRef(),
            &mut title_value,
        );
        core_foundation::base::CFRelease(window_value as _);

        if result != 0 || title_value.is_null() {
            return None;
        }

        let cf_title = core_foundation::string::CFString::wrap_under_create_rule(
            title_value as core_foundation::string::CFStringRef,
        );
        Some(cf_title.to_string())
    }
}