1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/// A wrapper for the `wmctrl` command line interface.
///
/// # Examples
///
/// Move firefox to desktop 2 and make it fullscreen
/// ```
/// use wmctrl::{self, utils, State, Action, Property};
///
/// let mut windows = wmctrl::get_windows();
/// let mut firefox = utils::find_window_by_title_mut(&mut windows, "firefox").unwrap();
///
/// firefox.set_desktop("1");
/// firefox.change_state(State::new(Action::Add, Property::Fullscreen));
/// # Ok::<(), std::io::Error>(())
/// ```
use std::process::Output;

pub mod desktop;
pub mod utils;

mod state;
mod transformation;
mod window;

pub use state::Action;
pub use state::Property;
pub use state::State;
pub use transformation::Transformation;
pub use window::Window;

use utils::wmctrl;

/// Print help
///
/// This function is the equivalent of `wmctrl -h`.
/// # Examples
///
/// ```
/// println!("{}", String::from_utf8(wmctrl::help().stdout)?);
/// # Ok::<(), std::string::FromUtf8Error>(())
/// ```
pub fn help() -> Output {
    wmctrl("-h")
}

/// Get windows managed by the window manager
///
/// This function is the equivalent of `wmctrl -l -G`.
///
/// You should never fetch a second `Vec<Window>` while the first one is still in use!
/// This will lead to inconsistencies between the two Vectors and most likely to incorrect behavior of your code.
/// It is advised to get a `Vec<Window>` once at the beginning of the program and operate on it.
pub fn get_windows() -> Vec<Window> {
    let output_table = String::from_utf8(wmctrl("-l -G").stdout).unwrap();

    let mut windows = Vec::new();
    for row in output_table.lines() {
        windows.push(parse_row(row))
    }

    windows
}

/// Show information about the window manager and about the environment
///
/// This function is the equivalent of `wmctrl -m`.
///
/// # Examples
///
/// ```
/// println!("{}", String::from_utf8(wmctrl::show_wm_information().stdout)?);
/// # Ok::<(), std::string::FromUtf8Error>(())
/// ```
pub fn show_wm_information() -> Output {
    wmctrl("-m")
}

fn parse_row(row: &str) -> Window {
    let columns = row
        .split(' ')
        .filter(|e| !e.is_empty())
        .collect::<Vec<&str>>();

    let (x, y, w, h) = (
        columns[2].parse::<u16>().unwrap(),
        columns[3].parse::<u16>().unwrap(),
        columns[4].parse::<u16>().unwrap(),
        columns[5].parse::<u16>().unwrap(),
    );

    let t = Transformation::new(x, y, w, h);

    let (id, desktop, client_machine) = (
        columns[0].to_owned(),
        columns[1].to_owned(),
        columns[6].to_owned(),
    );

    let mut title = String::from("");
    let title_substrings: Vec<&str> = columns[7..].to_vec();

    title_substrings
        .into_iter()
        .for_each(|e| title += format!("{} ", e).as_str());
    // Remove last whitespace
    title.pop();

    Window::new(id, desktop, client_machine, title, t)
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}