1use std::io;
7use std::process::Command;
8
9#[derive(Clone)]
11pub struct Pane {
12 pub id: String,
13 pub index: String,
14 pub left: u16,
15 pub top: u16,
16 pub width: u16,
17 pub height: u16,
18 pub active: bool,
19 pub cmd: String,
20}
21
22#[derive(Clone)]
24pub struct Win {
25 pub id: String,
26 pub index: String,
27 pub name: String,
28 pub active: bool,
29 pub w: u16,
30 pub h: u16,
31 pub panes: Vec<Pane>,
32}
33
34fn out(args: &[&str]) -> io::Result<String> {
35 let o = Command::new("tmux").args(args).output()?;
36 Ok(String::from_utf8_lossy(&o.stdout).to_string())
37}
38
39fn fire(args: &[&str]) -> io::Result<()> {
40 Command::new("tmux").args(args).status()?;
41 Ok(())
42}
43
44fn capture(args: &[&str]) -> io::Result<Result<String, String>> {
47 let o = Command::new("tmux").args(args).output()?;
48 Ok(if o.status.success() {
49 Ok(String::from_utf8_lossy(&o.stdout).trim().to_string())
50 } else {
51 Err(String::from_utf8_lossy(&o.stderr).trim().to_string())
52 })
53}
54
55fn parse_pane(f: &[&str]) -> Pane {
56 Pane {
57 id: f[0].to_string(),
58 index: f[1].to_string(),
59 left: f[2].parse().unwrap_or(0),
60 top: f[3].parse().unwrap_or(0),
61 width: f[4].parse().unwrap_or(1),
62 height: f[5].parse().unwrap_or(1),
63 active: f[6].trim() == "1",
64 cmd: f[7].to_string(),
65 }
66}
67
68const PANE_FMT: &str = "#{pane_id}\t#{pane_index}\t#{pane_left}\t#{pane_top}\t#{pane_width}\t#{pane_height}\t#{pane_active}\t#{pane_current_command}";
69
70pub fn query() -> io::Result<(u16, u16, Vec<Pane>)> {
72 let dims = out(&["display-message", "-p", "#{window_width} #{window_height}"])?;
73 let mut it = dims.split_whitespace();
74 let win_w = it.next().and_then(|s| s.parse().ok()).unwrap_or(80u16);
75 let win_h = it.next().and_then(|s| s.parse().ok()).unwrap_or(24u16);
76 let raw = out(&["list-panes", "-F", PANE_FMT])?;
77 let mut panes = Vec::new();
78 for line in raw.lines() {
79 let f: Vec<&str> = line.split('\t').collect();
80 if f.len() >= 8 {
81 panes.push(parse_pane(&f));
82 }
83 }
84 Ok((win_w, win_h, panes))
85}
86
87pub fn query_windows() -> io::Result<Vec<Win>> {
89 let fmt = "#{window_id}\t#{window_index}\t#{window_name}\t#{window_active}\t#{window_width}\t#{window_height}\t#{pane_id}\t#{pane_index}\t#{pane_left}\t#{pane_top}\t#{pane_width}\t#{pane_height}\t#{pane_active}\t#{pane_current_command}";
90 let raw = out(&["list-panes", "-s", "-F", fmt])?;
91 let mut wins: Vec<Win> = Vec::new();
92 for line in raw.lines() {
93 let f: Vec<&str> = line.split('\t').collect();
94 if f.len() < 14 {
95 continue;
96 }
97 let pane = parse_pane(&f[6..14]);
98 let wid = f[0].to_string();
99 if let Some(w) = wins.iter_mut().find(|w| w.id == wid) {
100 w.panes.push(pane);
101 } else {
102 wins.push(Win {
103 id: wid,
104 index: f[1].to_string(),
105 name: f[2].to_string(),
106 active: f[3].trim() == "1",
107 w: f[4].parse().unwrap_or(80),
108 h: f[5].parse().unwrap_or(24),
109 panes: vec![pane],
110 });
111 }
112 }
113 wins.sort_by_key(|w| w.index.parse::<i64>().unwrap_or(0));
114 Ok(wins)
115}
116
117pub fn split(target: &str, flag: &str, before: bool) -> io::Result<Result<String, String>> {
120 let mut args = vec!["split-window", flag];
121 if before {
122 args.push("-b");
123 }
124 args.extend(["-t", target, "-P", "-F", "#{pane_id}"]);
125 capture(&args)
126}
127
128pub fn select_layout(name: &str) -> io::Result<Result<String, String>> {
129 capture(&["select-layout", name])
130}
131
132pub fn join_pane(pane: &str, win: &str) -> io::Result<Result<String, String>> {
133 capture(&["join-pane", "-s", pane, "-t", win])
134}
135
136pub fn swap_pane(s: &str, t: &str) -> io::Result<()> {
137 fire(&["swap-pane", "-s", s, "-t", t])
138}
139
140pub fn kill_pane(id: &str) -> io::Result<()> {
141 fire(&["kill-pane", "-t", id])
142}
143
144pub fn kill_window(id: &str) -> io::Result<()> {
145 fire(&["kill-window", "-t", id])
146}
147
148pub fn next_layout() -> io::Result<()> {
149 fire(&["next-layout"])
150}
151
152pub fn select_window(id: &str) -> io::Result<()> {
153 fire(&["select-window", "-t", id])
154}
155
156pub fn swap_window(a: &str, b: &str) -> io::Result<()> {
157 fire(&["swap-window", "-d", "-s", a, "-t", b])
158}
159
160pub fn rename_window(id: &str, name: &str) -> io::Result<()> {
161 fire(&["rename-window", "-t", id, name])
162}
163
164pub fn new_window() -> io::Result<()> {
165 fire(&["new-window", "-a"])
166}
167
168pub fn select_pane(id: &str) -> io::Result<()> {
170 fire(&["select-pane", "-t", id])
171}
172
173pub fn select_pane_dir(dir: &str) -> io::Result<()> {
175 fire(&["select-pane", dir])
176}