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: list window in z order #173

Merged
merged 9 commits into from
Dec 22, 2024
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ windows = { version = "0.58", features = [
] }

[target.'cfg(target_os="linux")'.dependencies]
dbus = "0.9"
percent-encoding = "2.3"
xcb = { version = "1.5", features = ["randr"] }
dbus = { version = "0.9" }

[dev-dependencies]
fs_extra = "1.3"
63 changes: 23 additions & 40 deletions examples/monitor_record.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,30 @@
use fs_extra::dir;
use std::{
thread,
time::{Duration, Instant},
};
use std::{sync::Arc, thread, time::Duration};
use xcap::Monitor;

fn main() {
let monitors = Monitor::all().unwrap();
let monitor = Monitor::from_point(100, 100).unwrap();

dir::create_all("target/monitors", true).unwrap();
let video_recorder = Arc::new(monitor.video_recorder().unwrap());

let monitor = monitors.get(0).unwrap().clone();

let mut i = 0;
let frame = 20;
let start = Instant::now();
let fps = 1000 / frame;

loop {
i += 1;
let time = Instant::now();
let image = monitor.capture_image().unwrap();
image
.save(format!("target/monitors/monitor-{}.png", i,))
let video_recorder_clone = video_recorder.clone();
thread::spawn(move || {
video_recorder_clone
.on_frame(|frame| {
println!("frame: {:?}", frame.width);
Ok(())
})
.unwrap();
let sleep_time = fps * i - start.elapsed().as_millis() as i128;
println!(
"sleep_time: {:?} current_step_time: {:?}",
sleep_time,
time.elapsed()
);
if sleep_time > 0 {
thread::sleep(Duration::from_millis(sleep_time as u64));
}

if i >= 900 {
break;
}
}

println!("time {:?}", start.elapsed());
let actual_fps = 900 / start.elapsed().as_secs();
println!("actual fps: {}", actual_fps);

// ffmpeg -framerate {actual_fps} -i monitor-%d.png -c:v libx264 -pix_fmt yuv420p output.mp4
});

println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
thread::sleep(Duration::from_secs(2));
println!("start");
video_recorder.start().unwrap();
thread::sleep(Duration::from_secs(2));
println!("stop");
video_recorder.stop().unwrap();
}
15 changes: 7 additions & 8 deletions examples/window.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
use std::time::Instant;
use std::thread;
use xcap::Window;

fn main() {
let start = Instant::now();
thread::sleep(std::time::Duration::from_secs(3));

let windows = Window::all().unwrap();
println!("Window::all() 运行耗时: {:?}", start.elapsed());

for window in windows {
for window in windows.clone() {
println!(
"Window:\n id: {}\n title: {}\n app_name: {}\n monitor: {:?}\n position: {:?}\n size {:?}\n state {:?}\n",
"Window:\n id: {}\n title: {}\n app_name: {}\n pid: {}\n monitor: {:?}\n position: {:?}\n size {:?}\n state {:?}\n",
window.id(),
window.title(),
window.app_name(),
window.pid(),
window.current_monitor().name(),
(window.x(), window.y()),
(window.x(), window.y(), window.z()),
(window.width(), window.height()),
(window.is_minimized(), window.is_maximized())
);
}

println!("运行耗时: {:?}", start.elapsed());
}
70 changes: 0 additions & 70 deletions examples/window_record.rs

This file was deleted.

30 changes: 0 additions & 30 deletions examples/windows_monitor_record.rs

This file was deleted.

62 changes: 38 additions & 24 deletions src/linux/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::str;
use xcb::{
x::{
Atom, Drawable, GetGeometry, GetProperty, GetPropertyReply, InternAtom, QueryPointer,
TranslateCoordinates, Window, ATOM_ATOM, ATOM_NONE, ATOM_STRING, ATOM_WM_CLASS,
ATOM_WM_NAME,
TranslateCoordinates, Window, ATOM_ATOM, ATOM_CARDINAL, ATOM_NONE, ATOM_STRING,
ATOM_WM_CLASS, ATOM_WM_NAME,
},
Connection, Xid,
};
Expand All @@ -19,9 +19,11 @@ pub(crate) struct ImplWindow {
pub id: u32,
pub title: String,
pub app_name: String,
pub pid: u32,
pub current_monitor: ImplMonitor,
pub x: i32,
pub y: i32,
pub z: i32,
pub width: u32,
pub height: u32,
pub is_minimized: bool,
Expand Down Expand Up @@ -65,10 +67,24 @@ fn get_window_property(
Ok(window_property_reply)
}

pub fn get_window_pid(conn: &Connection, window: &Window) -> XCapResult<u32> {
let wm_pid_atom = get_atom(conn, "_NET_WM_PID")?;

let reply = get_window_property(conn, *window, wm_pid_atom, ATOM_CARDINAL, 0, 4)?;
let value = reply.value::<u32>();

value
.first()
.ok_or(XCapError::new("Get window pid failed"))
.copied()
}

impl ImplWindow {
fn new(
conn: &Connection,
window: &Window,
pid: u32,
z: i32,
impl_monitors: &Vec<ImplMonitor>,
) -> XCapResult<ImplWindow> {
let title = {
Expand Down Expand Up @@ -172,9 +188,11 @@ impl ImplWindow {
id: window.resource_id(),
title,
app_name,
pid,
current_monitor,
x,
y,
z,
width,
height,
is_minimized,
Expand All @@ -187,11 +205,14 @@ impl ImplWindow {
let setup = conn.get_setup();

// https://github.com/rust-x-bindings/rust-xcb/blob/main/examples/get_all_windows.rs
let client_list_atom = get_atom(&conn, "_NET_CLIENT_LIST")?;
// 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 mut impl_windows = Vec::new();
let impl_monitors = ImplMonitor::all()?;

let mut z = -1;
for screen in setup.roots() {
let root_window = screen.root();

Expand All @@ -210,14 +231,24 @@ impl ImplWindow {
client_list_atom,
ATOM_NONE,
0,
100,
1024,
) {
Ok(list_window_reply) => list_window_reply,
_ => continue,
};

for client in list_window_reply.value::<Window>() {
if let Ok(impl_window) = ImplWindow::new(&conn, client, &impl_monitors) {
z += 1;
let pid = match get_window_pid(&conn, client) {
Ok(pid) => pid,
err => {
log::error!("{:?}", err);
continue;
}
};

if let Ok(impl_window) = ImplWindow::new(&conn, client, pid, z, &impl_monitors)
{
impl_windows.push(impl_window);
} else {
log::error!(
Expand All @@ -230,30 +261,13 @@ impl ImplWindow {
}
}

impl_windows.reverse();

Ok(impl_windows)
}
}

impl ImplWindow {
pub fn refresh(&mut self) -> XCapResult<()> {
let (conn, _) = Connection::connect(None)?;
let impl_monitors = ImplMonitor::all()?;
let impl_window = ImplWindow::new(&conn, &self.window, &impl_monitors)?;

self.window = impl_window.window;
self.id = impl_window.id;
self.title = impl_window.title;
self.app_name = impl_window.app_name;
self.current_monitor = impl_window.current_monitor;
self.x = impl_window.x;
self.y = impl_window.y;
self.width = impl_window.width;
self.height = impl_window.height;
self.is_minimized = impl_window.is_minimized;
self.is_maximized = impl_window.is_maximized;

Ok(())
}
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
capture_window(self)
}
Expand Down
Loading
Loading