Browse Source

Integrate stalonetray into panel

Thomas Dy 9 years ago
parent
commit
434dd0f541
5 changed files with 144 additions and 18 deletions
  1. 8 0
      src/bar.rs
  2. 5 4
      src/main.rs
  3. 29 0
      src/tray.rs
  4. 61 1
      src/wm/ewmh.rs
  5. 41 13
      src/wm/mod.rs

+ 8 - 0
src/bar.rs

@@ -18,6 +18,14 @@ impl Bar {
         bar.stdin(Stdio::piped());
         bar.stdout(Stdio::piped());
 
+        bar.arg("-n");
+        if top {
+            bar.arg("__panel_top");
+        }
+        else {
+            bar.arg("__panel_bot");
+        }
+
         bar.arg("-B");
         bar.arg("#FF000000");
 

+ 5 - 4
src/main.rs

@@ -3,6 +3,7 @@ mod comm;
 mod config;
 mod bspwm;
 mod bar;
+mod tray;
 mod store;
 mod music;
 mod wm;
@@ -19,7 +20,7 @@ fn main() {
     let cfg = config::load(&config_path);
 
     let mut topbar = bar::Bar::new(true, &cfg);
-    let mut botbar = bar::Bar::new(false, &cfg);
+    let _tray = tray::Tray::new();
 
     let cfg = Arc::new(cfg);
     let (tx, rx) = mpsc::channel::<Message>();
@@ -34,12 +35,12 @@ fn main() {
         let msg = rx.recv().unwrap();
         data.save(msg);
 
-        topbar.send(&format!("{}| {}%{{r}}{}",
+        topbar.send(&format!("{}| {}%{{r}}{}{}",
             data.get("desktops"),
             data.get("title"),
-            data.get("sensors")
+            data.get("sensors"),
+            data.get("spacer")
         ));
-        botbar.send(&format!("{}", data.get("mpd")));
     }
 }
 

+ 29 - 0
src/tray.rs

@@ -0,0 +1,29 @@
+use std::process::{Child, Command};
+
+pub struct Tray {
+    child: Child
+}
+
+impl Tray {
+    pub fn new() -> Tray {
+        let child = Command::new("stalonetray")
+            .arg("-c").arg("/dev/null")
+            .arg("--geometry").arg("1x1-0+0")
+            .arg("-bg").arg("black")
+            .arg("--grow-gravity").arg("SE")
+            .arg("--icon-size").arg("20")
+            .arg("--kludges").arg("force_icons_size")
+            .spawn()
+            .unwrap_or_else(|e| { panic!("Could not start stalonetray: {}", e) });
+
+        Tray {
+            child: child
+        }
+    }
+}
+
+impl Drop for Tray {
+    fn drop(&mut self) {
+        self.child.kill().ok();
+    }
+}

+ 61 - 1
src/wm/ewmh.rs

@@ -17,6 +17,11 @@ pub struct EWMH<'a> {
     pub UTF8_STRING: xcb::Atom
 }
 
+pub struct WinClass {
+    pub instance: String,
+    pub class: String
+}
+
 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();
@@ -50,7 +55,40 @@ impl<'a> EWMH<'a> {
             Ok(reply) => unsafe { mem::transmute(reply.value()) },
             _ => ""
         };
-        value.to_string()
+        if value == "" {
+            let cookie = xcb::get_property(&self.conn, false, win, xcb::ATOM_WM_NAME, xcb::ATOM_STRING, 0, 100);
+            let reply = cookie.get_reply();
+            let value: &str = match reply {
+                Ok(reply) => unsafe { mem::transmute(reply.value()) },
+                _ => ""
+            };
+            value.to_string()
+        }
+        else {
+            value.to_string()
+        }
+    }
+
+    pub fn get_window_class(&self, win: xcb::Window) -> WinClass {
+        let cookie = xcb::get_property(&self.conn, false, win, xcb::ATOM_WM_CLASS, xcb::ATOM_STRING, 0, 100);
+        let reply = cookie.get_reply();
+        let value: &str = match reply {
+            Ok(reply) => unsafe { mem::transmute(reply.value()) },
+            _ => ""
+        };
+        let parts: Vec<&str> = value.split_terminator('\0').collect();
+        if parts.len() == 2 {
+            WinClass {
+                instance: parts[0].to_string(),
+                class: parts[1].to_string()
+            }
+        }
+        else {
+            WinClass {
+                instance: "".to_string(),
+                class: "".to_string()
+            }
+        }
     }
 
     pub fn watch(&self, win: xcb::Window, enabled: bool) {
@@ -62,5 +100,27 @@ impl<'a> EWMH<'a> {
         };
         xcb::change_window_attributes(&self.conn, win, &[(xcb::CW_EVENT_MASK, value)]);
     }
+
+    pub fn search_by_name(&self, name: &str) -> xcb::Window {
+        let cookie = xcb::query_tree(&self.conn, self.root);
+        let reply = cookie.get_reply().unwrap();
+        for w in reply.children() {
+            if self.get_window_name(*w) == name {
+                return *w;
+            }
+        }
+        return 0;
+    }
+
+    pub fn search_by_class(&self, class_name: &str) -> xcb::Window {
+        let cookie = xcb::query_tree(&self.conn, self.root);
+        let reply = cookie.get_reply().unwrap();
+        for w in reply.children() {
+            if self.get_window_class(*w).class == class_name {
+                return *w;
+            }
+        }
+        return 0;
+    }
 }
 

+ 41 - 13
src/wm/mod.rs

@@ -5,6 +5,19 @@ mod ewmh;
 use comm;
 use comm::Channel;
 use config::Config;
+use std::thread;
+
+pub fn wait_for<F>(f: F) -> xcb::Window where F: Fn() -> xcb::Window {
+    loop {
+        let w = f();
+        if w == 0 {
+            thread::sleep_ms(100);
+        }
+        else {
+            return w;
+        }
+    }
+}
 
 pub fn wm(tx: &Channel, _config: &Config) {
     if let Ok((conn, screen_num)) = xcb::Connection::connect(None) {
@@ -14,6 +27,13 @@ pub fn wm(tx: &Channel, _config: &Config) {
         comm::send(tx, "title", ewmh.get_window_name(last_win).as_ref());
         ewmh.watch(ewmh.root, true);
         ewmh.watch(last_win, true);
+
+        let stalonetray = wait_for(|| ewmh.search_by_class("stalonetray"));
+        let panel = wait_for(|| ewmh.search_by_name("__panel_top"));
+
+        xcb::configure_window(&conn, stalonetray, &[(xcb::CONFIG_WINDOW_SIBLING as u16, panel), (xcb::CONFIG_WINDOW_STACK_MODE as u16, xcb::STACK_MODE_ABOVE)]);
+        xcb::change_window_attributes(&conn, stalonetray, &[(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)]);
+
         conn.flush();
 
         loop {
@@ -21,19 +41,27 @@ pub fn wm(tx: &Channel, _config: &Config) {
             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());
-                        }
+                    match 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());
+                            }
+                        },
+                        xcb::CONFIGURE_NOTIFY => {
+                            let event: &xcb::ConfigureNotifyEvent = xcb::cast_event(&event);
+                            let spacer = format!("%{{O{}}}", event.width()+5);
+                            comm::send(tx, "spacer", spacer.as_ref());
+                        },
+                        _ => ()
                     }
                 }
             }