Skip to content

Commit

Permalink
Merge pull request #261 from rtbo/fix_special_event_api
Browse files Browse the repository at this point in the history
Fix special event API
  • Loading branch information
rtbo authored Apr 22, 2024
2 parents 4a91741 + e77b421 commit 69bb3e8
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 5 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
153 changes: 153 additions & 0 deletions examples/present_special_event.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
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<Msg>) -> 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(())
}
84 changes: 80 additions & 4 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,13 +505,15 @@ pub fn parse_display(name: &str) -> Option<DisplayInfo> {
/// 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 {
raw: *mut xcb_special_event_t,
stamp: Timestamp,
}

#[allow(deprecated)]
#[cfg(any(feature = "xinput", feature = "present"))]
impl SpecialEventId {
/// The X timestamp associated with this special event Id
Expand All @@ -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 {
Expand Down Expand Up @@ -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<XGE: GeEvent>(&self) -> SpecialEventId {
unsafe {
let ext: *mut xcb_extension_t = match XGE::EXTENSION {
Expand All @@ -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<Event> {
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<Option<Event>> {
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<EID: Xid>(
&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 _,

Check warning on line 1342 in src/base.rs

View workflow job for this annotation

GitHub Actions / Build and Test

creating a mutable reference to mutable static is discouraged

Check warning on line 1342 in src/base.rs

View workflow job for this annotation

GitHub Actions / Build and Test

creating a mutable reference to mutable static is discouraged
#[cfg(feature = "present")]
Extension::Present => &mut present::FFI_EXT as *mut _,

Check warning on line 1344 in src/base.rs

View workflow job for this annotation

GitHub Actions / Build and Test

creating a mutable reference to mutable static is discouraged

Check warning on line 1344 in src/base.rs

View workflow job for this annotation

GitHub Actions / Build and Test

creating a mutable reference to mutable static is discouraged
_ => 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<Event> {
pub fn wait_for_special_event2(&self, se: &SpecialEvent) -> Result<Event> {
unsafe {
let ev = xcb_wait_for_special_event(self.c, se.raw);
self.handle_wait_for_event(ev)
Expand All @@ -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<Option<Event>> {
pub fn poll_for_special_event2(&self, se: &SpecialEvent) -> Result<Option<Event>> {
unsafe {
let ev = xcb_poll_for_special_event(self.c, se.raw);
self.handle_poll_for_event(ev)
Expand Down
2 changes: 1 addition & 1 deletion src/ffi/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand Down

0 comments on commit 69bb3e8

Please sign in to comment.