mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
Replace mio with calloop in the X11 backend
This commit is contained in:
parent
ba5ad3be13
commit
4ac2006cbc
|
@ -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 }
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue