Implement DPI Usability Upgrades for X11 and Wayland (#1098)

* Fix compile errors

* Use `mio` for the X11 event loop

* Removes `calloop` from the X11 event loop, as the method of draining a
  source using a closure provided to the `calloop::EventLoop` instance
  conflicts with the need to deliver events directly to the callback
  provided to `EventLoop::run`, in order to respond to the value provided by
  `WindowEvent::HiDpiFactorChanged`.

* Implement interactive `HiDpiFactorChanged` event for X11

* Implement interactive `HiDpiFactorChanged` event for Wayland

* Run cargo fmt

* Fix Wayland not processing events from EventQueue

* Backport #981
This commit is contained in:
Murarth 2020-01-04 01:31:23 -05:00 committed by Osspial
parent 6bb7db7c11
commit 7b43b0bc94
12 changed files with 563 additions and 627 deletions

View file

@ -2,11 +2,16 @@ use std::{
cell::RefCell,
collections::VecDeque,
fmt,
io::ErrorKind,
rc::Rc,
sync::{Arc, Mutex},
time::Instant,
time::{Duration, Instant},
};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel::{channel, Receiver, SendError, Sender};
use smithay_client_toolkit::reexports::protocols::unstable::pointer_constraints::v1::client::{
zwp_locked_pointer_v1::ZwpLockedPointerV1, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1,
};
@ -21,15 +26,17 @@ use smithay_client_toolkit::reexports::client::protocol::{
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::ModifiersState,
dpi::{LogicalSize, PhysicalPosition, PhysicalSize},
event::{
DeviceEvent, DeviceId as RootDeviceId, Event, ModifiersState, StartCause, WindowEvent,
},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform_impl::platform::{
sticky_exit_callback, MonitorHandle as PlatformMonitorHandle,
VideoMode as PlatformVideoMode,
sticky_exit_callback, DeviceId as PlatformDeviceId, MonitorHandle as PlatformMonitorHandle,
VideoMode as PlatformVideoMode, WindowId as PlatformWindowId,
},
window::CursorIcon,
window::{CursorIcon, WindowId as RootWindowId},
};
use super::{window::WindowStore, DeviceId, WindowId};
@ -43,43 +50,37 @@ use smithay_client_toolkit::{
Environment,
};
pub struct WindowEventsSink<T> {
buffer: VecDeque<crate::event::Event<T>>,
const KBD_TOKEN: Token = Token(0);
const USER_TOKEN: Token = Token(1);
const EVQ_TOKEN: Token = Token(2);
#[derive(Clone)]
pub struct EventsSink {
sender: Sender<Event<'static, ()>>,
}
impl<T> WindowEventsSink<T> {
pub fn new() -> WindowEventsSink<T> {
WindowEventsSink {
buffer: VecDeque::new(),
}
impl EventsSink {
pub fn new(sender: Sender<Event<'static, ()>>) -> EventsSink {
EventsSink { sender }
}
pub fn send_event(&mut self, evt: crate::event::Event<T>) {
self.buffer.push_back(evt);
pub fn send_event(&self, event: Event<'static, ()>) {
self.sender.send(event).unwrap()
}
pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) {
self.buffer.push_back(crate::event::Event::WindowEvent {
event: evt,
window_id: crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)),
pub fn send_device_event(&self, event: DeviceEvent, device_id: DeviceId) {
self.send_event(Event::DeviceEvent {
event,
device_id: RootDeviceId(PlatformDeviceId::Wayland(device_id)),
});
}
pub fn send_device_event(&mut self, evt: crate::event::DeviceEvent, dev_id: DeviceId) {
self.buffer.push_back(crate::event::Event::DeviceEvent {
event: evt,
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(dev_id)),
pub fn send_window_event(&self, event: WindowEvent<'static>, window_id: WindowId) {
self.send_event(Event::WindowEvent {
event,
window_id: RootWindowId(PlatformWindowId::Wayland(window_id)),
});
}
fn empty_with<F>(&mut self, mut callback: F)
where
F: FnMut(crate::event::Event<T>),
{
for evt in self.buffer.drain(..) {
callback(evt)
}
}
}
pub struct CursorManager {
@ -230,21 +231,17 @@ impl CursorManager {
}
pub struct EventLoop<T: 'static> {
// The loop
inner_loop: ::calloop::EventLoop<()>,
// Poll instance
poll: Poll,
// The wayland display
pub display: Arc<Display>,
// The output manager
pub outputs: OutputMgr,
// Our sink, shared with some handlers, buffering the events
sink: Arc<Mutex<WindowEventsSink<T>>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
// The cursor manager
cursor_manager: Arc<Mutex<CursorManager>>,
// Utility for grabbing the cursor and changing visibility
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
user_sender: ::calloop::channel::Sender<T>,
_kbd_source: ::calloop::Source<::calloop::channel::Channel<crate::event::Event<()>>>,
kbd_channel: Receiver<Event<'static, ()>>,
user_channel: Receiver<T>,
user_sender: Sender<T>,
window_target: RootELW<T>,
}
@ -252,12 +249,12 @@ pub struct EventLoop<T: 'static> {
//
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
pub struct EventLoopProxy<T: 'static> {
user_sender: calloop::channel::Sender<T>,
user_sender: Sender<T>,
}
pub struct EventLoopWindowTarget<T> {
// The event queue
pub evq: RefCell<::calloop::Source<EventQueue>>,
// the event queue
pub evq: RefCell<EventQueue>,
// The window store
pub store: Arc<Mutex<WindowStore>>,
// The cursor manager
@ -284,7 +281,7 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
EventLoopClosed(if let SendError::Disconnected(x) = e {
x
} else {
unreachable!()
@ -298,33 +295,26 @@ impl<T: 'static> EventLoop<T> {
let (display, mut event_queue) = Display::connect_to_env()?;
let display = Arc::new(display);
let sink = Arc::new(Mutex::new(WindowEventsSink::new()));
let store = Arc::new(Mutex::new(WindowStore::new()));
let seats = Arc::new(Mutex::new(Vec::new()));
let inner_loop = ::calloop::EventLoop::new().unwrap();
let poll = Poll::new().unwrap();
let (kbd_sender, kbd_channel) = ::calloop::channel::channel::<crate::event::Event<()>>();
let kbd_sink = sink.clone();
let kbd_source = inner_loop
.handle()
.insert_source(kbd_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(evt) = evt {
let evt = evt.map_nonuser_event().ok().unwrap();
kbd_sink.lock().unwrap().send_event(evt);
}
})
let (kbd_sender, kbd_channel) = channel();
let sink = EventsSink::new(kbd_sender);
poll.register(&kbd_channel, KBD_TOKEN, Ready::readable(), PollOpt::level())
.unwrap();
let pointer_constraints_proxy = Arc::new(Mutex::new(None));
let mut seat_manager = SeatManager {
sink: sink.clone(),
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
sink,
store: store.clone(),
seats: seats.clone(),
kbd_sender,
relative_pointer_manager_proxy: Rc::new(RefCell::new(None)),
pointer_constraints_proxy: pointer_constraints_proxy.clone(),
cursor_manager: Arc::new(Mutex::new(CursorManager::new(pointer_constraints_proxy))),
};
@ -404,39 +394,31 @@ impl<T: 'static> EventLoop<T> {
)
.unwrap();
let source = inner_loop
.handle()
.insert_source(event_queue, |(), &mut ()| {})
poll.register(&event_queue, EVQ_TOKEN, Ready::readable(), PollOpt::level())
.unwrap();
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = channel();
let (user_sender, user_channel) = ::calloop::channel::channel();
let user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
poll.register(
&user_channel,
USER_TOKEN,
Ready::readable(),
PollOpt::level(),
)
.unwrap();
let cursor_manager_clone = cursor_manager.clone();
Ok(EventLoop {
inner_loop,
sink,
pending_user_events,
poll,
display: display.clone(),
outputs: env.outputs.clone(),
_user_source: user_source,
user_sender,
user_channel,
kbd_channel,
cursor_manager,
_kbd_source: kbd_source,
window_target: RootELW {
p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
evq: RefCell::new(source),
evq: RefCell::new(event_queue),
store,
env,
cursor_manager: cursor_manager_clone,
@ -458,7 +440,7 @@ impl<T: 'static> EventLoop<T> {
pub fn run<F>(mut self, callback: F) -> !
where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: 'static + FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
std::process::exit(0);
@ -466,53 +448,60 @@ impl<T: 'static> EventLoop<T> {
pub fn run_return<F>(&mut self, mut callback: F)
where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
// send pending events to the server
self.display.flush().expect("Wayland connection lost.");
let mut control_flow = ControlFlow::default();
let sink = self.sink.clone();
let user_events = self.pending_user_events.clone();
let mut events = Events::with_capacity(8);
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
Event::NewEvents(StartCause::Init),
&self.window_target,
&mut control_flow,
);
loop {
self.post_dispatch_triggers();
// Read events from the event queue
{
let mut evq = get_target(&self.window_target).evq.borrow_mut();
// empty buffer of events
{
let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| {
sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
});
}
// empty user events
{
let mut guard = user_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(
crate::event::Event::UserEvent(evt),
&self.window_target,
&mut control_flow,
&mut callback,
);
evq.dispatch_pending()
.expect("failed to dispatch wayland events");
if let Some(read) = evq.prepare_read() {
if let Err(e) = read.read_events() {
if e.kind() != ErrorKind::WouldBlock {
panic!("failed to read wayland events: {}", e);
}
}
evq.dispatch_pending()
.expect("failed to dispatch wayland events");
}
}
self.post_dispatch_triggers(&mut callback, &mut control_flow);
while let Ok(event) = self.kbd_channel.try_recv() {
let event = event.map_nonuser_event().unwrap();
sticky_exit_callback(event, &self.window_target, &mut control_flow, &mut callback);
}
while let Ok(event) = self.user_channel.try_recv() {
sticky_exit_callback(
Event::UserEvent(event),
&self.window_target,
&mut control_flow,
&mut callback,
);
}
// send Events cleared
{
sticky_exit_callback(
crate::event::Event::MainEventsCleared,
Event::MainEventsCleared,
&self.window_target,
&mut control_flow,
&mut callback,
@ -523,7 +512,7 @@ impl<T: 'static> EventLoop<T> {
{
self.redraw_triggers(|wid, window_target| {
sticky_exit_callback(
crate::event::Event::RedrawRequested(crate::window::WindowId(
Event::RedrawRequested(crate::window::WindowId(
crate::platform_impl::WindowId::Wayland(wid),
)),
window_target,
@ -536,7 +525,7 @@ impl<T: 'static> EventLoop<T> {
// send RedrawEventsCleared
{
sticky_exit_callback(
crate::event::Event::RedrawEventsCleared,
Event::RedrawEventsCleared,
&self.window_target,
&mut control_flow,
&mut callback,
@ -569,24 +558,25 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::Exit => break,
ControlFlow::Poll => {
// non-blocking dispatch
self.inner_loop
.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
self.poll
.poll(&mut events, Some(Duration::from_millis(0)))
.unwrap();
events.clear();
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
Event::NewEvents(StartCause::Poll),
&self.window_target,
&mut control_flow,
);
}
ControlFlow::Wait => {
let timeout = if instant_wakeup {
Some(::std::time::Duration::from_millis(0))
} else {
None
};
self.inner_loop.dispatch(timeout, &mut ()).unwrap();
if !instant_wakeup {
self.poll.poll(&mut events, None).unwrap();
events.clear();
}
callback(
crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
Event::NewEvents(StartCause::WaitCancelled {
start: Instant::now(),
requested_resume: None,
}),
@ -600,29 +590,27 @@ impl<T: 'static> EventLoop<T> {
let duration = if deadline > start && !instant_wakeup {
deadline - start
} else {
::std::time::Duration::from_millis(0)
Duration::from_millis(0)
};
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
self.poll.poll(&mut events, Some(duration)).unwrap();
events.clear();
let now = Instant::now();
if now < deadline {
callback(
crate::event::Event::NewEvents(
crate::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
},
),
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
}),
&self.window_target,
&mut control_flow,
);
} else {
callback(
crate::event::Event::NewEvents(
crate::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
},
),
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
}),
&self.window_target,
&mut control_flow,
);
@ -631,11 +619,7 @@ impl<T: 'static> EventLoop<T> {
}
}
callback(
crate::event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
callback(Event::LoopDestroyed, &self.window_target, &mut control_flow);
}
pub fn primary_monitor(&self) -> MonitorHandle {
@ -687,12 +671,19 @@ impl<T> EventLoop<T> {
)
}
fn post_dispatch_triggers(&mut self) {
let mut sink = self.sink.lock().unwrap();
fn post_dispatch_triggers<F>(&mut self, mut callback: F, control_flow: &mut ControlFlow)
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let window_target = match self.window_target.p {
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!(),
};
let mut callback = |event: Event<'_, T>| {
sticky_exit_callback(event, &self.window_target, control_flow, &mut callback);
};
// prune possible dead windows
{
let mut cleanup_needed = window_target.cleanup_needed.lock().unwrap();
@ -700,41 +691,67 @@ impl<T> EventLoop<T> {
let pruned = window_target.store.lock().unwrap().cleanup();
*cleanup_needed = false;
for wid in pruned {
sink.send_window_event(crate::event::WindowEvent::Destroyed, wid);
callback(Event::WindowEvent {
window_id: crate::window::WindowId(
crate::platform_impl::WindowId::Wayland(wid),
),
event: WindowEvent::Destroyed,
});
}
}
}
// process pending resize/refresh
window_target.store.lock().unwrap().for_each(|window| {
let window_id =
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(window.wid));
if let Some(frame) = window.frame {
if let Some(newsize) = window.newsize {
let (w, h) = newsize;
if let Some((w, h)) = window.newsize {
// mutter (GNOME Wayland) relies on `set_geometry` to reposition window in case
// it overlaps mutter's `bounding box`, so we can't avoid this resize call,
// which calls `set_geometry` under the hood, for now.
frame.resize(w, h);
frame.refresh();
// Don't send resize event downstream if the new size is identical to the
// current one.
if newsize != *window.size {
if (w, h) != *window.size {
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
window.wid,
);
let physical_size = logical_size
.to_physical(window.new_dpi.unwrap_or(window.prev_dpi) as f64);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Resized(physical_size),
});
*window.size = (w, h);
}
}
if let Some(dpi) = window.new_dpi {
let dpi = dpi as f64;
let logical_size = LogicalSize::from(*window.size);
let mut new_inner_size = Some(logical_size.to_physical(dpi));
callback(Event::WindowEvent {
window_id,
event: WindowEvent::HiDpiFactorChanged {
hidpi_factor: dpi,
new_inner_size: &mut new_inner_size,
},
});
if let Some(new_size) = new_inner_size {
let (w, h) = new_size.to_logical(dpi).into();
frame.resize(w, h);
*window.size = (w, h);
}
}
}
if let Some(dpi) = window.new_dpi {
sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
window.wid,
);
}
if window.closed {
sink.send_window_event(crate::event::WindowEvent::CloseRequested, window.wid);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
});
}
if let Some(grab_cursor) = window.grab_cursor {
@ -749,21 +766,27 @@ impl<T> EventLoop<T> {
}
}
fn get_target<T>(target: &RootELW<T>) -> &EventLoopWindowTarget<T> {
match target.p {
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!(),
}
}
/*
* Wayland protocol implementations
*/
struct SeatManager<T: 'static> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
struct SeatManager {
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatManager<T> {
impl SeatManager {
fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) {
use std::cmp::min;
@ -775,7 +798,6 @@ impl<T: 'static> SeatManager<T> {
relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.clone(),
keyboard: None,
touch: None,
kbd_sender: self.kbd_sender.clone(),
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
cursor_manager: self.cursor_manager.clone(),
};
@ -799,10 +821,9 @@ impl<T: 'static> SeatManager<T> {
}
}
struct SeatData<T> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
struct SeatData {
sink: EventsSink,
store: Arc<Mutex<WindowStore>>,
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
pointer: Option<wl_pointer::WlPointer>,
relative_pointer: Option<ZwpRelativePointerV1>,
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
@ -812,7 +833,7 @@ struct SeatData<T> {
cursor_manager: Arc<Mutex<CursorManager>>,
}
impl<T: 'static> SeatData<T> {
impl SeatData {
fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) {
match evt {
wl_seat::Event::Name { .. } => (),
@ -858,7 +879,7 @@ impl<T: 'static> SeatData<T> {
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
self.keyboard = Some(super::keyboard::init_keyboard(
&seat,
self.kbd_sender.clone(),
self.sink.clone(),
self.modifiers_tracker.clone(),
))
}
@ -892,7 +913,7 @@ impl<T: 'static> SeatData<T> {
}
}
impl<T> Drop for SeatData<T> {
impl Drop for SeatData {
fn drop(&mut self) {
if let Some(pointer) = self.pointer.take() {
if pointer.as_ref().version() >= 3 {