Linux: Rewrite the event loop using calloop (#8314)
This PR unifies the event loop code for Wayland and X11. On Wayland, blocking dispatch is now used. On X11, the invisible window is no longer needed. Release Notes: - N/A --------- Co-authored-by: Dzmitry Malyshau <kvark@fastmail.com> Co-authored-by: Tadeo Kondrak <me@tadeo.ca> Co-authored-by: Mikayla Maki <mikayla@zed.dev> Co-authored-by: julia <julia@zed.dev>
This commit is contained in:
parent
198dfe0097
commit
b76e0d997e
15 changed files with 615 additions and 632 deletions
|
@ -3,10 +3,12 @@ use std::rc::Rc;
|
|||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use smol::Timer;
|
||||
use calloop::timer::{TimeoutAction, Timer};
|
||||
use calloop::LoopHandle;
|
||||
use calloop_wayland_source::WaylandSource;
|
||||
use wayland_backend::client::ObjectId;
|
||||
use wayland_backend::protocol::WEnum;
|
||||
use wayland_client::globals::{registry_queue_init, GlobalListContents};
|
||||
use wayland_client::protocol::wl_callback::WlCallback;
|
||||
use wayland_client::protocol::wl_pointer::AxisRelativeDirection;
|
||||
use wayland_client::{
|
||||
|
@ -16,7 +18,7 @@ use wayland_client::{
|
|||
wl_shm, wl_shm_pool,
|
||||
wl_surface::{self, WlSurface},
|
||||
},
|
||||
Connection, Dispatch, EventQueue, Proxy, QueueHandle,
|
||||
Connection, Dispatch, Proxy, QueueHandle,
|
||||
};
|
||||
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||
wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
|
||||
|
@ -39,18 +41,17 @@ use crate::{
|
|||
ScrollWheelEvent, TouchPhase, WindowOptions,
|
||||
};
|
||||
|
||||
const MIN_KEYCODE: u32 = 8; // used to convert evdev scancode to xkb scancode
|
||||
/// Used to convert evdev scancode to xkb scancode
|
||||
const MIN_KEYCODE: u32 = 8;
|
||||
|
||||
pub(crate) struct WaylandClientStateInner {
|
||||
compositor: Option<wl_compositor::WlCompositor>,
|
||||
buffer: Option<wl_buffer::WlBuffer>,
|
||||
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
||||
compositor: wl_compositor::WlCompositor,
|
||||
wm_base: xdg_wm_base::XdgWmBase,
|
||||
viewporter: Option<wp_viewporter::WpViewporter>,
|
||||
fractional_scale_manager: Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
|
||||
decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
|
||||
windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
|
||||
platform_inner: Rc<LinuxPlatformInner>,
|
||||
wl_seat: Option<wl_seat::WlSeat>,
|
||||
keymap_state: Option<xkb::State>,
|
||||
repeat: KeyRepeat,
|
||||
modifiers: Modifiers,
|
||||
|
@ -59,6 +60,7 @@ pub(crate) struct WaylandClientStateInner {
|
|||
button_pressed: Option<MouseButton>,
|
||||
mouse_focused_window: Option<Rc<WaylandWindowState>>,
|
||||
keyboard_focused_window: Option<Rc<WaylandWindowState>>,
|
||||
loop_handle: Rc<LoopHandle<'static, ()>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -73,24 +75,40 @@ pub(crate) struct KeyRepeat {
|
|||
|
||||
pub(crate) struct WaylandClient {
|
||||
platform_inner: Rc<LinuxPlatformInner>,
|
||||
conn: Arc<Connection>,
|
||||
state: WaylandClientState,
|
||||
event_queue: Mutex<EventQueue<WaylandClientState>>,
|
||||
qh: Arc<QueueHandle<WaylandClientState>>,
|
||||
}
|
||||
|
||||
const WL_SEAT_VERSION: u32 = 4;
|
||||
|
||||
impl WaylandClient {
|
||||
pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>, conn: Arc<Connection>) -> Self {
|
||||
let state = WaylandClientState(Rc::new(RefCell::new(WaylandClientStateInner {
|
||||
compositor: None,
|
||||
buffer: None,
|
||||
wm_base: None,
|
||||
viewporter: None,
|
||||
fractional_scale_manager: None,
|
||||
decoration_manager: None,
|
||||
pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>) -> Self {
|
||||
let conn = Connection::connect_to_env().unwrap();
|
||||
|
||||
let (globals, mut event_queue) = registry_queue_init::<WaylandClientState>(&conn).unwrap();
|
||||
let qh = event_queue.handle();
|
||||
|
||||
globals.contents().with_list(|list| {
|
||||
for global in list {
|
||||
if global.interface == "wl_seat" {
|
||||
globals.registry().bind::<wl_seat::WlSeat, _, _>(
|
||||
global.name,
|
||||
WL_SEAT_VERSION,
|
||||
&qh,
|
||||
(),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner {
|
||||
compositor: globals.bind(&qh, 1..=1, ()).unwrap(),
|
||||
wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
|
||||
viewporter: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
|
||||
windows: Vec::new(),
|
||||
platform_inner: Rc::clone(&linux_platform_inner),
|
||||
wl_seat: None,
|
||||
keymap_state: None,
|
||||
repeat: KeyRepeat {
|
||||
characters_per_second: 16,
|
||||
|
@ -110,41 +128,28 @@ impl WaylandClient {
|
|||
button_pressed: None,
|
||||
mouse_focused_window: None,
|
||||
keyboard_focused_window: None,
|
||||
})));
|
||||
let event_queue: EventQueue<WaylandClientState> = conn.new_event_queue();
|
||||
let qh = event_queue.handle();
|
||||
loop_handle: Rc::clone(&linux_platform_inner.loop_handle),
|
||||
}));
|
||||
|
||||
let source = WaylandSource::new(conn, event_queue);
|
||||
|
||||
let mut state = WaylandClientState(Rc::clone(&state_inner));
|
||||
linux_platform_inner
|
||||
.loop_handle
|
||||
.insert_source(source, move |_, queue, _| {
|
||||
queue.dispatch_pending(&mut state)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
platform_inner: linux_platform_inner,
|
||||
conn,
|
||||
state,
|
||||
event_queue: Mutex::new(event_queue),
|
||||
state: WaylandClientState(state_inner),
|
||||
qh: Arc::new(qh),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Client for WaylandClient {
|
||||
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
|
||||
let display = self.conn.display();
|
||||
let mut eq = self.event_queue.lock();
|
||||
let _registry = display.get_registry(&self.qh, ());
|
||||
|
||||
eq.roundtrip(&mut self.state.clone()).unwrap();
|
||||
|
||||
on_finish_launching();
|
||||
while !self.platform_inner.state.lock().quit_requested {
|
||||
eq.flush().unwrap();
|
||||
eq.dispatch_pending(&mut self.state.clone()).unwrap();
|
||||
if let Some(guard) = self.conn.prepare_read() {
|
||||
guard.read().unwrap();
|
||||
eq.dispatch_pending(&mut self.state.clone()).unwrap();
|
||||
}
|
||||
if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
@ -160,10 +165,8 @@ impl Client for WaylandClient {
|
|||
) -> Box<dyn PlatformWindow> {
|
||||
let mut state = self.state.0.borrow_mut();
|
||||
|
||||
let wm_base = state.wm_base.as_ref().unwrap();
|
||||
let compositor = state.compositor.as_ref().unwrap();
|
||||
let wl_surface = compositor.create_surface(&self.qh, ());
|
||||
let xdg_surface = wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
|
||||
let wl_surface = state.compositor.create_surface(&self.qh, ());
|
||||
let xdg_surface = state.wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
|
||||
let toplevel = xdg_surface.get_toplevel(&self.qh, ());
|
||||
let wl_surface = Arc::new(wl_surface);
|
||||
|
||||
|
@ -200,8 +203,7 @@ impl Client for WaylandClient {
|
|||
wl_surface.frame(&self.qh, wl_surface.clone());
|
||||
wl_surface.commit();
|
||||
|
||||
let window_state = Rc::new(WaylandWindowState::new(
|
||||
&self.conn,
|
||||
let window_state: Rc<WaylandWindowState> = Rc::new(WaylandWindowState::new(
|
||||
wl_surface.clone(),
|
||||
viewport,
|
||||
Arc::new(toplevel),
|
||||
|
@ -217,62 +219,27 @@ impl Client for WaylandClient {
|
|||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, ()> for WaylandClientState {
|
||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientState {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
event: wl_registry::Event,
|
||||
_: &(),
|
||||
_: &GlobalListContents,
|
||||
_: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
) {
|
||||
let mut state = state.0.borrow_mut();
|
||||
if let wl_registry::Event::Global {
|
||||
name, interface, ..
|
||||
} = event
|
||||
{
|
||||
match &interface[..] {
|
||||
"wl_compositor" => {
|
||||
let compositor =
|
||||
registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
|
||||
state.compositor = Some(compositor);
|
||||
match event {
|
||||
wl_registry::Event::Global {
|
||||
name,
|
||||
interface,
|
||||
version: _,
|
||||
} => {
|
||||
if interface.as_str() == "wl_seat" {
|
||||
registry.bind::<wl_seat::WlSeat, _, _>(name, 4, qh, ());
|
||||
}
|
||||
"xdg_wm_base" => {
|
||||
let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
|
||||
state.wm_base = Some(wm_base);
|
||||
}
|
||||
"wl_seat" => {
|
||||
let seat = registry.bind::<wl_seat::WlSeat, _, _>(name, 4, qh, ());
|
||||
state.wl_seat = Some(seat);
|
||||
}
|
||||
"wp_fractional_scale_manager_v1" => {
|
||||
let manager = registry
|
||||
.bind::<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, _, _>(
|
||||
name,
|
||||
1,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
state.fractional_scale_manager = Some(manager);
|
||||
}
|
||||
"wp_viewporter" => {
|
||||
let view_porter =
|
||||
registry.bind::<wp_viewporter::WpViewporter, _, _>(name, 1, qh, ());
|
||||
state.viewporter = Some(view_porter);
|
||||
}
|
||||
"zxdg_decoration_manager_v1" => {
|
||||
// Unstable and optional
|
||||
let decoration_manager = registry
|
||||
.bind::<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, _, _>(
|
||||
name,
|
||||
1,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
state.decoration_manager = Some(decoration_manager);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
wl_registry::Event::GlobalRemove { name: _ } => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,7 +334,7 @@ impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
|
|||
true
|
||||
}
|
||||
});
|
||||
state.platform_inner.state.lock().quit_requested |= state.windows.is_empty();
|
||||
state.platform_inner.loop_signal.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -529,34 +496,29 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
|
|||
let id = state.repeat.current_id;
|
||||
let this = this.clone();
|
||||
|
||||
let timer = Timer::from_duration(delay);
|
||||
let state_ = Rc::clone(&this.0);
|
||||
state
|
||||
.platform_inner
|
||||
.foreground_executor
|
||||
.spawn(async move {
|
||||
let mut wait_time = delay;
|
||||
.loop_handle
|
||||
.insert_source(timer, move |event, _metadata, shared_data| {
|
||||
let state_ = state_.borrow_mut();
|
||||
let is_repeating = id == state_.repeat.current_id
|
||||
&& state_.repeat.current_keysym.is_some()
|
||||
&& state_.keyboard_focused_window.is_some();
|
||||
|
||||
loop {
|
||||
Timer::after(wait_time).await;
|
||||
|
||||
let state = this.0.borrow_mut();
|
||||
let is_repeating = id == state.repeat.current_id
|
||||
&& state.repeat.current_keysym.is_some()
|
||||
&& state.keyboard_focused_window.is_some();
|
||||
|
||||
if !is_repeating {
|
||||
return;
|
||||
}
|
||||
|
||||
state
|
||||
.keyboard_focused_window
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.handle_input(input.clone());
|
||||
|
||||
wait_time = Duration::from_secs(1) / rate;
|
||||
if !is_repeating {
|
||||
return TimeoutAction::Drop;
|
||||
}
|
||||
|
||||
state_
|
||||
.keyboard_focused_window
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.handle_input(input.clone());
|
||||
|
||||
TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
|
||||
})
|
||||
.detach();
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
wl_keyboard::KeyState::Released => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue