xremap 0.15.5

Dynamic key remap for X and Wayland
Documentation
use crate::client::{Client, WindowInfo};
use anyhow::{bail, Result};
use std::collections::HashMap;
use zbus::zvariant::Value;
use zbus::{block_on, Connection};

#[derive(Debug)]
struct PantheonWindow {
    id: String,
    app_class: Option<String>,
    title: Option<String>,
    focused: bool,
}

pub struct PantheonClient {
    connection: Option<Connection>,
}

impl PantheonClient {
    pub fn new() -> Self {
        Self { connection: None }
    }

    fn connect(&mut self) -> Result<&mut Connection> {
        if self.connection.is_none() {
            self.connection = Some(block_on(Connection::session())?);
        }

        Ok(self
            .connection
            .as_mut()
            .ok_or_else(|| anyhow::format_err!("This cannot happen"))?)
    }

    fn get_focused_window(&mut self) -> Result<Option<PantheonWindow>> {
        Ok(self.get_windows()?.into_iter().find(|window| window.focused))
    }

    fn get_windows(&mut self) -> Result<Vec<PantheonWindow>> {
        let body = block_on(self.connect()?.call_method(
            Some("org.pantheon.gala"),
            "/org/pantheon/gala/DesktopInterface",
            Some("org.pantheon.gala.DesktopIntegration"),
            "GetWindows",
            &(),
        ))?
        .body();

        Ok(body
            .deserialize::<Vec<(u64, HashMap<String, Value>)>>()?
            .into_iter()
            .map(|(id, dict)| PantheonWindow {
                id: format!("{id}"),
                title: dict.get("title").map(|v| String::try_from(v).unwrap_or_default()),
                app_class: dict.get("wm-class").map(|v| String::try_from(v).unwrap_or_default()),
                focused: dict
                    .get("has-focus")
                    .map(|v| bool::try_from(v))
                    .unwrap_or(Ok(false))
                    .unwrap_or_default(),
            })
            .collect())
    }
}

impl Client for PantheonClient {
    fn supported(&mut self) -> bool {
        self.get_windows().is_ok()
    }

    fn current_window(&mut self) -> Option<String> {
        match self.get_focused_window() {
            Ok(window) => window.and_then(|window| window.title),
            Err(e) => {
                eprintln!("Error when fetching window title: {e:?}");
                None
            }
        }
    }

    fn current_application(&mut self) -> Option<String> {
        match self.get_focused_window() {
            Ok(window) => window.and_then(|window| window.app_class),
            Err(e) => {
                eprintln!("Error when fetching app_class: {e:?}");
                None
            }
        }
    }

    fn window_list(&mut self) -> Result<Vec<WindowInfo>> {
        Ok(self
            .get_windows()?
            .into_iter()
            .map(|window| WindowInfo {
                winid: Some(window.id),
                app_class: window.app_class,
                title: window.title,
            })
            .collect())
    }

    fn close_windows_by_app_class(&mut self, _app_class: &str) -> Result<()> {
        bail!("close_windows_by_app_class not implemented for Pantheon")
    }
}