diff --git a/examples/focused_window.rs b/examples/focused_window.rs new file mode 100644 index 0000000..1f53172 --- /dev/null +++ b/examples/focused_window.rs @@ -0,0 +1,25 @@ +use std::thread; +use xcap::Window; + +fn main() { + thread::sleep(std::time::Duration::from_secs(3)); + + let windows = Window::all().unwrap(); + + loop { + windows.iter().filter(|w| w.is_focused()).for_each(|focused| { + println!( + "Focused Window:\n id: {}\n title: {}\n app_name: {}\n monitor: {:?}\n position: {:?}\n size {:?}\n state {:?}\n", + focused.id(), + focused.title(), + focused.app_name(), + focused.current_monitor().name(), + (focused.x(), focused.y(), focused.z()), + (focused.width(), focused.height()), + (focused.is_minimized(), focused.is_maximized(), focused.is_focused()) + ); + }); + + thread::sleep(std::time::Duration::from_secs(1)); + } +} diff --git a/src/linux/impl_window.rs b/src/linux/impl_window.rs index 61124c6..a80d719 100644 --- a/src/linux/impl_window.rs +++ b/src/linux/impl_window.rs @@ -28,7 +28,6 @@ pub(crate) struct ImplWindow { pub height: u32, pub is_minimized: bool, pub is_maximized: bool, - pub is_focused: bool, } fn get_atom(conn: &Connection, name: &str) -> XCapResult { @@ -80,14 +79,15 @@ pub fn get_window_pid(conn: &Connection, window: &Window) -> XCapResult { .copied() } -fn get_active_window_id(conn: &Connection) -> Option { - let active_window_atom = get_atom(conn, "_NET_ACTIVE_WINDOW").ok()?; +fn get_active_window_id() -> Option { + let (conn, _) = Connection::connect(None).ok()?; + let active_window_atom = get_atom(&conn, "_NET_ACTIVE_WINDOW").ok()?; let setup = conn.get_setup(); for screen in setup.roots() { let root_window = screen.root(); let active_window_id = - get_window_property(conn, root_window, active_window_atom, ATOM_NONE, 0, 4).ok()?; + get_window_property(&conn, root_window, active_window_atom, ATOM_NONE, 0, 4).ok()?; if let Some(&active_window_id) = active_window_id.value::().first() { return Some(active_window_id); } @@ -102,7 +102,6 @@ impl ImplWindow { window: &Window, pid: u32, z: i32, - is_focused: bool, impl_monitors: &Vec, ) -> XCapResult { let title = { @@ -215,10 +214,15 @@ impl ImplWindow { height, is_minimized, is_maximized, - is_focused, }) } + pub fn is_focused(&self) -> bool { + let active_window_id = get_active_window_id(); + + active_window_id.eq(&Some(self.pid)) + } + pub fn all() -> XCapResult> { let (conn, _) = Connection::connect(None)?; let setup = conn.get_setup(); @@ -227,7 +231,6 @@ impl ImplWindow { // https://specifications.freedesktop.org/wm-spec/1.5/ar01s03.html#id-1.4.4 // list all windows by stacking order let client_list_atom = get_atom(&conn, "_NET_CLIENT_LIST_STACKING")?; - let active_window_id = get_active_window_id(&conn); let mut impl_windows = Vec::new(); let impl_monitors = ImplMonitor::all()?; @@ -267,10 +270,7 @@ impl ImplWindow { } }; - let is_focused = active_window_id.eq(&Some(client.resource_id())); - - if let Ok(impl_window) = - ImplWindow::new(&conn, client, pid, z, is_focused, &impl_monitors) + if let Ok(impl_window) = ImplWindow::new(&conn, client, pid, z, &impl_monitors) { impl_windows.push(impl_window); } else { diff --git a/src/macos/impl_window.rs b/src/macos/impl_window.rs index 57fd920..a920bd3 100644 --- a/src/macos/impl_window.rs +++ b/src/macos/impl_window.rs @@ -11,6 +11,7 @@ use objc2_core_graphics::{ CGDisplayBounds, CGMainDisplayID, CGRectContainsPoint, CGRectIntersectsRect, CGRectMakeWithDictionaryRepresentation, CGWindowListCopyWindowInfo, CGWindowListOption, }; +use objc2_foundation::{NSNumber, NSString}; use crate::{error::XCapResult, XCapError}; @@ -30,7 +31,6 @@ pub(crate) struct ImplWindow { pub height: u32, pub is_minimized: bool, pub is_maximized: bool, - pub is_focused: bool, } unsafe impl Send for ImplWindow {} @@ -117,7 +117,6 @@ impl ImplWindow { window_name: String, window_owner_name: String, z: i32, - focused_app_pid: Option, ) -> XCapResult { let id = get_cf_number_i32_value(window_cf_dictionary, "kCGWindowNumber")? as u32; let pid = get_cf_number_i32_value(window_cf_dictionary, "kCGWindowOwnerPID")?; @@ -154,8 +153,6 @@ impl ImplWindow { let is_minimized = !get_cf_bool_value(window_cf_dictionary, "kCGWindowIsOnscreen")? && !is_maximized; - let is_focused = focused_app_pid.eq(&Some(pid)); - Ok(ImplWindow { id, title: window_name, @@ -169,17 +166,26 @@ impl ImplWindow { height: cg_rect.size.height as u32, is_minimized, is_maximized, - is_focused, }) } + pub fn is_focused(&self) -> bool { + let focused_app_pid = unsafe { + let workspace = NSWorkspace::sharedWorkspace(); + let pid_key = NSString::from_str("NSApplicationProcessIdentifier"); + workspace + .activeApplication() + .and_then(|dictionary| dictionary.valueForKey(&pid_key)) + .and_then(|pid| pid.downcast::().ok()) + .map(|pid| pid.intValue()) + }; + + focused_app_pid.eq(&Some(self.pid as i32)) + } + pub fn all() -> XCapResult> { unsafe { let impl_monitors = ImplMonitor::all()?; - let workspace = NSWorkspace::sharedWorkspace(); - let focused_app_pid = workspace - .frontmostApplication() - .map(|focused_app| focused_app.processIdentifier()); let mut impl_windows = Vec::new(); @@ -236,7 +242,6 @@ impl ImplWindow { window_name.clone(), window_owner_name.clone(), num_windows as i32 - i as i32 - 1, - focused_app_pid, ) { impl_windows.push(impl_window); } else { diff --git a/src/window.rs b/src/window.rs index 7e66d55..a1ef62f 100644 --- a/src/window.rs +++ b/src/window.rs @@ -76,7 +76,7 @@ impl Window { } /// The window is focused. pub fn is_focused(&self) -> bool { - self.impl_window.is_focused + self.impl_window.is_focused() } } diff --git a/src/windows/impl_window.rs b/src/windows/impl_window.rs index 7008ffb..a8b5ea0 100644 --- a/src/windows/impl_window.rs +++ b/src/windows/impl_window.rs @@ -51,7 +51,6 @@ pub(crate) struct ImplWindow { pub height: u32, pub is_minimized: bool, pub is_maximized: bool, - pub is_focused: bool, } fn is_window_cloaked(hwnd: HWND) -> bool { @@ -330,7 +329,6 @@ impl ImplWindow { let rc_client = window_info.rcClient; let is_minimized = IsIconic(hwnd).as_bool(); let is_maximized = IsZoomed(hwnd).as_bool(); - let is_focused = GetForegroundWindow() == hwnd; Ok(ImplWindow { hwnd, @@ -347,11 +345,14 @@ impl ImplWindow { height: (rc_client.bottom - rc_client.top) as u32, is_minimized, is_maximized, - is_focused, }) } } + pub fn is_focused(&self) -> bool { + unsafe { GetForegroundWindow() == self.hwnd } + } + pub fn all() -> XCapResult> { // (HWND, i32) 表示当前窗口以及层级,既(窗口,层级 z),i32 表示 max_z_order,既最大的窗口的 z 顺序 // 窗口当前层级为 max_z_order - z