diff --git a/Cargo.toml b/Cargo.toml index f1a3ccd3e26..4a99d38c49a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,6 +98,10 @@ name = "get_all_windows" name = "opengl_window" required-features = ["glx", "xlib_xcb", "dri2"] +[[example]] +name = "present_special_event" +required-features = ["present", "randr"] + [[example]] name = "randr_crtc_info" required-features = ["randr"] diff --git a/examples/present_special_event.rs b/examples/present_special_event.rs new file mode 100644 index 00000000000..0294202e341 --- /dev/null +++ b/examples/present_special_event.rs @@ -0,0 +1,153 @@ +use xcb::{present, x, Xid}; + +use std::{ + sync::{mpsc, Arc}, + thread, + time::Duration, +}; + +xcb::atoms_struct! { + #[derive(Debug)] + struct Atoms { + wm_protocols => b"WM_PROTOCOLS", + wm_del_window => b"WM_DELETE_WINDOW", + } +} + +struct Example { + conn: xcb::Connection, + window: x::Window, + atoms: Atoms, +} + +impl Example { + fn build() -> xcb::Result { + let (conn, screen_num) = + xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[])?; + let setup = conn.get_setup(); + let screen = setup.roots().nth(screen_num as usize).unwrap(); + + let window: x::Window = conn.generate_id(); + + conn.send_request(&x::CreateWindow { + depth: x::COPY_FROM_PARENT as u8, + wid: window, + parent: screen.root(), + x: 0, + y: 0, + width: 800, + height: 600, + border_width: 10, + class: x::WindowClass::InputOutput, + visual: screen.root_visual(), + value_list: &[ + x::Cw::BackPixel(screen.white_pixel()), + x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + ], + }); + + conn.send_and_check_request(&x::MapWindow { window })?; + + // retrieving a few atoms + let atoms = Atoms::intern_all(&conn)?; + + // activate the sending of close event through `x::Event::ClientMessage` + // either the request must be checked as follow, or conn.flush() must be called before entering the loop + conn.send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window, + property: atoms.wm_protocols, + r#type: x::ATOM_ATOM, + data: &[atoms.wm_del_window], + })?; + + conn.flush()?; + + Ok(Self { + conn, + window, + atoms, + }) + } + + fn main_event_loop(&self) -> xcb::Result<()> { + loop { + let ev = self.conn.wait_for_event()?; + println!("Received regular event {:#?}", ev); + match ev { + xcb::Event::X(x::Event::KeyPress(ev)) => { + if ev.detail() == 0x18 { + // Q (on qwerty) + break Ok(()); + } + } + xcb::Event::X(x::Event::ClientMessage(ev)) => { + if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { + if atom == self.atoms.wm_del_window.resource_id() { + // window "x" button clicked by user, we gracefully exit + break Ok(()); + } + } + } + _ => {} + } + } + } + + fn special_event_loop(&self, rx: mpsc::Receiver) -> xcb::Result<()> { + let eid = self.conn.generate_id(); + + self.conn.send_request(&present::SelectInput { + eid, + window: self.window, + event_mask: present::EventMask::CONFIGURE_NOTIFY + | present::EventMask::COMPLETE_NOTIFY + | present::EventMask::IDLE_NOTIFY, + }); + + let special_event = self + .conn + .register_for_special_event(xcb::Extension::Present, eid); + + self.conn.flush()?; + + loop { + let ev = self.conn.poll_for_special_event2(&special_event)?; + if let Some(ev) = ev { + println!("Received special event {:#?}", ev); + } + + if let Ok(msg) = rx.recv_timeout(Duration::from_millis(100)) { + match msg { + Msg::Quit => break, + } + } + } + + self.conn.unregister_for_special_event(special_event); + + Ok(()) + } +} + +enum Msg { + Quit, +} + +fn main() -> xcb::Result<()> { + let example = Example::build()?; + let example = Arc::new(example); + + let (tx, rx) = mpsc::channel(); + + let special_event_thread = { + let example = example.clone(); + thread::spawn(move || example.special_event_loop(rx)) + }; + example.main_event_loop()?; + + tx.send(Msg::Quit).unwrap(); + special_event_thread.join().unwrap()?; + + Ok(()) +} diff --git a/src/base.rs b/src/base.rs index 2d728200809..aeb19927ccd 100644 --- a/src/base.rs +++ b/src/base.rs @@ -505,6 +505,7 @@ pub fn parse_display(name: &str) -> Option { /// A struct that serve as an identifier for internal special queue in XCB /// /// See [Connection::register_for_special_xge]. +#[deprecated(note = "Broken API: use `SpecialEvent` instead")] #[cfg(any(feature = "xinput", feature = "present"))] #[derive(Debug)] pub struct SpecialEventId { @@ -512,6 +513,7 @@ pub struct SpecialEventId { stamp: Timestamp, } +#[allow(deprecated)] #[cfg(any(feature = "xinput", feature = "present"))] impl SpecialEventId { /// The X timestamp associated with this special event Id @@ -520,6 +522,22 @@ impl SpecialEventId { } } +/// A struct that serve as an identifier for internal special queue in XCB +/// +/// See [Connection::register_for_special_event]. +#[cfg(any(feature = "xinput", feature = "present"))] +#[derive(Debug)] +pub struct SpecialEvent { + raw: *mut xcb_special_event_t, +} + +// safe because XCB is thread safe. +#[cfg(any(feature = "xinput", feature = "present"))] +unsafe impl Send for SpecialEvent {} + +#[cfg(any(feature = "xinput", feature = "present"))] +unsafe impl Sync for SpecialEvent {} + /// Error type that is returned by `Connection::has_error`. #[derive(Debug)] pub enum ConnError { @@ -1256,7 +1274,9 @@ impl Connection { /// XGE events are only defined in the `xinput` and `present` extensions /// /// This function is present only if either of the `xinput` or `present` cargo features are active. + #[deprecated(note = "Broken API: use `register_for_special_event` instead")] #[cfg(any(feature = "xinput", feature = "present"))] + #[allow(deprecated)] pub fn register_for_special_xge(&self) -> SpecialEventId { unsafe { let ext: *mut xcb_extension_t = match XGE::EXTENSION { @@ -1275,17 +1295,73 @@ impl Connection { } } + /// Stop listening to a special event + #[deprecated(note = "use `unregister_for_special_event` instead")] + #[cfg(any(feature = "xinput", feature = "present"))] + pub fn unregister_for_special_xge(&self, se: SpecialEvent) { + unsafe { + xcb_unregister_for_special_event(self.c, se.raw); + } + } + + /// Returns the next event from a special queue, blocking until one arrives + #[deprecated(note = "Broken API: use `wait_for_special_event2` instead")] + #[cfg(any(feature = "xinput", feature = "present"))] + pub fn wait_for_special_event(&self, se: SpecialEvent) -> Result { + unsafe { + let ev = xcb_wait_for_special_event(self.c, se.raw); + self.handle_wait_for_event(ev) + } + } + + /// Returns the next event from a special queue + #[deprecated(note = "Broken API: use `poll_for_special_event2` instead")] + #[cfg(any(feature = "xinput", feature = "present"))] + pub fn poll_for_special_event(&self, se: SpecialEvent) -> Result> { + unsafe { + let ev = xcb_poll_for_special_event(self.c, se.raw); + self.handle_poll_for_event(ev) + } + } + + /// Start listening for a special event. + /// + /// Effectively creates an internal special queue for this event + /// XGE events are only defined in the `xinput` and `present` extensions + /// + /// This function is present only if either of the `xinput` or `present` cargo features are active. + #[cfg(any(feature = "xinput", feature = "present"))] + pub fn register_for_special_event( + &self, + extension: Extension, + eid: EID, + ) -> SpecialEvent { + unsafe { + let ext: *mut xcb_extension_t = match extension { + #[cfg(feature = "xinput")] + Extension::Input => &mut xinput::FFI_EXT as *mut _, + #[cfg(feature = "present")] + Extension::Present => &mut present::FFI_EXT as *mut _, + _ => panic!("only Input and Present have XGE events"), + }; + + let raw = xcb_register_for_special_xge(self.c, ext, eid.resource_id(), ptr::null_mut()); + + SpecialEvent { raw } + } + } + /// Stop listening to a special event #[cfg(any(feature = "xinput", feature = "present"))] - pub fn unregister_for_special_xge(&self, se: SpecialEventId) { + pub fn unregister_for_special_event(&self, se: SpecialEvent) { unsafe { - xcb_unregister_for_special_xge(self.c, se.raw); + xcb_unregister_for_special_event(self.c, se.raw); } } /// Returns the next event from a special queue, blocking until one arrives #[cfg(any(feature = "xinput", feature = "present"))] - pub fn wait_for_special_event(&self, se: SpecialEventId) -> Result { + pub fn wait_for_special_event2(&self, se: &SpecialEvent) -> Result { unsafe { let ev = xcb_wait_for_special_event(self.c, se.raw); self.handle_wait_for_event(ev) @@ -1294,7 +1370,7 @@ impl Connection { /// Returns the next event from a special queue #[cfg(any(feature = "xinput", feature = "present"))] - pub fn poll_for_special_event(&self, se: SpecialEventId) -> Result> { + pub fn poll_for_special_event2(&self, se: &SpecialEvent) -> Result> { unsafe { let ev = xcb_poll_for_special_event(self.c, se.raw); self.handle_poll_for_event(ev) diff --git a/src/ffi/base.rs b/src/ffi/base.rs index d2ae07f3230..4e69785fa45 100644 --- a/src/ffi/base.rs +++ b/src/ffi/base.rs @@ -145,7 +145,7 @@ extern "C" { stamp: *mut u32, ) -> *mut xcb_special_event_t; - pub(crate) fn xcb_unregister_for_special_xge( + pub(crate) fn xcb_unregister_for_special_event( c: *mut xcb_connection_t, se: *mut xcb_special_event_t, );