You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rust中获取macOS当前聚焦窗口信息并修改其位置

macOS Rust 获取聚焦窗口并修改窗口属性

崩溃原因分析

你的代码触发SIGABRT崩溃,核心原因是错误地将kCGWindowLayer对应的CFNumber类型值当作CFString处理。CFDictionary中kCGWindowLayer的存储类型是CFNumber,而非CFString,强行调用CFStringGetIntValue会导致类型不匹配,触发系统崩溃。

正确获取聚焦窗口的名称与Window ID

要精准获取当前聚焦窗口,需在窗口列表中筛选出标记为kCGWindowIsFrontmost的窗口,同时正确处理CFNumber类型字段。

首先在Cargo.toml添加依赖:

[dependencies]
core-foundation = "0.9"
core-graphics = "0.23"
libc = "0.2"

实现获取聚焦窗口的函数:

use core_foundation::{
    array::CFArray,
    base::{CFRelease, CFTypeRef, TCFType},
    dictionary::CFDictionary,
    number::CFNumber,
    string::CFString,
};
use core_graphics::window::{CGWindowListOption, CGWindowListCopyWindowInfo, kCGNullWindowID};
use libc::c_void;

#[derive(Debug)]
pub struct FocusedWindow {
    pub name: Option<String>,
    pub window_id: u64,
}

pub fn get_focused_window() -> Option<FocusedWindow> {
    const OPTIONS: CGWindowListOption = CGWindowListOption::kCGWindowListOptionOnScreenOnly 
        | CGWindowListOption::kCGWindowListExcludeDesktopElements;
    
    // 获取所有屏幕窗口信息
    let window_list = unsafe { CGWindowListCopyWindowInfo(OPTIONS, kCGNullWindowID) };
    if window_list.is_null() {
        return None;
    }
    let window_array = unsafe { CFArray::wrap_under_create_rule(window_list) };

    for elem in window_array.iter() {
        let dict = unsafe { CFDictionary::wrap_under_get_rule(*elem as *mut _) };
        
        // 筛选前台窗口
        let is_frontmost_key = CFString::new("kCGWindowIsFrontmost");
        let is_frontmost = dict.find(&is_frontmost_key)
            .and_then(|val| unsafe { CFNumber::wrap_under_get_rule(*val as *mut _) }.to_i32())
            .unwrap_or(0);
        if is_frontmost != 1 {
            continue;
        }

        // 获取窗口名称
        let name_key = CFString::new("kCGWindowName");
        let name = dict.find(&name_key)
            .and_then(|val| unsafe { CFString::wrap_under_get_rule(*val as *mut _) }.to_string());

        // 获取Window ID(kCGWindowNumber为CFNumber类型)
        let window_id_key = CFString::new("kCGWindowNumber");
        let window_id = dict.find(&window_id_key)
            .and_then(|val| unsafe { CFNumber::wrap_under_get_rule(*val as *mut _) }.to_u64())?;

        unsafe {
            CFRelease(window_array.as_CFTypeRef());
        }

        return Some(FocusedWindow { name, window_id });
    }

    unsafe {
        CFRelease(window_array.as_CFTypeRef());
    }
    None
}

修改窗口位置与大小

通过CGWindowListCreateDescriptionFromArray和CGWindowListSetWindowDescriptions接口实现窗口属性修改:

use core_graphics::window::{CGWindowListCreateDescriptionFromArray, CGWindowListSetWindowDescriptions};
use core_foundation::{
    array::CFArray,
    dictionary::CFDictionary,
    number::CFNumber,
    string::CFString,
    base::TCFType,
};

/// 修改窗口位置(x,y)与大小(width,height)
pub fn set_window_geometry(window_id: u64, x: i32, y: i32, width: u32, height: u32) -> bool {
    // 构造窗口ID字段
    let window_num_key = CFString::new("kCGWindowNumber");
    let window_num_val = CFNumber::from(window_id);
    
    // 构造窗口边界字典
    let bounds_key = CFString::new("kCGWindowBounds");
    let bounds_dict = CFDictionary::from_CFType_pairs(&[
        (CFString::new("X").as_CFType(), CFNumber::from(x).as_CFType()),
        (CFString::new("Y").as_CFType(), CFNumber::from(y).as_CFType()),
        (CFString::new("Width").as_CFType(), CFNumber::from(width).as_CFType()),
        (CFString::new("Height").as_CFType(), CFNumber::from(height).as_CFType()),
    ]);

    // 构造完整窗口描述字典
    let window_desc = CFDictionary::from_CFType_pairs(&[
        (window_num_key.as_CFType(), window_num_val.as_CFType()),
        (bounds_key.as_CFType(), bounds_dict.as_CFType()),
    ]);

    // 创建描述数组并调用API修改窗口
    let desc_array = CFArray::from_CFTypes(&[window_desc.as_CFType()]);
    let result = unsafe {
        CGWindowListSetWindowDescriptions(CGWindowListCreateDescriptionFromArray(desc_array.as_CFArrayRef()))
    };

    result != 0
}

关键注意事项

  • 权限要求:macOS 10.14及以上版本,需在「系统偏好设置>安全性与隐私>隐私>辅助功能」中添加你的应用,否则无法修改窗口属性。
  • 内存管理:使用core-foundation的wrap_under_create_rule包裹的对象需手动调用CFRelease释放,wrap_under_get_rule包裹的对象无需手动释放。
  • 错误处理:实际项目中需替换unwrap为自定义错误处理逻辑,避免panic。

内容的提问来源于stack exchange,提问作者Frederic Laing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 02:54:51