瀏覽代碼

Replace external title fetching

Thomas Dy 9 年之前
父節點
當前提交
525232fb45
共有 5 個文件被更改,包括 114 次插入1 次删除
  1. 1 0
      Cargo.toml
  2. 0 1
      src/external.rs
  3. 2 0
      src/main.rs
  4. 66 0
      src/wm/ewmh.rs
  5. 45 0
      src/wm/mod.rs

+ 1 - 0
Cargo.toml

@@ -7,3 +7,4 @@ authors = ["Thomas Dy <thatsmydoing@gmail.com>"]
 time = "0.1"
 toml = "0.1"
 mpd = "0.0.11"
+xcb = "0.7.0"

+ 0 - 1
src/external.rs

@@ -12,7 +12,6 @@ pub fn external(tx: &Channel, _cfg: &Config) {
         let line = line.trim();
 
         match kind {
-            'T' => comm::send(tx, "title", line),
             'M' => comm::send(tx, "cmus", &format!("%{{A:cmus:}}{}%{{A}}", line)),
             'W' => comm::send(tx, "desktops", &parse_bspwm(line)),
             _ => ()

+ 2 - 0
src/main.rs

@@ -5,6 +5,7 @@ mod external;
 mod bar;
 mod store;
 mod music;
+mod wm;
 
 use comm::{Channel, Message};
 use config::Config;
@@ -25,6 +26,7 @@ fn main() {
     make_thread(&tx, &cfg, external::external);
     make_thread(&tx, &cfg, sensors::sensors);
     make_thread(&tx, &cfg, music::music);
+    make_thread(&tx, &cfg, wm::wm);
 
     let mut data = store::Store::new();
 

+ 66 - 0
src/wm/ewmh.rs

@@ -0,0 +1,66 @@
+extern crate xcb;
+
+use std::mem;
+
+const ATOMS: [&'static str; 3] = [
+    "_NET_ACTIVE_WINDOW",
+    "_NET_WM_NAME",
+    "UTF8_STRING"
+];
+
+#[allow(non_snake_case)]
+pub struct EWMH<'a> {
+    pub conn: &'a xcb::Connection,
+    pub root: xcb::Window,
+    pub _NET_ACTIVE_WINDOW: xcb::Atom,
+    pub _NET_WM_NAME: xcb::Atom,
+    pub UTF8_STRING: xcb::Atom
+}
+
+pub fn connect<'a>(conn: &'a xcb::Connection, screen_num: i32) -> EWMH {
+    let setup = conn.get_setup();
+    let screen = setup.roots().nth(screen_num as usize).unwrap();
+    let atoms: Vec<xcb::Atom> = ATOMS
+        .iter()
+        .map(|name| xcb::intern_atom(&conn, false, name))
+        .map(|cookie| cookie.get_reply().unwrap().atom())
+        .collect();
+
+    EWMH {
+        conn: conn,
+        root: screen.root(),
+        _NET_ACTIVE_WINDOW: atoms[0],
+        _NET_WM_NAME: atoms[1],
+        UTF8_STRING: atoms[2]
+    }
+}
+
+impl<'a> EWMH<'a> {
+    pub fn get_active_window(&self) -> xcb::Window {
+        let cookie = xcb::get_property(&self.conn, false, self.root, self._NET_ACTIVE_WINDOW, xcb::ATOM_WINDOW, 0, 4);
+        let reply = cookie.get_reply().unwrap();
+        let value: &xcb::Window = unsafe { mem::transmute(&(reply.value()[0])) };
+        *value
+    }
+
+    pub fn get_window_name(&self, win: xcb::Window) -> String {
+        let cookie = xcb::get_property(&self.conn, false, win, self._NET_WM_NAME, self.UTF8_STRING, 0, 100);
+        let reply = cookie.get_reply();
+        let value: &str = match reply {
+            Ok(reply) => unsafe { mem::transmute(reply.value()) },
+            _ => ""
+        };
+        value.to_string()
+    }
+
+    pub fn watch(&self, win: xcb::Window, enabled: bool) {
+        let value = if enabled {
+            xcb::EVENT_MASK_PROPERTY_CHANGE
+        }
+        else {
+            xcb::EVENT_MASK_NO_EVENT
+        };
+        xcb::change_window_attributes(&self.conn, win, &[(xcb::CW_EVENT_MASK, value)]);
+    }
+}
+

+ 45 - 0
src/wm/mod.rs

@@ -0,0 +1,45 @@
+extern crate xcb;
+
+mod ewmh;
+
+use comm;
+use comm::Channel;
+use config::Config;
+
+pub fn wm(tx: &Channel, _config: &Config) {
+    if let Ok((conn, screen_num)) = xcb::Connection::connect(None) {
+        let ewmh = ewmh::connect(&conn, screen_num);
+
+        let mut last_win = ewmh.get_active_window();
+        comm::send(tx, "title", ewmh.get_window_name(last_win).as_ref());
+        ewmh.watch(ewmh.root, true);
+        ewmh.watch(last_win, true);
+        conn.flush();
+
+        loop {
+            let event = conn.wait_for_event();
+            match event {
+                None => { break; }
+                Some(event) => {
+                    if event.response_type() == xcb::PROPERTY_NOTIFY {
+                        let prop_event: &xcb::PropertyNotifyEvent = xcb::cast_event(&event);
+                        if prop_event.atom() == ewmh._NET_ACTIVE_WINDOW {
+                            let new_win = ewmh.get_active_window();
+                            ewmh.watch(last_win, false);
+                            ewmh.watch(new_win, true);
+                            conn.flush();
+                            last_win = new_win;
+                            comm::send(tx, "title", ewmh.get_window_name(last_win).as_ref());
+                        }
+                        else if prop_event.atom() == ewmh._NET_WM_NAME {
+                            comm::send(tx, "title", ewmh.get_window_name(last_win).as_ref());
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        println!("Could not connect to X!");
+    }
+}
+