Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 支持获取窗口是否 focused #184

Merged
merged 6 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xcap"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (WIP)."
license = "Apache-2.0"
Expand All @@ -23,6 +23,11 @@ thiserror = "2.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.10"
core-graphics = "0.24"
objc2-app-kit = { version = "0.2.2", features = [
"libc",
"NSWorkspace",
"NSRunningApplication",
] }

[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.58", features = [
Expand Down
2 changes: 1 addition & 1 deletion examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
window.current_monitor().name(),
(window.x(), window.y(), window.z()),
(window.width(), window.height()),
(window.is_minimized(), window.is_maximized())
(window.is_minimized(), window.is_maximized(), window.is_focused())
);
}
}
27 changes: 25 additions & 2 deletions src/linux/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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<Atom> {
Expand All @@ -38,7 +39,7 @@ fn get_atom(conn: &Connection, name: &str) -> XCapResult<Atom> {
let atom_reply = conn.wait_for_reply(atom_cookie)?;
let atom = atom_reply.atom();

if atom == ATOM_NONE {
if atom.is_none() {
return Err(XCapError::new(format!("{} not supported", name)));
}

Expand Down Expand Up @@ -79,12 +80,29 @@ pub fn get_window_pid(conn: &Connection, window: &Window) -> XCapResult<u32> {
.copied()
}

fn get_active_window_id(conn: &Connection) -> Option<u32> {
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()?;
if let Some(&active_window_id) = active_window_id.value::<u32>().first() {
return Some(active_window_id);
}
}

None
}

impl ImplWindow {
fn new(
conn: &Connection,
window: &Window,
pid: u32,
z: i32,
is_focused: bool,
impl_monitors: &Vec<ImplMonitor>,
) -> XCapResult<ImplWindow> {
let title = {
Expand Down Expand Up @@ -197,6 +215,7 @@ impl ImplWindow {
height,
is_minimized,
is_maximized,
is_focused,
})
}

Expand All @@ -208,6 +227,7 @@ 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()?;
Expand Down Expand Up @@ -247,7 +267,10 @@ impl ImplWindow {
}
};

if let Ok(impl_window) = ImplWindow::new(&conn, client, pid, z, &impl_monitors)
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)
{
impl_windows.push(impl_window);
} else {
Expand Down
16 changes: 14 additions & 2 deletions src/macos/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use core_graphics::{
window::{kCGNullWindowID, kCGWindowSharingNone},
};
use image::RgbaImage;
use objc2_app_kit::NSWorkspace;

use crate::{error::XCapResult, XCapError};

Expand All @@ -35,6 +36,7 @@ pub(crate) struct ImplWindow {
pub height: u32,
pub is_minimized: bool,
pub is_maximized: bool,
pub is_focused: bool,
}

unsafe impl Send for ImplWindow {}
Expand Down Expand Up @@ -129,9 +131,10 @@ impl ImplWindow {
window_name: String,
window_owner_name: String,
z: i32,
focused_app_pid: Option<i32>,
) -> XCapResult<ImplWindow> {
let id = get_cf_number_i32_value(window_cf_dictionary_ref, "kCGWindowNumber")? as u32;
let pid = get_cf_number_i32_value(window_cf_dictionary_ref, "kCGWindowOwnerPID")? as u32;
let pid = get_cf_number_i32_value(window_cf_dictionary_ref, "kCGWindowOwnerPID")?;

let cg_rect = get_window_cg_rect(window_cf_dictionary_ref)?;

Expand Down Expand Up @@ -164,11 +167,13 @@ impl ImplWindow {
let is_minimized =
!get_cf_bool_value(window_cf_dictionary_ref, "kCGWindowIsOnscreen")? && !is_maximized;

let is_focused = focused_app_pid.eq(&Some(pid));

Ok(ImplWindow {
id,
title: window_name,
app_name: window_owner_name,
pid,
pid: pid as u32,
current_monitor: current_monitor.clone(),
x: cg_rect.origin.x as i32,
y: cg_rect.origin.y as i32,
Expand All @@ -177,12 +182,18 @@ impl ImplWindow {
height: cg_rect.size.height as u32,
is_minimized,
is_maximized,
is_focused,
})
}

pub fn all() -> XCapResult<Vec<ImplWindow>> {
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();

// CGWindowListCopyWindowInfo 返回窗口顺序为从顶层到最底层
Expand Down Expand Up @@ -240,6 +251,7 @@ 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 {
Expand Down
4 changes: 4 additions & 0 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl Window {
pub fn is_maximized(&self) -> bool {
self.impl_window.is_maximized
}
/// The window is focused.
pub fn is_focused(&self) -> bool {
self.impl_window.is_focused
}
}

impl Window {
Expand Down
4 changes: 2 additions & 2 deletions src/windows/capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use windows::Win32::{
OBJ_BITMAP, SRCCOPY,
},
},
Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS},
UI::WindowsAndMessaging::{GetDesktopWindow, WINDOWINFO},
Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS, PW_CLIENTONLY},
UI::WindowsAndMessaging::{GetDesktopWindow, PW_RENDERFULLCONTENT, WINDOWINFO},
};

use crate::error::{XCapError, XCapResult};
Expand Down
9 changes: 6 additions & 3 deletions src/windows/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ use windows::{
},
},
UI::WindowsAndMessaging::{
EnumWindows, GetClassNameW, GetWindowInfo, GetWindowLongPtrW, GetWindowTextLengthW,
GetWindowTextW, GetWindowThreadProcessId, IsIconic, IsWindow, IsWindowVisible,
IsZoomed, GWL_EXSTYLE, WINDOWINFO, WINDOW_EX_STYLE, WS_EX_TOOLWINDOW,
EnumWindows, GetClassNameW, GetForegroundWindow, GetWindowInfo, GetWindowLongPtrW,
GetWindowTextLengthW, GetWindowTextW, GetWindowThreadProcessId, IsIconic, IsWindow,
IsWindowVisible, IsZoomed, GWL_EXSTYLE, WINDOWINFO, WINDOW_EX_STYLE, WS_EX_TOOLWINDOW,
},
},
};
Expand Down Expand Up @@ -52,6 +52,7 @@ 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 {
Expand Down Expand Up @@ -326,6 +327,7 @@ 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,
Expand All @@ -342,6 +344,7 @@ impl ImplWindow {
height: (rc_client.bottom - rc_client.top) as u32,
is_minimized,
is_maximized,
is_focused,
})
}
}
Expand Down
Loading