在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
相关产品推荐
相关产品推荐

