mirror of
https://github.com/harivansh-afk/deskctl.git
synced 2026-04-18 03:00:39 +00:00
grouped runtime reads and waits
selector modes
This commit is contained in:
parent
cc8f8e548a
commit
f87ac61790
11 changed files with 1285 additions and 67 deletions
|
|
@ -17,11 +17,30 @@ pub struct BackendWindow {
|
|||
pub minimized: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BackendMonitor {
|
||||
pub name: String,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub width_mm: u32,
|
||||
pub height_mm: u32,
|
||||
pub primary: bool,
|
||||
pub automatic: bool,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub trait DesktopBackend: Send {
|
||||
/// Collect z-ordered windows for read-only queries and targeting.
|
||||
fn list_windows(&mut self) -> Result<Vec<BackendWindow>>;
|
||||
|
||||
/// Get the currently focused window, if one is known.
|
||||
fn active_window(&mut self) -> Result<Option<BackendWindow>>;
|
||||
|
||||
/// Collect monitor geometry and metadata.
|
||||
fn list_monitors(&self) -> Result<Vec<BackendMonitor>>;
|
||||
|
||||
/// Capture the current desktop image without writing it to disk.
|
||||
fn capture_screenshot(&mut self) -> Result<RgbaImage>;
|
||||
|
||||
|
|
@ -69,4 +88,7 @@ pub trait DesktopBackend: Send {
|
|||
|
||||
/// Launch an application.
|
||||
fn launch(&self, command: &str, args: &[String]) -> Result<u32>;
|
||||
|
||||
/// Human-readable backend name for diagnostics and runtime queries.
|
||||
fn backend_name(&self) -> &'static str;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
|||
use enigo::{Axis, Button, Coordinate, Direction, Enigo, Key, Keyboard, Mouse, Settings};
|
||||
use image::RgbaImage;
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::randr::ConnectionExt as RandrConnectionExt;
|
||||
use x11rb::protocol::xproto::{
|
||||
Atom, AtomEnum, ClientMessageData, ClientMessageEvent, ConfigureWindowAux,
|
||||
ConnectionExt as XprotoConnectionExt, EventMask, GetPropertyReply, ImageFormat, ImageOrder,
|
||||
|
|
@ -9,7 +10,7 @@ use x11rb::protocol::xproto::{
|
|||
};
|
||||
use x11rb::rust_connection::RustConnection;
|
||||
|
||||
use crate::backend::BackendWindow;
|
||||
use crate::backend::{BackendMonitor, BackendWindow};
|
||||
|
||||
struct Atoms {
|
||||
client_list_stacking: Atom,
|
||||
|
|
@ -103,6 +104,74 @@ impl X11Backend {
|
|||
Ok(window_infos)
|
||||
}
|
||||
|
||||
fn active_window_info(&self) -> Result<Option<BackendWindow>> {
|
||||
let Some(active_window) = self.active_window()? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let title = self.window_title(active_window).unwrap_or_default();
|
||||
let app_name = self.window_app_name(active_window).unwrap_or_default();
|
||||
if title.is_empty() && app_name.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let (x, y, width, height) = self.window_geometry(active_window)?;
|
||||
let minimized = self.window_is_minimized(active_window).unwrap_or(false);
|
||||
Ok(Some(BackendWindow {
|
||||
native_id: active_window,
|
||||
title,
|
||||
app_name,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
focused: true,
|
||||
minimized,
|
||||
}))
|
||||
}
|
||||
|
||||
fn collect_monitors(&self) -> Result<Vec<BackendMonitor>> {
|
||||
let reply = self
|
||||
.conn
|
||||
.randr_get_monitors(self.root, true)?
|
||||
.reply()
|
||||
.context("Failed to query RANDR monitors")?;
|
||||
|
||||
let mut monitors = Vec::with_capacity(reply.monitors.len());
|
||||
for (index, monitor) in reply.monitors.into_iter().enumerate() {
|
||||
monitors.push(BackendMonitor {
|
||||
name: self
|
||||
.atom_name(monitor.name)
|
||||
.unwrap_or_else(|_| format!("monitor{}", index + 1)),
|
||||
x: i32::from(monitor.x),
|
||||
y: i32::from(monitor.y),
|
||||
width: u32::from(monitor.width),
|
||||
height: u32::from(monitor.height),
|
||||
width_mm: monitor.width_in_millimeters,
|
||||
height_mm: monitor.height_in_millimeters,
|
||||
primary: monitor.primary,
|
||||
automatic: monitor.automatic,
|
||||
});
|
||||
}
|
||||
|
||||
if monitors.is_empty() {
|
||||
let (width, height) = self.root_geometry()?;
|
||||
monitors.push(BackendMonitor {
|
||||
name: "screen".to_string(),
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
width_mm: 0,
|
||||
height_mm: 0,
|
||||
primary: true,
|
||||
automatic: true,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(monitors)
|
||||
}
|
||||
|
||||
fn capture_root_image(&self) -> Result<RgbaImage> {
|
||||
let (width, height) = self.root_geometry()?;
|
||||
let reply = self
|
||||
|
|
@ -224,6 +293,14 @@ impl X11Backend {
|
|||
.reply()
|
||||
.with_context(|| format!("Failed to read property {property} from window {window}"))
|
||||
}
|
||||
|
||||
fn atom_name(&self, atom: Atom) -> Result<String> {
|
||||
self.conn
|
||||
.get_atom_name(atom)?
|
||||
.reply()
|
||||
.map(|reply| String::from_utf8_lossy(&reply.name).to_string())
|
||||
.with_context(|| format!("Failed to read atom name for {atom}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl super::DesktopBackend for X11Backend {
|
||||
|
|
@ -231,6 +308,30 @@ impl super::DesktopBackend for X11Backend {
|
|||
self.collect_window_infos()
|
||||
}
|
||||
|
||||
fn active_window(&mut self) -> Result<Option<BackendWindow>> {
|
||||
self.active_window_info()
|
||||
}
|
||||
|
||||
fn list_monitors(&self) -> Result<Vec<BackendMonitor>> {
|
||||
match self.collect_monitors() {
|
||||
Ok(monitors) => Ok(monitors),
|
||||
Err(_) => {
|
||||
let (width, height) = self.root_geometry()?;
|
||||
Ok(vec![BackendMonitor {
|
||||
name: "screen".to_string(),
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
width_mm: 0,
|
||||
height_mm: 0,
|
||||
primary: true,
|
||||
automatic: true,
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_screenshot(&mut self) -> Result<RgbaImage> {
|
||||
self.capture_root_image()
|
||||
}
|
||||
|
|
@ -452,6 +553,10 @@ impl super::DesktopBackend for X11Backend {
|
|||
.with_context(|| format!("Failed to launch: {command}"))?;
|
||||
Ok(child.id())
|
||||
}
|
||||
|
||||
fn backend_name(&self) -> &'static str {
|
||||
"x11"
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_key(name: &str) -> Result<Key> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue