Replace mio with calloop in the X11 backend

This commit is contained in:
John Nunley 2023-05-31 09:44:42 -07:00 committed by GitHub
parent ba5ad3be13
commit 4ac2006cbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 93 deletions

View file

@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
x11 = ["x11-dl", "mio", "percent-encoding", "xkbcommon-dl/x11"]
x11 = ["x11-dl", "percent-encoding", "xkbcommon-dl/x11"]
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
@ -112,7 +112,6 @@ features = [
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
libc = "0.2.64"
mio = { version = "0.8", features = ["os-ext"], optional = true }
percent-encoding = { version = "2.0", optional = true }
fnv = { version = "1.0.3", optional = true }
sctk = { package = "smithay-client-toolkit", version = "0.17.0", default-features = false, features = ["calloop"], optional = true }

View file

@ -17,24 +17,26 @@ pub(crate) use self::{
pub use self::xdisplay::{XError, XNotSupported};
use calloop::channel::{channel, Channel, Event as ChanResult, Sender};
use calloop::generic::Generic;
use calloop::{Dispatcher, EventLoop as Loop};
use std::{
cell::{Cell, RefCell},
collections::{HashMap, HashSet},
collections::{HashMap, HashSet, VecDeque},
ffi::CStr,
mem::{self, MaybeUninit},
ops::Deref,
os::raw::*,
os::unix::io::RawFd,
ptr,
rc::Rc,
slice,
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
use libc::{self, setlocale, LC_CTYPE};
use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker};
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
use self::{
@ -54,47 +56,7 @@ use crate::{
window::WindowAttributes,
};
const X_TOKEN: Token = Token(0);
const USER_REDRAW_TOKEN: Token = Token(1);
struct WakeSender<T> {
sender: Sender<T>,
waker: Arc<Waker>,
}
struct PeekableReceiver<T> {
recv: Receiver<T>,
first: Option<T>,
}
impl<T> PeekableReceiver<T> {
pub fn from_recv(recv: Receiver<T>) -> Self {
Self { recv, first: None }
}
pub fn has_incoming(&mut self) -> bool {
if self.first.is_some() {
return true;
}
match self.recv.try_recv() {
Ok(v) => {
self.first = Some(v);
true
}
Err(TryRecvError::Empty) => false,
Err(TryRecvError::Disconnected) => {
warn!("Channel was disconnected when checking incoming");
false
}
}
}
pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
if let Some(first) = self.first.take() {
return Ok(first);
}
self.recv.try_recv()
}
}
type X11Source = Generic<RawFd>;
pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
@ -104,31 +66,40 @@ pub struct EventLoopWindowTarget<T> {
root: ffi::Window,
ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>,
redraw_sender: Sender<WindowId>,
device_events: Cell<DeviceEvents>,
_marker: ::std::marker::PhantomData<T>,
}
pub struct EventLoop<T: 'static> {
poll: Poll,
waker: Arc<Waker>,
event_loop: Loop<'static, EventLoopState<T>>,
event_processor: EventProcessor<T>,
redraw_receiver: PeekableReceiver<WindowId>,
user_receiver: PeekableReceiver<T>, //waker.wake needs to be called whenever something gets sent
user_sender: Sender<T>,
target: Rc<RootELW<T>>,
/// The current state of the event loop.
state: EventLoopState<T>,
/// Dispatcher for redraw events.
redraw_dispatcher: Dispatcher<'static, Channel<WindowId>, EventLoopState<T>>,
}
struct EventLoopState<T> {
/// Incoming user events.
user_events: VecDeque<T>,
/// Incoming redraw events.
redraw_events: VecDeque<WindowId>,
}
pub struct EventLoopProxy<T: 'static> {
user_sender: Sender<T>,
waker: Arc<Waker>,
}
impl<T: 'static> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
EventLoopProxy {
user_sender: self.user_sender.clone(),
waker: self.waker.clone(),
}
}
}
@ -237,15 +208,41 @@ impl<T: 'static> EventLoop<T> {
xconn.update_cached_wm_info(root);
let poll = Poll::new().unwrap();
let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap());
// Create an event loop.
let event_loop =
Loop::<EventLoopState<T>>::try_new().expect("Failed to initialize the event loop");
let handle = event_loop.handle();
poll.registry()
.register(&mut SourceFd(&xconn.x11_fd), X_TOKEN, Interest::READABLE)
.unwrap();
// Create the X11 event dispatcher.
let source = X11Source::new(xconn.x11_fd, calloop::Interest::READ, calloop::Mode::Level);
handle
.insert_source(source, |_, _, _| Ok(calloop::PostAction::Continue))
.expect("Failed to register the X11 event dispatcher");
let (user_sender, user_channel) = std::sync::mpsc::channel();
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
// Create a channel for sending user events.
let (user_sender, user_channel) = channel();
handle
.insert_source(user_channel, |ev, _, state| {
if let ChanResult::Msg(user) = ev {
state.user_events.push_back(user);
}
})
.expect("Failed to register the user event channel with the event loop");
// Create a channel for handling redraw requests.
let (redraw_sender, redraw_channel) = channel();
// Create a dispatcher for the redraw channel such that we can dispatch it independent of the
// event loop.
let redraw_dispatcher =
Dispatcher::<_, EventLoopState<T>>::new(redraw_channel, |ev, _, state| {
if let ChanResult::Msg(window_id) = ev {
state.redraw_events.push_back(window_id);
}
});
handle
.register_dispatcher(redraw_dispatcher.clone())
.expect("Failed to register the redraw event channel with the event loop");
let kb_state =
KbdState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) })
@ -260,10 +257,7 @@ impl<T: 'static> EventLoop<T> {
xconn,
wm_delete_window,
net_wm_ping,
redraw_sender: WakeSender {
sender: redraw_sender, // not used again so no clone
waker: waker.clone(),
},
redraw_sender,
device_events: Default::default(),
};
@ -310,20 +304,21 @@ impl<T: 'static> EventLoop<T> {
event_processor.init_device(ffi::XIAllDevices);
EventLoop {
poll,
waker,
event_loop,
event_processor,
redraw_receiver: PeekableReceiver::from_recv(redraw_channel),
user_receiver: PeekableReceiver::from_recv(user_channel),
user_sender,
target,
redraw_dispatcher,
state: EventLoopState {
user_events: VecDeque::new(),
redraw_events: VecDeque::new(),
},
}
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
user_sender: self.user_sender.clone(),
waker: self.waker.clone(),
}
}
@ -372,7 +367,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer
{
while let Ok(event) = this.user_receiver.try_recv() {
while let Some(event) = this.state.user_events.pop_front() {
sticky_exit_callback(
crate::event::Event::UserEvent(event),
&this.target,
@ -390,11 +385,19 @@ impl<T: 'static> EventLoop<T> {
callback,
);
}
// Quickly dispatch all redraw events to avoid buffering them.
while let Ok(event) = this.redraw_dispatcher.as_source_mut().try_recv() {
this.state.redraw_events.push_back(event);
}
// Empty the redraw requests
{
let mut windows = HashSet::new();
while let Ok(window_id) = this.redraw_receiver.try_recv() {
// Empty the channel.
while let Some(window_id) = this.state.redraw_events.pop_front() {
windows.insert(window_id);
}
@ -464,7 +467,6 @@ impl<T: 'static> EventLoop<T> {
}
let mut control_flow = ControlFlow::default();
let mut events = Events::with_capacity(8);
let mut cause = StartCause::Init;
// run the initial loop iteration
@ -475,22 +477,23 @@ impl<T: 'static> EventLoop<T> {
break code;
}
let has_pending = self.event_processor.poll()
|| self.user_receiver.has_incoming()
|| self.redraw_receiver.has_incoming();
|| !self.state.user_events.is_empty()
|| !self.state.redraw_events.is_empty();
if !has_pending {
// Wait until
if let Err(e) = self.poll.poll(&mut events, iter_result.timeout) {
if e.raw_os_error() != Some(libc::EINTR) {
panic!("epoll returned an error: {e:?}");
}
if let Err(error) = self
.event_loop
.dispatch(iter_result.timeout, &mut self.state)
.map_err(std::io::Error::from)
{
break error.raw_os_error().unwrap_or(1);
}
events.clear();
if control_flow == ControlFlow::Wait {
// We don't go straight into executing the event loop iteration, we instead go
// to the start of this loop and check again if there's any pending event. We
// must do this because during the execution of the iteration we sometimes wake
// the mio waker, and if the waker is already awaken before we call poll(),
// the calloop waker, and if the waker is already awaken before we call poll(),
// then poll doesn't block, but it returns immediately. This caused the event
// loop to run continuously even if the control_flow was `Wait`
continue;
@ -544,8 +547,7 @@ impl<T: 'static> EventLoop<T> {
control_flow,
&mut |event, window_target, control_flow| {
if let Event::RedrawRequested(crate::window::WindowId(wid)) = event {
wt.redraw_sender.sender.send(wid).unwrap();
wt.redraw_sender.waker.wake().unwrap();
wt.redraw_sender.send(wid).unwrap();
} else {
callback(event, window_target, control_flow);
}
@ -608,7 +610,6 @@ impl<T: 'static> EventLoopProxy<T> {
self.user_sender
.send(event)
.map_err(|e| EventLoopClosed(e.0))
.map(|_| self.waker.wake().unwrap())
}
}

View file

@ -27,9 +27,9 @@ use crate::{
};
use super::{
ffi, util, EventLoopWindowTarget, ImeRequest, ImeSender, WakeSender, WindowId, XConnection,
XError,
ffi, util, EventLoopWindowTarget, ImeRequest, ImeSender, WindowId, XConnection, XError,
};
use calloop::channel::Sender;
#[derive(Debug)]
pub struct SharedState {
@ -114,7 +114,7 @@ pub(crate) struct UnownedWindow {
cursor_visible: Mutex<bool>,
ime_sender: Mutex<ImeSender>,
pub shared_state: Mutex<SharedState>,
redraw_sender: WakeSender<WindowId>,
redraw_sender: Sender<WindowId>,
}
impl UnownedWindow {
@ -289,10 +289,7 @@ impl UnownedWindow {
cursor_visible: Mutex::new(true),
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
shared_state: SharedState::new(guessed_monitor, &window_attrs),
redraw_sender: WakeSender {
waker: event_loop.redraw_sender.waker.clone(),
sender: event_loop.redraw_sender.sender.clone(),
},
redraw_sender: event_loop.redraw_sender.clone(),
};
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
@ -1594,10 +1591,8 @@ impl UnownedWindow {
#[inline]
pub fn request_redraw(&self) {
self.redraw_sender
.sender
.send(WindowId(self.xwindow as _))
.unwrap();
self.redraw_sender.waker.wake().unwrap();
}
#[inline]