From 5486c3dc930b0d5c02cb28d9f13bf099e027883c Mon Sep 17 00:00:00 2001 From: apricotbucket28 <71973804+apricotbucket28@users.noreply.github.com> Date: Mon, 6 May 2024 17:15:42 -0300 Subject: [PATCH] wayland: Refactor serial usage (#11388) Adds a `SerialTracker` type which helps simplify serial handling. Release Notes: - N/A --- crates/gpui/src/platform/linux/wayland.rs | 1 + .../gpui/src/platform/linux/wayland/client.rs | 42 ++++----- .../gpui/src/platform/linux/wayland/serial.rs | 91 +++++++++++++++++++ 3 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 crates/gpui/src/platform/linux/wayland/serial.rs diff --git a/crates/gpui/src/platform/linux/wayland.rs b/crates/gpui/src/platform/linux/wayland.rs index 89a4851d63..61eb0bf498 100644 --- a/crates/gpui/src/platform/linux/wayland.rs +++ b/crates/gpui/src/platform/linux/wayland.rs @@ -1,6 +1,7 @@ mod client; mod cursor; mod display; +mod serial; mod window; pub(crate) use client::*; diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index d3be43765e..7dfde29921 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -55,6 +55,7 @@ use super::super::{open_uri_internal, read_fd, DOUBLE_CLICK_INTERVAL}; use super::window::{WaylandWindowState, WaylandWindowStatePtr}; use crate::platform::linux::is_within_click_distance; use crate::platform::linux::wayland::cursor::Cursor; +use crate::platform::linux::wayland::serial::{SerialKind, SerialTracker}; use crate::platform::linux::wayland::window::WaylandWindow; use crate::platform::linux::LinuxClient; use crate::platform::PlatformWindow; @@ -124,8 +125,7 @@ impl Globals { } pub(crate) struct WaylandClientState { - serial: u32, // todo(linux): storing a general serial is wrong - pointer_serial: u32, + serial_tracker: SerialTracker, globals: Globals, wl_seat: wl_seat::WlSeat, // todo(linux): multi-seat support wl_pointer: Option, @@ -315,8 +315,7 @@ impl WaylandClient { let cursor = Cursor::new(&conn, &globals, 24); let mut state = Rc::new(RefCell::new(WaylandClientState { - serial: 0, - pointer_serial: 0, + serial_tracker: SerialTracker::new(), globals, wl_seat: seat, wl_pointer: None, @@ -414,7 +413,7 @@ impl LinuxClient for WaylandClient { .map_or(true, |current_style| current_style != style); if need_update { - let serial = state.pointer_serial; + let serial = state.serial_tracker.get(SerialKind::MouseEnter); state.cursor_style = Some(style); if let Some(cursor_shape_device) = &state.cursor_shape_device { @@ -440,7 +439,8 @@ impl LinuxClient for WaylandClient { ) { state.pending_open_uri = Some(uri.to_owned()); let token = activation.get_activation_token(&state.globals.qh, ()); - token.set_serial(state.serial, &state.wl_seat); + let serial = state.serial_tracker.get(SerialKind::MousePress); + token.set_serial(serial, &state.wl_seat); token.set_surface(&window.surface()); token.commit(); } else { @@ -692,7 +692,7 @@ impl Dispatch for WaylandClientStatePtr { impl Dispatch for WaylandClientStatePtr { fn event( - this: &mut Self, + _: &mut Self, wm_base: &xdg_wm_base::XdgWmBase, event: ::Event, _: &(), @@ -700,9 +700,6 @@ impl Dispatch for WaylandClientStatePtr { _: &QueueHandle, ) { if let xdg_wm_base::Event::Ping { serial } = event { - let client = this.get_client(); - let mut state = client.borrow_mut(); - state.serial = serial; wm_base.pong(serial); } } @@ -802,10 +799,7 @@ impl Dispatch for WaylandClientStatePtr { }; state.keymap_state = Some(xkb::State::new(&keymap)); } - wl_keyboard::Event::Enter { - serial, surface, .. - } => { - state.serial = serial; + wl_keyboard::Event::Enter { surface, .. } => { state.keyboard_focused_window = get_window(&mut state, &surface.id()); state.enter_token = Some(()); @@ -814,10 +808,7 @@ impl Dispatch for WaylandClientStatePtr { window.set_focused(true); } } - wl_keyboard::Event::Leave { - serial, surface, .. - } => { - state.serial = serial; + wl_keyboard::Event::Leave { surface, .. } => { let keyboard_focused_window = get_window(&mut state, &surface.id()); state.keyboard_focused_window = None; state.enter_token.take(); @@ -828,14 +819,12 @@ impl Dispatch for WaylandClientStatePtr { } } wl_keyboard::Event::Modifiers { - serial, mods_depressed, mods_latched, mods_locked, group, .. } => { - state.serial = serial; let focused_window = state.keyboard_focused_window.clone(); let Some(focused_window) = focused_window else { return; @@ -853,12 +842,12 @@ impl Dispatch for WaylandClientStatePtr { focused_window.handle_input(input); } wl_keyboard::Event::Key { + serial, key, state: WEnum::Value(key_state), - serial, .. } => { - state.serial = serial; + state.serial_tracker.update(SerialKind::KeyPress, serial); let focused_window = state.keyboard_focused_window.clone(); let Some(focused_window) = focused_window else { @@ -971,7 +960,7 @@ impl Dispatch for WaylandClientStatePtr { surface_y, .. } => { - state.pointer_serial = serial; + state.serial_tracker.update(SerialKind::MouseEnter, serial); state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32))); if let Some(window) = get_window(&mut state, &surface.id()) { @@ -1041,7 +1030,7 @@ impl Dispatch for WaylandClientStatePtr { state: WEnum::Value(button_state), .. } => { - state.serial = serial; + state.serial_tracker.update(SerialKind::MousePress, serial); let button = linux_button_to_gpui(button); let Some(button) = button else { return }; if state.mouse_focused_window.is_none() { @@ -1299,7 +1288,7 @@ impl Dispatch for WaylandClientStatePtr { y, id: data_offer, } => { - state.serial = serial; + state.serial_tracker.update(SerialKind::DataDevice, serial); if let Some(data_offer) = data_offer { let Some(drag_window) = get_window(&mut state, &surface.id()) else { return; @@ -1429,7 +1418,8 @@ impl Dispatch for WaylandClientStatePtr { match event { wl_data_offer::Event::Offer { mime_type } => { if mime_type == FILE_LIST_MIME_TYPE { - data_offer.accept(state.serial, Some(mime_type)); + let serial = state.serial_tracker.get(SerialKind::DataDevice); + data_offer.accept(serial, Some(mime_type)); } } _ => {} diff --git a/crates/gpui/src/platform/linux/wayland/serial.rs b/crates/gpui/src/platform/linux/wayland/serial.rs new file mode 100644 index 0000000000..14d2dea3b3 --- /dev/null +++ b/crates/gpui/src/platform/linux/wayland/serial.rs @@ -0,0 +1,91 @@ +use std::time::Instant; + +use collections::HashMap; + +#[derive(Debug, Hash, PartialEq, Eq)] +pub(crate) enum SerialKind { + DataDevice, + MouseEnter, + MousePress, + KeyPress, +} + +#[derive(Debug)] +struct SerialData { + serial: u32, + time: Instant, +} + +impl SerialData { + fn new(value: u32) -> Self { + Self { + serial: value, + time: Instant::now(), + } + } +} + +#[derive(Debug)] +/// Helper for tracking of different serial kinds. +pub(crate) struct SerialTracker { + serials: HashMap, +} + +impl SerialTracker { + pub fn new() -> Self { + Self { + serials: HashMap::default(), + } + } + + pub fn update(&mut self, kind: SerialKind, value: u32) { + self.serials.insert(kind, SerialData::new(value)); + } + + /// Returns the latest tracked serial of the provided [`SerialKind`] + /// + /// Will return 0 if not tracked. + pub fn get(&self, kind: SerialKind) -> u32 { + self.serials + .get(&kind) + .map(|serial_data| serial_data.serial) + .unwrap_or(0) + } + + /// Returns the newest serial of any of the provided [`SerialKind`] + pub fn get_newest_of(&self, kinds: &[SerialKind]) -> u32 { + kinds + .iter() + .filter_map(|kind| self.serials.get(&kind)) + .max_by_key(|serial_data| serial_data.time) + .map(|serial_data| serial_data.serial) + .unwrap_or(0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serial_tracker() { + let mut tracker = SerialTracker::new(); + + tracker.update(SerialKind::KeyPress, 100); + tracker.update(SerialKind::MousePress, 50); + tracker.update(SerialKind::MouseEnter, 300); + + assert_eq!( + tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]), + 50 + ); + assert_eq!(tracker.get(SerialKind::DataDevice), 0); + + tracker.update(SerialKind::KeyPress, 2000); + assert_eq!(tracker.get(SerialKind::KeyPress), 2000); + assert_eq!( + tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]), + 2000 + ); + } +}