|
@@ -0,0 +1,138 @@
|
|
|
+use std::io::prelude::*;
|
|
|
+use std::io::BufReader;
|
|
|
+use std::process::{Command, Stdio};
|
|
|
+use std::sync::mpsc;
|
|
|
+use std::thread;
|
|
|
+use ui::widget;
|
|
|
+
|
|
|
+const MARGIN: u16 = 7;
|
|
|
+
|
|
|
+struct Desktop {
|
|
|
+ name: String,
|
|
|
+ selected: bool,
|
|
|
+ urgent: bool
|
|
|
+}
|
|
|
+
|
|
|
+pub struct Bspwm {
|
|
|
+ context: widget::DrawContext,
|
|
|
+ tx: mpsc::Sender<widget::WidgetMessage>,
|
|
|
+ desktops: Vec<Desktop>
|
|
|
+}
|
|
|
+
|
|
|
+impl Bspwm {
|
|
|
+ pub fn new(tx: mpsc::Sender<widget::WidgetMessage>, context: widget::DrawContext) -> Bspwm {
|
|
|
+ Bspwm {
|
|
|
+ context: context,
|
|
|
+ tx: tx,
|
|
|
+ desktops: vec![]
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl widget::Widget for Bspwm {
|
|
|
+ fn init(&mut self) {
|
|
|
+ let tx = self.tx.clone();
|
|
|
+ thread::spawn(move || monitor_thread(tx));
|
|
|
+ }
|
|
|
+
|
|
|
+ fn render(&mut self, x: u16) {
|
|
|
+ let mut offset = x;
|
|
|
+ for desktop in self.desktops.iter() {
|
|
|
+ let text_width = self.context.measure_text(&desktop.name);
|
|
|
+
|
|
|
+ if desktop.selected {
|
|
|
+ self.context.set_bg_color(0x6666, 0xCCCC, 0x6666, 0xFFFF);
|
|
|
+ }
|
|
|
+ else if desktop.urgent {
|
|
|
+ self.context.set_bg_color(0xCCCC, 0x0, 0x3333, 0xFFFF);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.context.set_bg_color(0x0, 0x0, 0x0, 0xFFFF);
|
|
|
+ }
|
|
|
+ self.context.draw_bg(offset, text_width + MARGIN * 2);
|
|
|
+ self.context.draw_text(&desktop.name, offset + MARGIN);
|
|
|
+
|
|
|
+ offset += text_width + MARGIN * 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn width(&mut self) -> u16 {
|
|
|
+ let mut sum = 0;
|
|
|
+ for desktop in self.desktops.iter() {
|
|
|
+ sum += self.context.measure_text(&desktop.name) + MARGIN * 2;
|
|
|
+ }
|
|
|
+ sum
|
|
|
+ }
|
|
|
+
|
|
|
+ fn handle_event(&mut self, event: &widget::WidgetMessage) -> bool {
|
|
|
+ match event {
|
|
|
+ &widget::WidgetMessage::BspwmEvent(ref line) => {
|
|
|
+ self.desktops = parse_bspwm(line);
|
|
|
+ self.tx.send(widget::WidgetMessage::Relayout).expect("Failed to send relayout");
|
|
|
+ },
|
|
|
+ _ => {}
|
|
|
+ }
|
|
|
+ false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn monitor_thread(tx: mpsc::Sender<widget::WidgetMessage>) {
|
|
|
+ let bspc = Command::new("bspc")
|
|
|
+ .arg("subscribe")
|
|
|
+ .arg("report")
|
|
|
+ .stdout(Stdio::piped())
|
|
|
+ .spawn()
|
|
|
+ .ok()
|
|
|
+ .expect("Failed to start bspc subscribe");
|
|
|
+
|
|
|
+ let stdout = bspc.stdout.unwrap();
|
|
|
+ let mut reader = BufReader::new(stdout);
|
|
|
+
|
|
|
+ let mut line = String::new();
|
|
|
+ loop {
|
|
|
+ line.clear();
|
|
|
+ reader.read_line(&mut line).ok().expect("Failed to read line");
|
|
|
+ let kind = line.remove(0);
|
|
|
+ let line = line.trim();
|
|
|
+
|
|
|
+ match kind {
|
|
|
+ 'W' => {
|
|
|
+ let event = widget::WidgetMessage::BspwmEvent(line.to_string());
|
|
|
+ tx.send(event).expect("Failed to send bswpm event")
|
|
|
+ },
|
|
|
+ _ => {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_bspwm(line: &str) -> Vec<Desktop> {
|
|
|
+ let mut desktops = vec![];
|
|
|
+ let elems = line.split(':');
|
|
|
+
|
|
|
+ for elem in elems {
|
|
|
+ let mut chars = elem.chars();
|
|
|
+ let kind = chars.next().unwrap();
|
|
|
+ let name = chars.collect::<String>();
|
|
|
+
|
|
|
+ if kind == 'M' || kind == 'm' {}
|
|
|
+ else if kind == 'L' {}
|
|
|
+ else if kind == 'G' {}
|
|
|
+ else if kind == 'T' {}
|
|
|
+ else {
|
|
|
+ let empty = kind == 'f';
|
|
|
+ let urgent = kind == 'U' || kind == 'u';
|
|
|
+ let selected = kind.is_uppercase();
|
|
|
+
|
|
|
+ if !empty {
|
|
|
+ desktops.push(Desktop {
|
|
|
+ name: name,
|
|
|
+ selected: selected,
|
|
|
+ urgent: urgent
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ desktops
|
|
|
+}
|
|
|
+
|