use anyhow::{Context, Result, bail};
use x11rb::connection::Connection;
use x11rb::protocol::xproto::{Atom, AtomEnum, ConnectionExt, Window};
pub fn focused_window_pid() -> Result<u32> {
let (conn, screen_num) = x11rb::connect(None)
.context("connecting to the X server (no DISPLAY? on Wayland use --pid)")?;
let root = conn.setup().roots[screen_num].root;
let active_atom = intern(&conn, b"_NET_ACTIVE_WINDOW")?;
let pid_atom = intern(&conn, b"_NET_WM_PID")?;
let active: Window = single_u32(&conn, root, active_atom, AtomEnum::WINDOW.into())?
.filter(|&w| w != 0)
.context("no active window (_NET_ACTIVE_WINDOW unset or zero)")?;
match single_u32(&conn, active, pid_atom, AtomEnum::CARDINAL.into())? {
Some(pid) => Ok(pid),
None => bail!(
"focused window {active:#x} advertises no _NET_WM_PID; its client did \
not set one \u{2014} pass --pid <shell PID> instead"
),
}
}
fn intern(conn: &impl Connection, name: &[u8]) -> Result<Atom> {
let show = || String::from_utf8_lossy(name).into_owned();
Ok(conn
.intern_atom(false, name)
.with_context(|| format!("requesting atom {}", show()))?
.reply()
.with_context(|| format!("interning atom {}", show()))?
.atom)
}
fn single_u32(
conn: &impl Connection,
window: Window,
property: Atom,
type_: Atom,
) -> Result<Option<u32>> {
Ok(conn
.get_property(false, window, property, type_, 0, 1)
.context("requesting property")?
.reply()
.context("reading property")?
.value32()
.and_then(|mut values| values.next()))
}