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..19a8b913 --- /dev/null +++ b/src/wrappers/connection_poller.rs @@ -0,0 +1,66 @@ +use polling::{Event, Events, Poller}; +use std::io; +use std::os::fd::{AsFd, BorrowedFd}; +use std::time::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); + } + + return Ok(PollStatus::ReadAvailable); + } + + Ok(PollStatus::Nothing) + } + + pub fn delete(self) -> io::Result<()> { + 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..21b50b1d 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 @@ -123,6 +109,8 @@ impl EventLoop { } } + poller.delete()?; + Ok(()) }