focus_monitor 0.1.4

Library for iterating over the focused window each time it changes
Documentation
use crate::{get_active_window, get_window, Window};

use anyhow::{bail, Context, Result};
use xcb::{x, Connection, Event};

pub struct FocusMonitor {
    conn: Connection,
    root_window: x::Window,
    atom_active_window: x::Atom,
    last_window: Option<x::Window>,
}

impl FocusMonitor {
    pub fn try_new() -> Result<Self> {
        let (conn, screen_num) = Connection::connect(None)?;
        let setup = conn.get_setup();

        let atom_active_window = conn.send_request(&x::InternAtom {
            only_if_exists: true,
            name: "_NET_ACTIVE_WINDOW".as_bytes(),
        });
        let atom_active_window = conn.wait_for_reply(atom_active_window)?.atom();
        if atom_active_window == x::ATOM_NONE {
            bail!("Not supported");
        }

        let screen = setup
            .roots()
            .nth(screen_num as usize)
            .context("screen was None")?;
        let root_window = screen.root();

        conn.send_request(&x::ChangeWindowAttributes {
            window: root_window,
            value_list: &[x::Cw::EventMask(x::EventMask::PROPERTY_CHANGE)],
        });

        conn.flush()?;
        Ok(Self {
            conn,
            root_window,
            atom_active_window,
            last_window: None,
        })
    }

    fn wait_for_window_change(&mut self) -> Result<x::Window> {
        let event = self.conn.wait_for_event()?;
        if let Event::X(x::Event::PropertyNotify(ev)) = event {
            if ev.atom() == self.atom_active_window {
                let window =
                    get_active_window(&self.conn, self.root_window, self.atom_active_window)?;

                if let Some(last_window) = self.last_window {
                    if window == last_window {
                        return self.wait_for_window_change();
                    }
                }

                self.last_window = Some(window);
                return Ok(window);
            }
        }
        self.wait_for_window_change()
    }

    fn get_next_window(&mut self) -> Result<Option<Window>> {
        let window = self.wait_for_window_change()?;
        if window == x::WINDOW_NONE {
            return Ok(None);
        }
        let window = get_window(&self.conn, window)?;
        Ok(Some(window))
    }
}

impl Iterator for FocusMonitor {
    type Item = Result<Option<Window>>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.get_next_window())
    }
}