Skip to content

Commit

Permalink
feat: list window in z order (#173)
Browse files Browse the repository at this point in the history
* feat: window focus

* refactor: window is focused is mouse in it

* fix: fix windows build error

* fix: formatted code

* feat: 修改 image 不作为默认feature

* feat: macos 实现获取窗口是否聚焦

* feat(breaking): List windows in z order (#178)

* feat: 窗口按照z轴顺序返回

* refactor(BREAKING CHANGE): remove window.refresh()

---------

Co-authored-by: nashaofu <nashaofu@users.noreply.github.com>

* chore: fmt

* feat: 修改 macos 窗口 z 值 (#179)

Co-authored-by: nashaofu <nashaofu@users.noreply.github.com>

---------

Co-authored-by: Louis Beaumont <louis.beaumont@gmail.com>
Co-authored-by: nashaofu <nashaofu@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 22, 2024
1 parent ce2fe4f commit 5457eb2
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 294 deletions.
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

0 comments on commit 5457eb2

Please sign in to comment.