tray.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. use ui;
  2. use ui::x11;
  3. use widgets::{Message, MessageSender, Widget};
  4. use xcb;
  5. use std::sync::Arc;
  6. const CLIENT_MESSAGE: u8 = xcb::CLIENT_MESSAGE | 0x80; // 0x80 flag for client messages
  7. pub struct Tray {
  8. tx: MessageSender,
  9. conn: Arc<x11::Connection>,
  10. window: xcb::Window,
  11. children: Vec<xcb::Window>,
  12. timestamp: xcb::Timestamp,
  13. }
  14. impl Tray {
  15. pub fn new(tx: MessageSender, conn: Arc<x11::Connection>, window: xcb::Window) -> Tray {
  16. Tray {
  17. conn: conn,
  18. tx: tx,
  19. window: window,
  20. children: vec![],
  21. timestamp: 0
  22. }
  23. }
  24. pub fn take_selection(&mut self, timestamp: xcb::Timestamp) -> bool {
  25. let selection = self.conn.atom(x11::_NET_SYSTEM_TRAY_S0);
  26. xcb::set_selection_owner(&self.conn, self.window, selection, timestamp);
  27. let owner = xcb::get_selection_owner(&self.conn, selection).get_reply().unwrap().owner();
  28. let ok = owner == self.window;
  29. if ok {
  30. self.timestamp = timestamp;
  31. let screen = self.conn.default_screen();
  32. let client_event = xcb::ClientMessageEvent::new(
  33. 32, // 32 bits (refers to data)
  34. screen.root,
  35. self.conn.atom(x11::MANAGER),
  36. xcb::ClientMessageData::from_data32([timestamp, selection, self.window, 0, 0])
  37. );
  38. xcb::send_event(&self.conn, false, screen.root, xcb::EVENT_MASK_STRUCTURE_NOTIFY, &client_event);
  39. self.conn.flush();
  40. }
  41. ok
  42. }
  43. pub fn adopt(&mut self, window: xcb::Window) {
  44. let conn = &self.conn;
  45. xcb::change_window_attributes(conn, window, &[
  46. (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)
  47. ]);
  48. xcb::reparent_window(conn, window, self.window, 0, 0);
  49. xcb::map_window(conn, window);
  50. self.force_size(window, None);
  51. conn.flush();
  52. self.children.push(window);
  53. self.relayout();
  54. }
  55. pub fn forget(&mut self, window: xcb::Window) {
  56. self.children.retain(|child| *child != window);
  57. self.relayout();
  58. }
  59. fn relayout(&self) {
  60. self.tx.send(Message::Relayout).expect("Failed to send relayout");
  61. }
  62. pub fn force_size(&self, window: xcb::Window, dimensions: Option<(u16, u16)>) {
  63. let conn = &self.conn;
  64. let dimensions = dimensions.unwrap_or_else(|| {
  65. let geometry = xcb::get_geometry(conn, window).get_reply().unwrap();
  66. (geometry.width(), geometry.height())
  67. });
  68. if dimensions != (ui::SIZE, ui::SIZE) {
  69. xcb::configure_window(conn, window, &[
  70. (xcb::CONFIG_WINDOW_WIDTH as u16, ui::SIZE as u32),
  71. (xcb::CONFIG_WINDOW_HEIGHT as u16, ui::SIZE as u32)
  72. ]);
  73. conn.flush();
  74. }
  75. }
  76. }
  77. impl Widget for Tray {
  78. fn width(&mut self) -> u16 {
  79. (self.children.len() * 20) as u16
  80. }
  81. fn render(&mut self, x: u16, _w: u16) {
  82. for (index, child) in self.children.iter().enumerate() {
  83. let window = *child;
  84. let xpos = x as u32 + index as u32 * 20;
  85. xcb::configure_window(&self.conn, window, &[
  86. (xcb::CONFIG_WINDOW_X as u16, xpos)
  87. ]);
  88. }
  89. }
  90. fn handle_event(&mut self, event: &Message) -> bool {
  91. match event {
  92. &Message::XcbEvent(ref event) =>
  93. match event.response_type() {
  94. xcb::PROPERTY_NOTIFY if self.timestamp == 0 => {
  95. let event: &xcb::PropertyNotifyEvent = unsafe { xcb::cast_event(&event) };
  96. if !self.take_selection(event.time()) {
  97. println!("Could not take ownership of tray selection. Maybe another tray is also running?");
  98. return true
  99. }
  100. },
  101. CLIENT_MESSAGE => {
  102. let event: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(&event) };
  103. if event.type_() == self.conn.atom(x11::_NET_SYSTEM_TRAY_OPCODE) {
  104. let data = event.data().data32();
  105. let opcode = data[1];
  106. let window = data[2];
  107. if opcode == 0 {
  108. self.adopt(window);
  109. }
  110. }
  111. },
  112. xcb::REPARENT_NOTIFY => {
  113. let event: &xcb::ReparentNotifyEvent = unsafe { xcb::cast_event(&event) };
  114. if event.parent() != self.window {
  115. self.forget(event.window());
  116. }
  117. },
  118. xcb::DESTROY_NOTIFY => {
  119. let event: &xcb::DestroyNotifyEvent = unsafe { xcb::cast_event(&event) };
  120. self.forget(event.window());
  121. },
  122. xcb::CONFIGURE_NOTIFY => {
  123. let event: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(&event) };
  124. let window = event.window();
  125. if window != self.window {
  126. self.force_size(window, Some((event.width(), event.height())));
  127. }
  128. },
  129. xcb::SELECTION_CLEAR => {
  130. self.finish();
  131. },
  132. _ => {}
  133. },
  134. _ => {}
  135. }
  136. false
  137. }
  138. fn finish(&mut self) {
  139. let conn = &self.conn;
  140. let screen = conn.default_screen();
  141. for child in self.children.iter() {
  142. let window = *child;
  143. xcb::change_window_attributes(conn, window, &[
  144. (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_NO_EVENT)
  145. ]);
  146. xcb::unmap_window(conn, window);
  147. xcb::reparent_window(conn, window, screen.root, 0, 0);
  148. }
  149. xcb::change_window_attributes(conn, self.window, &[
  150. (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_STRUCTURE_NOTIFY)
  151. ]);
  152. xcb::set_selection_owner(conn, xcb::NONE, conn.atom(x11::_NET_SYSTEM_TRAY_S0), self.timestamp);
  153. conn.flush();
  154. }
  155. }