123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- use ui;
- use ui::x11;
- use widgets::{Message, MessageSender, Widget};
- use xcb;
- use std::sync::Arc;
- const CLIENT_MESSAGE: u8 = xcb::CLIENT_MESSAGE | 0x80; // 0x80 flag for client messages
- pub struct Tray {
- tx: MessageSender,
- conn: Arc<x11::Connection>,
- window: xcb::Window,
- children: Vec<xcb::Window>,
- timestamp: xcb::Timestamp,
- }
- impl Tray {
- pub fn new(tx: MessageSender, conn: Arc<x11::Connection>, window: xcb::Window) -> Tray {
- Tray {
- conn: conn,
- tx: tx,
- window: window,
- children: vec![],
- timestamp: 0
- }
- }
- pub fn take_selection(&mut self, timestamp: xcb::Timestamp) -> bool {
- let selection = self.conn.atom(x11::_NET_SYSTEM_TRAY_S0);
- xcb::set_selection_owner(&self.conn, self.window, selection, timestamp);
- let owner = xcb::get_selection_owner(&self.conn, selection).get_reply().unwrap().owner();
- let ok = owner == self.window;
- if ok {
- self.timestamp = timestamp;
- let screen = self.conn.default_screen();
- let client_event = xcb::ClientMessageEvent::new(
- 32, // 32 bits (refers to data)
- screen.root,
- self.conn.atom(x11::MANAGER),
- xcb::ClientMessageData::from_data32([timestamp, selection, self.window, 0, 0])
- );
- xcb::send_event(&self.conn, false, screen.root, xcb::EVENT_MASK_STRUCTURE_NOTIFY, &client_event);
- self.conn.flush();
- }
- ok
- }
- pub fn adopt(&mut self, window: xcb::Window) {
- let conn = &self.conn;
- xcb::change_window_attributes(conn, window, &[
- (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)
- ]);
- xcb::reparent_window(conn, window, self.window, 0, 0);
- xcb::map_window(conn, window);
- self.force_size(window, None);
- conn.flush();
- self.children.push(window);
- self.relayout();
- }
- pub fn forget(&mut self, window: xcb::Window) {
- self.children.retain(|child| *child != window);
- self.relayout();
- }
- fn relayout(&self) {
- self.tx.send(Message::Relayout).expect("Failed to send relayout");
- }
- pub fn force_size(&self, window: xcb::Window, dimensions: Option<(u16, u16)>) {
- let conn = &self.conn;
- let dimensions = dimensions.unwrap_or_else(|| {
- let geometry = xcb::get_geometry(conn, window).get_reply().unwrap();
- (geometry.width(), geometry.height())
- });
- if dimensions != (ui::SIZE, ui::SIZE) {
- xcb::configure_window(conn, window, &[
- (xcb::CONFIG_WINDOW_WIDTH as u16, ui::SIZE as u32),
- (xcb::CONFIG_WINDOW_HEIGHT as u16, ui::SIZE as u32)
- ]);
- conn.flush();
- }
- }
- }
- impl Widget for Tray {
- fn width(&mut self) -> u16 {
- (self.children.len() * 20) as u16
- }
- fn render(&mut self, x: u16, _w: u16) {
- for (index, child) in self.children.iter().enumerate() {
- let window = *child;
- let xpos = x as u32 + index as u32 * 20;
- xcb::configure_window(&self.conn, window, &[
- (xcb::CONFIG_WINDOW_X as u16, xpos)
- ]);
- }
- }
- fn handle_event(&mut self, event: &Message) -> bool {
- match event {
- &Message::XcbEvent(ref event) =>
- match event.response_type() {
- xcb::PROPERTY_NOTIFY if self.timestamp == 0 => {
- let event: &xcb::PropertyNotifyEvent = unsafe { xcb::cast_event(&event) };
- if !self.take_selection(event.time()) {
- println!("Could not take ownership of tray selection. Maybe another tray is also running?");
- return true
- }
- },
- CLIENT_MESSAGE => {
- let event: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(&event) };
- if event.type_() == self.conn.atom(x11::_NET_SYSTEM_TRAY_OPCODE) {
- let data = event.data().data32();
- let opcode = data[1];
- let window = data[2];
- if opcode == 0 {
- self.adopt(window);
- }
- }
- },
- xcb::REPARENT_NOTIFY => {
- let event: &xcb::ReparentNotifyEvent = unsafe { xcb::cast_event(&event) };
- if event.parent() != self.window {
- self.forget(event.window());
- }
- },
- xcb::DESTROY_NOTIFY => {
- let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
- self.forget(event.window());
- },
- xcb::CONFIGURE_NOTIFY => {
- let event: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(&event) };
- let window = event.window();
- if window != self.window {
- self.force_size(window, Some((event.width(), event.height())));
- }
- },
- xcb::SELECTION_CLEAR => {
- self.finish();
- },
- _ => {}
- },
- _ => {}
- }
- false
- }
- fn finish(&mut self) {
- let conn = &self.conn;
- let screen = conn.default_screen();
- for child in self.children.iter() {
- let window = *child;
- xcb::change_window_attributes(conn, window, &[
- (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_NO_EVENT)
- ]);
- xcb::unmap_window(conn, window);
- xcb::reparent_window(conn, window, screen.root, 0, 0);
- }
- xcb::change_window_attributes(conn, self.window, &[
- (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)
- ]);
- xcb::set_selection_owner(conn, xcb::NONE, conn.atom(x11::_NET_SYSTEM_TRAY_S0), self.timestamp);
- conn.flush();
- }
- }
|