فهرست منبع

Implement adopting tray icons

Thomas Dy 7 سال پیش
والد
کامیت
74a30163c8
3فایلهای تغییر یافته به همراه92 افزوده شده و 16 حذف شده
  1. 31 0
      src/event.rs
  2. 14 14
      src/main.rs
  3. 47 2
      src/tray.rs

+ 31 - 0
src/event.rs

@@ -0,0 +1,31 @@
+use chan;
+use xcb;
+
+pub enum Event {
+    ChildRequest(xcb::Window),
+    ChildDestroyed(xcb::Window)
+}
+
+const CLIENT_MESSAGE: u8 = xcb::CLIENT_MESSAGE | 0x80;
+
+pub fn event_loop(conn: &xcb::Connection, tx: chan::Sender<Event>) {
+    loop {
+        match conn.wait_for_event() {
+            Some(event) => match event.response_type() {
+                xcb::EXPOSE => { println!("expose") },
+                CLIENT_MESSAGE => {
+                    let event: &xcb::ClientMessageEvent = xcb::cast_event(&event);
+                    let data = event.data().data32();
+                    let window = data[2];
+                    tx.send(Event::ChildRequest(window));
+                },
+                xcb::DESTROY_NOTIFY => {
+                    let event: &xcb::DestroyNotifyEvent = xcb::cast_event(&event);
+                    tx.send(Event::ChildDestroyed(event.window()));
+                },
+                _ => {}
+            },
+            None => { break; }
+        }
+    }
+}

+ 14 - 14
src/main.rs

@@ -4,6 +4,7 @@ extern crate chan_signal;
 extern crate xcb;
 
 mod atom;
+mod event;
 mod tray;
 
 use std::thread;
@@ -22,34 +23,32 @@ fn main() {
             return
         }
 
-        let tray = tray::Tray::new(&conn, &atoms, preferred as usize);
+        let mut tray = tray::Tray::new(&conn, &atoms, preferred as usize);
         tray.create();
         if !tray.take_selection() {
             println!("Could not take ownership of tray selection. Maybe another tray is also running?");
             return
         }
 
+        let (tx, rx) = chan::sync::<event::Event>(0);
         {
             let conn = conn.clone();
-            const CLIENT_MESSAGE: u8 = xcb::CLIENT_MESSAGE | 0x80;
             thread::spawn(move || {
-                loop {
-                    match conn.wait_for_event() {
-                        Some(event) => match event.response_type() {
-                            xcb::EXPOSE => { println!("expose") },
-                            CLIENT_MESSAGE => {
-                                println!("client message");
-                            },
-                            _ => {}
-                        },
-                        None => { break; }
-                    }
-                }
+                event::event_loop(&conn, tx);
             });
         }
 
         loop {
+            use event::Event::*;
             chan_select!(
+                rx.recv() -> event => match event.unwrap() {
+                    ChildRequest(window) => {
+                        tray.adopt(window);
+                    },
+                    ChildDestroyed(window) => {
+                        tray.forget(window);
+                    }
+                },
                 signal.recv() => {
                     break;
                 }
@@ -57,6 +56,7 @@ fn main() {
         }
 
         // cleanup code
+        tray.cleanup();
     }
     else {
         println!("Could not connect to X server!");

+ 47 - 2
src/tray.rs

@@ -5,7 +5,8 @@ pub struct Tray<'a> {
     conn: &'a xcb::Connection,
     atoms: &'a atom::Atoms<'a>,
     screen: usize,
-    window: xcb::Window
+    window: xcb::Window,
+    children: Vec<xcb::Window>
 }
 
 impl<'a> Tray<'a> {
@@ -14,7 +15,8 @@ impl<'a> Tray<'a> {
             conn: conn,
             atoms: atoms,
             screen: screen,
-            window: conn.generate_id()
+            window: conn.generate_id(),
+            children: vec![]
         }
     }
 
@@ -45,4 +47,47 @@ impl<'a> Tray<'a> {
         let owner = xcb::get_selection_owner(self.conn, selection).get_reply().unwrap().owner();
         owner == self.window
     }
+
+    pub fn adopt(&mut self, window: xcb::Window) {
+        let offset = (self.children.len() * 20) as i16;
+        xcb::change_window_attributes(self.conn, window, &[
+            (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)
+        ]);
+        xcb::reparent_window(self.conn, window, self.window, offset, 0);
+        xcb::map_window(self.conn, window);
+        self.conn.flush();
+        self.children.push(window);
+        self.resize();
+    }
+
+    pub fn forget(&mut self, window: xcb::Window) {
+        self.children.retain(|child| *child != window);
+        self.resize();
+    }
+
+    pub fn resize(&self) {
+        let len = self.children.len();
+        if len > 0 {
+            xcb::configure_window(self.conn, self.window, &[
+                (xcb::CONFIG_WINDOW_WIDTH as u16, (len * 20) as u32)
+            ]);
+            xcb::map_window(self.conn, self.window);
+        }
+        else {
+            xcb::unmap_window(self.conn, self.window);
+        }
+        self.conn.flush();
+    }
+
+    pub fn cleanup(&self) {
+        let setup = self.conn.get_setup();
+        let screen = setup.roots().nth(self.screen).unwrap();
+        let root = screen.root();
+
+        for child in self.children.iter() {
+            xcb::unmap_window(self.conn, *child);
+            xcb::reparent_window(self.conn, *child, root, 0, 0);
+        }
+        self.conn.flush();
+    }
 }