wayland: Refactor serial usage (#11388)

Adds a `SerialTracker` type which helps simplify serial handling.

Release Notes:

- N/A
This commit is contained in:
apricotbucket28 2024-05-06 17:15:42 -03:00 committed by GitHub
parent 3018a64a1b
commit 5486c3dc93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 108 additions and 26 deletions

View file

@ -1,6 +1,7 @@
mod client;
mod cursor;
mod display;
mod serial;
mod window;
pub(crate) use client::*;

View file

@ -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<wl_pointer::WlPointer>,
@ -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<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
fn event(
this: &mut Self,
_: &mut Self,
wm_base: &xdg_wm_base::XdgWmBase,
event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
_: &(),
@ -700,9 +700,6 @@ impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
_: &QueueHandle<Self>,
) {
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<wl_keyboard::WlKeyboard, ()> 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<wl_keyboard::WlKeyboard, ()> 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<wl_keyboard::WlKeyboard, ()> 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<wl_keyboard::WlKeyboard, ()> 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<wl_pointer::WlPointer, ()> 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<wl_pointer::WlPointer, ()> 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<wl_data_device::WlDataDevice, ()> 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<wl_data_offer::WlDataOffer, ()> 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));
}
}
_ => {}

View file

@ -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<SerialKind, SerialData>,
}
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
);
}
}