@@ -1,7 +1,8 @@ |
| 1 | 1 | use std::collections::HashSet; |
| 2 | 2 | use std::sync::Arc; |
| 3 | 3 | |
| 4 | | -use x11rb::connection::Connection; |
| 4 | +use x11rb::connection::{Connection, RequestConnection}; |
| 5 | +use x11rb::protocol::xfixes::{self, ConnectionExt as XFixesExt, SelectionEventMask}; |
| 5 | 6 | use x11rb::protocol::xproto::{ |
| 6 | 7 | Atom, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, Screen, Window, WindowClass, |
| 7 | 8 | }; |
@@ -19,6 +20,8 @@ pub struct SelectionManager { |
| 19 | 20 | window: Window, |
| 20 | 21 | atoms: Atoms, |
| 21 | 22 | owned_selections: HashSet<Atom>, |
| 23 | + /// XFixes extension event base (for identifying XFixes events) |
| 24 | + xfixes_event_base: u8, |
| 22 | 25 | } |
| 23 | 26 | |
| 24 | 27 | impl SelectionManager { |
@@ -26,6 +29,20 @@ impl SelectionManager { |
| 26 | 29 | pub fn new(conn: Arc<RustConnection>, screen_num: usize, atoms: Atoms) -> Result<Self> { |
| 27 | 30 | let screen = conn.setup().roots[screen_num].clone(); |
| 28 | 31 | |
| 32 | + // Query XFixes extension |
| 33 | + let xfixes_info = conn.xfixes_query_version(5, 0)?.reply()?; |
| 34 | + tracing::debug!( |
| 35 | + "XFixes version: {}.{}", |
| 36 | + xfixes_info.major_version, |
| 37 | + xfixes_info.minor_version |
| 38 | + ); |
| 39 | + |
| 40 | + // Get XFixes event base |
| 41 | + let ext_info = conn |
| 42 | + .extension_information(xfixes::X11_EXTENSION_NAME)? |
| 43 | + .ok_or_else(|| crate::error::Error::Other("XFixes extension not available".into()))?; |
| 44 | + let xfixes_event_base = ext_info.first_event; |
| 45 | + |
| 29 | 46 | // Create a hidden window for selection ownership |
| 30 | 47 | let window = conn.generate_id()?; |
| 31 | 48 | conn.create_window( |
@@ -60,9 +77,50 @@ impl SelectionManager { |
| 60 | 77 | window, |
| 61 | 78 | atoms, |
| 62 | 79 | owned_selections: HashSet::new(), |
| 80 | + xfixes_event_base, |
| 63 | 81 | }) |
| 64 | 82 | } |
| 65 | 83 | |
| 84 | + /// Subscribe to selection owner change notifications via XFixes |
| 85 | + pub fn watch_selection(&self, selection: Atom) -> Result<()> { |
| 86 | + self.conn.xfixes_select_selection_input( |
| 87 | + self.window, |
| 88 | + selection, |
| 89 | + SelectionEventMask::SET_SELECTION_OWNER |
| 90 | + | SelectionEventMask::SELECTION_WINDOW_DESTROY |
| 91 | + | SelectionEventMask::SELECTION_CLIENT_CLOSE, |
| 92 | + )?; |
| 93 | + self.conn.flush()?; |
| 94 | + |
| 95 | + tracing::debug!( |
| 96 | + "Watching selection: {}", |
| 97 | + self.atoms.name(&*self.conn, selection).unwrap_or_default() |
| 98 | + ); |
| 99 | + |
| 100 | + Ok(()) |
| 101 | + } |
| 102 | + |
| 103 | + /// Stop watching a selection |
| 104 | + pub fn unwatch_selection(&self, selection: Atom) -> Result<()> { |
| 105 | + self.conn.xfixes_select_selection_input( |
| 106 | + self.window, |
| 107 | + selection, |
| 108 | + SelectionEventMask::from(0u32), |
| 109 | + )?; |
| 110 | + self.conn.flush()?; |
| 111 | + Ok(()) |
| 112 | + } |
| 113 | + |
| 114 | + /// Get the XFixes event base |
| 115 | + pub fn xfixes_event_base(&self) -> u8 { |
| 116 | + self.xfixes_event_base |
| 117 | + } |
| 118 | + |
| 119 | + /// Check if an event code is an XFixes SelectionNotify |
| 120 | + pub fn is_xfixes_selection_notify(&self, event_code: u8) -> bool { |
| 121 | + event_code == self.xfixes_event_base + xfixes::SELECTION_NOTIFY_EVENT |
| 122 | + } |
| 123 | + |
| 66 | 124 | /// Get the connection |
| 67 | 125 | pub fn conn(&self) -> &Arc<RustConnection> { |
| 68 | 126 | &self.conn |