From 8c12314e8d084cecffc87a2b6ffe8efc87adcd2c Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 12 Jun 2026 22:45:51 +0200 Subject: [PATCH 1/2] Replace raw poll() calls with polling crate --- Cargo.toml | 2 +- src/wrappers.rs | 3 ++ src/wrappers/connection_poller.rs | 65 +++++++++++++++++++++++++++++++ src/wrappers/xlib/xlib_xcb.rs | 7 ++++ src/x11/event_loop.rs | 26 +++---------- 5 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 src/wrappers/connection_poller.rs diff --git a/Cargo.toml b/Cargo.toml index caa2bc70..534fb563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ raw-window-handle = "0.5" [target.'cfg(target_os="linux")'.dependencies] x11rb = { version = "0.13.2", features = ["cursor", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false } x11-dl = { version = "2.21" } -nix = "0.22.0" +polling = "3.11.0" percent-encoding = "2.3.1" bytemuck = "1.15.0" diff --git a/src/wrappers.rs b/src/wrappers.rs index 6c0f5bce..d06a6d8b 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -19,6 +19,9 @@ pub mod xlib; #[cfg(all(target_os = "linux", feature = "opengl"))] pub mod glx; +#[cfg(target_os = "linux")] +pub mod connection_poller; + /// Wrappers and utilities around the Win32 API #[cfg(target_os = "windows")] pub mod win32; diff --git a/src/wrappers/connection_poller.rs b/src/wrappers/connection_poller.rs new file mode 100644 index 00000000..ea92226b --- /dev/null +++ b/src/wrappers/connection_poller.rs @@ -0,0 +1,65 @@ +use polling::{Event, Events, Poller}; +use std::error::Error; +use std::io; +use std::os::fd::{AsFd, BorrowedFd}; +use std::time::{Duration, Instant}; + +pub struct ConnectionPoller<'a> { + poller: Poller, + events: Events, + fd: BorrowedFd<'a>, +} + +const CONNECTION_KEY: usize = 42; + +impl<'a> ConnectionPoller<'a> { + pub fn new(source: &'a impl AsFd) -> io::Result { + let poller = Poller::new()?; + let fd = source.as_fd(); + unsafe { poller.add(&fd, Event::readable(CONNECTION_KEY))? }; + + Ok(Self { poller, fd, events: Events::new() }) + } + + pub fn wait(&mut self, deadline: Instant) -> io::Result { + self.events.clear(); + // NOTE: polling crate already handles retrying on EINTR + let new_events_count = self.poller.wait_deadline(&mut self.events, deadline)?; + + if new_events_count == 0 { + return Ok(PollStatus::Nothing); + } + + for event in self.events.iter() { + if event.key != CONNECTION_KEY { + continue; + } + + if let Some(true) = event.is_err() { + panic!("xcb connection poll error") + } + + if event.is_interrupt() { + return Ok(PollStatus::ConnectionClosed); + } + } + + Ok(PollStatus::Nothing) + } + + pub fn delete(self) -> Result<(), Box> { + Ok(self.poller.delete(self.fd)?) + } +} + +impl<'a> Drop for ConnectionPoller<'a> { + fn drop(&mut self) { + let _ = self.poller.delete(self.fd); + } +} + +pub enum PollStatus { + Nothing, + ReadAvailable, + ConnectionClosed, +} diff --git a/src/wrappers/xlib/xlib_xcb.rs b/src/wrappers/xlib/xlib_xcb.rs index 88e84396..e321b736 100644 --- a/src/wrappers/xlib/xlib_xcb.rs +++ b/src/wrappers/xlib/xlib_xcb.rs @@ -1,6 +1,7 @@ use crate::wrappers::xlib::xlib_connection::XlibConnection; use std::error::Error; use std::ops::Deref; +use std::os::fd::{AsFd, BorrowedFd}; use std::os::raw::c_int; use x11_dl::xlib::Display; use x11_dl::xlib_xcb::Xlib_xcb; @@ -72,3 +73,9 @@ impl Deref for XlibXcbConnection { &self.xcb_connection } } + +impl AsFd for XlibXcbConnection { + fn as_fd(&self) -> BorrowedFd<'_> { + self.xcb_connection.as_fd() + } +} diff --git a/src/x11/event_loop.rs b/src/x11/event_loop.rs index 690fb129..19015df6 100644 --- a/src/x11/event_loop.rs +++ b/src/x11/event_loop.rs @@ -1,3 +1,4 @@ +use crate::wrappers::connection_poller::{ConnectionPoller, PollStatus}; use crate::x11::drag_n_drop::DragNDropState; use crate::x11::keyboard::{convert_key_press_event, convert_key_release_event, key_mods}; use crate::x11::{ParentHandle, Window, WindowInner}; @@ -6,7 +7,7 @@ use crate::{ WindowInfo, }; use std::error::Error; -use std::os::fd::AsRawFd; +use std::rc::Rc; use std::time::{Duration, Instant}; use x11rb::connection::Connection; use x11rb::protocol::Event as XEvent; @@ -63,13 +64,9 @@ impl EventLoop { } // Event loop - // FIXME: poll() acts fine on linux, sometimes funky on *BSD. XCB upstream uses a define to - // switch between poll() and select() (the latter of which is fine on *BSD), and we should do - // the same. pub fn run(&mut self) -> Result<(), Box> { - use nix::poll::*; - - let xcb_fd = self.window.xcb_connection.conn.as_raw_fd(); + let connection = Rc::clone(&self.window.xcb_connection); + let mut poller = ConnectionPoller::new(&connection.conn)?; let mut last_frame = Instant::now(); self.event_loop_running = true; @@ -87,24 +84,13 @@ impl EventLoop { last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval); } - let mut fds = [PollFd::new(xcb_fd, PollFlags::POLLIN)]; - // Check for any events in the internal buffers // before going to sleep: self.drain_xcb_events()?; // FIXME: handle errors - poll(&mut fds, next_frame.duration_since(Instant::now()).subsec_millis() as i32) - .unwrap(); - - if let Some(revents) = fds[0].revents() { - if revents.contains(PollFlags::POLLERR) { - panic!("xcb connection poll error"); - } - - if revents.contains(PollFlags::POLLIN) { - self.drain_xcb_events()?; - } + if let PollStatus::ReadAvailable = poller.wait(next_frame).unwrap() { + self.drain_xcb_events()?; } // Check if the parents's handle was dropped (such as when the host From bff3dc8c0251546dcee206d68cb7bc4c6c2129ca Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Fri, 12 Jun 2026 22:58:07 +0200 Subject: [PATCH 2/2] fixes --- src/wrappers/connection_poller.rs | 9 +++++---- src/x11/event_loop.rs | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wrappers/connection_poller.rs b/src/wrappers/connection_poller.rs index ea92226b..19a8b913 100644 --- a/src/wrappers/connection_poller.rs +++ b/src/wrappers/connection_poller.rs @@ -1,8 +1,7 @@ use polling::{Event, Events, Poller}; -use std::error::Error; use std::io; use std::os::fd::{AsFd, BorrowedFd}; -use std::time::{Duration, Instant}; +use std::time::Instant; pub struct ConnectionPoller<'a> { poller: Poller, @@ -42,13 +41,15 @@ impl<'a> ConnectionPoller<'a> { if event.is_interrupt() { return Ok(PollStatus::ConnectionClosed); } + + return Ok(PollStatus::ReadAvailable); } Ok(PollStatus::Nothing) } - pub fn delete(self) -> Result<(), Box> { - Ok(self.poller.delete(self.fd)?) + pub fn delete(self) -> io::Result<()> { + self.poller.delete(self.fd) } } diff --git a/src/x11/event_loop.rs b/src/x11/event_loop.rs index 19015df6..21b50b1d 100644 --- a/src/x11/event_loop.rs +++ b/src/x11/event_loop.rs @@ -109,6 +109,8 @@ impl EventLoop { } } + poller.delete()?; + Ok(()) }