From 18ebd8c5228d3bea454e4b40b20d84183bb9433c Mon Sep 17 00:00:00 2001 From: Nova Date: Sat, 21 Jan 2023 18:07:25 -0500 Subject: [PATCH] feat: input multiplexing --- Cargo.toml | 5 +- src/wayland/panel_item.rs | 337 ++++++++++------------------ src/wayland/seat.rs | 459 +++++++++++++++++++++++++++++++------- src/wayland/surface.rs | 27 +-- src/wayland/xdg_shell.rs | 1 + 5 files changed, 503 insertions(+), 326 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 034d37f..ae5dcf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ mint = "0.5.9" nanoid = "0.4.0" once_cell = "1.12.0" parking_lot = "0.12.1" -portable-atomic = {version = "0.3.0", features = ["float", "std"]} +portable-atomic = {version = "1.0.0", features = ["float", "std"]} rustc-hash = "1.1.0" slab = "0.4.6" tokio = { version = "1", features = ["rt", "signal"] } @@ -28,13 +28,14 @@ send_wrapper = "0.6.0" prisma = "0.1.1" slog = "2.7.0" xkbcommon = { version = "0.5.0", default-features = false, optional = true } -stardust-xr = "0.10.0" +stardust-xr = "0.10.4" directories = "4.0.1" serde = { version = "1.0.145", features = ["derive"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-slog = "0.2.0" global_counter = "0.2.2" +rand = "0.8.5" [dependencies.stereokit] default-features = false diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs index b67b90d..693cbae 100644 --- a/src/wayland/panel_item.rs +++ b/src/wayland/panel_item.rs @@ -1,5 +1,5 @@ use super::{ - seat::{Cursor, KeyboardInfo, SeatData}, + seat::{Cursor, SeatData}, surface::CoreSurface, xdg_shell::{XdgSurfaceData, XdgToplevelData}, SERIAL_COUNTER, @@ -14,8 +14,9 @@ use crate::{ spatial::Spatial, Node, }, + wayland::seat::{KeyboardEvent, PointerEvent}, }; -use color_eyre::eyre::{bail, eyre, Result}; +use color_eyre::eyre::{eyre, Result}; use glam::Mat4; use lazy_static::lazy_static; use mint::Vector2; @@ -28,12 +29,7 @@ use smithay::{ XdgToplevel, EVT_CONFIGURE_BOUNDS_SINCE, EVT_WM_CAPABILITIES_SINCE, }, wayland_server::{ - backend::Credentials, - protocol::{ - wl_pointer::{Axis, ButtonState}, - wl_surface::WlSurface, - }, - Resource, Weak as WlWeak, + backend::Credentials, protocol::wl_surface::WlSurface, Resource, Weak as WlWeak, }, }, wayland::compositor, @@ -47,17 +43,18 @@ lazy_static! { pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { type_name: "panel", aliased_local_signals: vec![ + "apply_cursor_material", "apply_toplevel_material", "configure_toplevel", "set_toplevel_capabilities", - "apply_cursor_material", - "pointer_deactivate", + "pointer_set_active", "pointer_scroll", "pointer_button", "pointer_motion", "keyboard_set_active", - "keyboard_set_keyState", - "keyboard_set_modifiers", + "keyboard_key", + "keyboard_set_keymap_names", + "keyboard_set_keymap_string", "close", ], aliased_local_methods: vec![], @@ -112,14 +109,14 @@ pub enum RecommendedState { pub struct PanelItem { node: Weak, client_credentials: Option, - toplevel_surface: WlWeak, - pub toplevel: WlWeak, + toplevel: WlWeak, pub cursor: Mutex>>, seat_data: SeatData, } impl PanelItem { pub fn create( toplevel: XdgToplevel, + wl_surface: WlSurface, client_credentials: Option, seat_data: SeatData, ) -> (Arc, Arc) { @@ -134,20 +131,14 @@ impl PanelItem { let panel_item = Arc::new(PanelItem { node: Arc::downgrade(&node), client_credentials, - toplevel_surface: toplevel - .data::() - .unwrap() - .xdg_surface_data - .wl_surface - .clone(), toplevel: toplevel.downgrade(), cursor: Mutex::new(None), seat_data, }); - let _ = panel_item + + panel_item .seat_data - .panel_item - .set(Arc::downgrade(&panel_item)); + .new_panel_item(&panel_item, &toplevel, &wl_surface); let item = Item::add_to( &node, @@ -169,20 +160,21 @@ impl PanelItem { "apply_cursor_material", PanelItem::apply_cursor_material_flex, ); - node.add_local_signal("pointer_deactivate", PanelItem::pointer_deactivate_flex); + node.add_local_signal("pointer_set_active", PanelItem::pointer_set_active_flex); node.add_local_signal("pointer_scroll", PanelItem::pointer_scroll_flex); node.add_local_signal("pointer_button", PanelItem::pointer_button_flex); node.add_local_signal("pointer_motion", PanelItem::pointer_motion_flex); + + node.add_local_signal("keyboard_set_active", PanelItem::keyboard_set_active_flex); node.add_local_signal( - "keyboard_activate_string", - PanelItem::keyboard_activate_string_flex, + "keyboard_set_keymap_string", + PanelItem::keyboard_set_keymap_string_flex, ); node.add_local_signal( - "keyboard_activate_names", - PanelItem::keyboard_activate_names_flex, + "keyboard_set_keymap_names", + PanelItem::keyboard_set_keymap_names_flex, ); - node.add_local_signal("keyboard_deactivate", PanelItem::keyboard_deactivate_flex); - node.add_local_signal("keyboard_key_state", PanelItem::keyboard_key_state_flex); + node.add_local_signal("keyboard_key", PanelItem::keyboard_key_flex); if let Some(startup_settings) = panel_item .client_credentials @@ -219,7 +211,7 @@ impl PanelItem { .clone(), ) } - pub fn toplevel_state(&self) -> Option>> { + fn toplevel_state(&self) -> Option>> { Some( self.toplevel .upgrade() @@ -229,7 +221,7 @@ impl PanelItem { .clone(), ) } - fn toplevel_wl_surface(&self) -> Option { + pub fn toplevel_wl_surface(&self) -> Option { self.toplevel_surface_data()?.wl_surface.upgrade().ok() } fn core_surface(&self) -> Option> { @@ -237,6 +229,11 @@ impl PanelItem { data.data_map.get::>().cloned() }) } + fn flush_clients(&self) { + if let Some(core_surface) = self.core_surface() { + core_surface.flush_clients(); + } + } fn apply_toplevel_material_flex( node: &Node, @@ -274,7 +271,7 @@ impl PanelItem { data: &[u8], ) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(cursor) = panel_item.seat_data.cursor() else { return Ok(())}; + let Some(cursor) = panel_item.cursor.lock().as_ref().and_then(|c| c.upgrade().ok()) else { return Ok(())}; let Some(core_surface) = CoreSurface::from_wl_surface(&cursor) else { return Ok(()) }; #[derive(Debug, Deserialize)] @@ -298,130 +295,73 @@ impl PanelItem { Ok(()) } - fn pointer_deactivate_flex( - node: &Node, - _calling_client: Arc, - _data: &[u8], - ) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - if !panel_item.seat_data.pointer_active() { - return Ok(()); - } - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(wl_surface) = core_surface.wl_surface() else { return Ok(()) }; - let Some(pointer) = panel_item.seat_data.pointer() else { return Ok(()) }; - - debug!(?wl_surface, ?pointer, "Pointer deactivate"); - pointer.leave(SERIAL_COUNTER.inc(), &wl_surface); - *panel_item.seat_data.pointer_focus.lock() = None; - pointer.frame(); - core_surface.flush_clients(); - - Ok(()) - } - fn pointer_motion_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(wl_surface) = core_surface.wl_surface() else { return Ok(()) }; - let Some(pointer) = panel_item.seat_data.pointer() else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; + debug!(?toplevel, "Pointer deactivate"); - let Some(pointer_surface_size) = - core_surface.with_data(|data| data.size) else { return Ok(()) }; - - let mut position: Vector2 = deserialize(data)?; - position.x = position.x.clamp(0.0, pointer_surface_size.x as f64); - position.y = position.y.clamp(0.0, pointer_surface_size.y as f64); - debug!(?wl_surface, ?pointer, ?position, "Pointer motion"); - - let mut pointer_focus = panel_item.seat_data.pointer_focus.lock(); - if let Some(old_surface) = pointer_focus - .as_ref() - .and_then(|surf| surf.upgrade().ok()) - .filter(|surf| surf.id() != wl_surface.id()) - { - pointer.leave(SERIAL_COUNTER.inc(), &old_surface); - *pointer_focus = None; - } - if pointer_focus.is_none() { - pointer.enter(SERIAL_COUNTER.inc(), &wl_surface, position.x, position.y); - *pointer_focus = Some(wl_surface.downgrade()); - } else { - pointer.motion(0, position.x, position.y); - } - pointer.frame(); - core_surface.flush_clients(); + panel_item + .seat_data + .pointer_event(&toplevel, PointerEvent::Motion(deserialize(data)?)); + panel_item.flush_clients(); Ok(()) } - fn pointer_button_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - if !panel_item.seat_data.pointer_active() { - return Ok(()); - } - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(pointer) = panel_item.seat_data.pointer() else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; let (button, state): (u32, u32) = deserialize(data)?; - debug!(?pointer, button, state, "Pointer button"); - pointer.button( - SERIAL_COUNTER.inc(), - 0, - button, - match state { - 0 => ButtonState::Released, - 1 => ButtonState::Pressed, - _ => { - bail!("Button state is out of bounds") - } - }, - ); - pointer.frame(); - core_surface.flush_clients(); + debug!(button, state, "Pointer button"); + + panel_item + .seat_data + .pointer_event(&toplevel, PointerEvent::Button { button, state }); + panel_item.flush_clients(); Ok(()) } - fn pointer_scroll_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - if !panel_item.seat_data.pointer_active() { - return Ok(()); - } - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(pointer) = panel_item.seat_data.pointer() else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; - #[derive(Deserialize)] + #[derive(Debug, Deserialize)] struct PointerScrollArgs { axis_continuous: Vector2, axis_discrete: Option>, } let args: Option = deserialize(data)?; - debug!(?pointer, "Pointer scroll"); + debug!(?args, "Pointer scroll"); - match args { - Some(args) => { - pointer.axis(0, Axis::HorizontalScroll, args.axis_continuous.x as f64); - pointer.axis(0, Axis::VerticalScroll, args.axis_continuous.y as f64); - if let Some(axis_discrete_vec) = args.axis_discrete { - pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete_vec.x as i32); - pointer.axis_discrete(Axis::VerticalScroll, axis_discrete_vec.y as i32); - } - } - None => { - pointer.axis_stop(0, Axis::HorizontalScroll); - pointer.axis_stop(0, Axis::VerticalScroll); - } - }; + panel_item.seat_data.pointer_event( + &toplevel, + PointerEvent::Scroll { + axis_continuous: args.as_ref().map(|a| a.axis_continuous), + axis_discrete: args.and_then(|a| a.axis_discrete), + }, + ); + panel_item.flush_clients(); - pointer.frame(); - core_surface.flush_clients(); + Ok(()) + } + fn pointer_set_active_flex( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; + let active: bool = deserialize(data)?; + debug!(?toplevel, active, "Pointer set active"); + + panel_item.seat_data.set_pointer_active(&toplevel, active); + panel_item.flush_clients(); Ok(()) } - fn keyboard_activate_string_flex( + fn keyboard_set_keymap_string_flex( node: &Node, _calling_client: Arc, data: &[u8], @@ -431,10 +371,9 @@ impl PanelItem { Keymap::new_from_string(&context, deserialize(data)?, XKB_KEYMAP_FORMAT_TEXT_V1, 0) .ok_or_else(|| eyre!("Keymap is not valid"))?; - PanelItem::keyboard_activate_flex(node, &keymap) + PanelItem::keyboard_set_keymap_flex(node, &keymap) } - - fn keyboard_activate_names_flex( + fn keyboard_set_keymap_names_flex( node: &Node, _calling_client: Arc, data: &[u8], @@ -460,66 +399,42 @@ impl PanelItem { ) .ok_or_else(|| eyre!("Keymap is not valid"))?; - PanelItem::keyboard_activate_flex(node, &keymap) + PanelItem::keyboard_set_keymap_flex(node, &keymap) } - - fn keyboard_activate_flex(node: &Node, keymap: &Keymap) -> Result<()> { + fn keyboard_set_keymap_flex(node: &Node, keymap: &Keymap) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(wl_surface) = panel_item.toplevel_wl_surface() else { return Ok(()) }; - let Some(keyboard) = panel_item.seat_data.keyboard() else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; + debug!(?toplevel, "Keyboard set keymap"); - let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); - if keyboard_info.is_none() { - debug!(?keyboard, ?wl_surface, "Activate keyboard"); - keyboard.enter(SERIAL_COUNTER.inc(), &wl_surface, vec![]); - keyboard.repeat_info(0, 0); - keyboard_info.replace(KeyboardInfo::new(keymap)); - keyboard_info.as_ref().unwrap().keymap.send(keyboard)?; - core_surface.flush_clients(); - } + panel_item.seat_data.set_keymap(&toplevel, keymap); Ok(()) } - fn keyboard_deactivate_flex( - node: &Node, - _calling_client: Arc, - _data: &[u8], - ) -> Result<()> { - let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(wl_surface) = panel_item.toplevel_wl_surface() else { return Ok(()) }; - let Some(keyboard) = panel_item.seat_data.keyboard() else { return Ok(()) }; - - debug!(?keyboard, ?wl_surface, "Deactivate keyboard"); - - let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); - if keyboard_info.is_some() { - keyboard.leave(SERIAL_COUNTER.inc(), &wl_surface); - *keyboard_info = None; - core_surface.flush_clients(); - } - - Ok(()) - } - - fn keyboard_key_state_flex( + fn keyboard_set_active_flex( node: &Node, _calling_client: Arc, data: &[u8], ) -> Result<()> { let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; - let Some(core_surface) = panel_item.core_surface() else { return Ok(()) }; - let Some(keyboard) = panel_item.seat_data.keyboard() else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; + let active: bool = deserialize(data)?; + debug!(?toplevel, active, "Keyboard set active"); - let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); - if let Some(keyboard_info) = &mut *keyboard_info { - let (key, state): (u32, u32) = deserialize(data)?; - debug!(?keyboard, key, state, "Set keyboard key state"); - keyboard_info.process(key, state, keyboard)?; - core_surface.flush_clients(); - } + panel_item.seat_data.set_keyboard_active(&toplevel, active); + + Ok(()) + } + + fn keyboard_key_flex(node: &Node, _calling_client: Arc, data: &[u8]) -> Result<()> { + let Some(panel_item) = PanelItem::from_node(node) else { return Ok(()) }; + let Ok(toplevel) = panel_item.toplevel.upgrade() else { return Ok(()) }; + let (key, state): (u32, u32) = deserialize(data)?; + debug!(key, state, "Set keyboard key state"); + + panel_item + .seat_data + .keyboard_event(&toplevel, KeyboardEvent::Key { key, state }); Ok(()) } @@ -586,36 +501,24 @@ impl PanelItem { } pub fn commit_toplevel(&self) { - let mapped = self.core_surface().map(|c| c.mapped()).unwrap_or(false); + let mapped_size = self.core_surface().and_then(|c| c.size()); let Some(state) = self.toplevel_state() else { return }; - let Some(surface_data) = self.toplevel_surface_data() else { return }; let mut state = state.lock(); - { - let queued_state = state.queued_state.as_mut().unwrap(); - queued_state.mapped = mapped; - if mapped { - queued_state.size = surface_data - .geometry - .lock() - .as_ref() - .map(|geo| geo.size) - .unwrap_or_else(|| { - self.core_surface() - .unwrap() - .with_data(|data| Vector2::from([data.size.x / 2, data.size.y / 2])) - .unwrap() - }); - } + let mut queued_state = state.queued_state.take().unwrap(); + queued_state.mapped = + mapped_size.is_some() && mapped_size.unwrap().x > 0 && mapped_size.unwrap().y > 0; + if let Some(size) = mapped_size { + queued_state.size = size; } - - debug!(state = ?&*state, "Commit toplevel"); - - let Some(node) = self.node.upgrade() else { return }; - let queued_state = state.queued_state.take().unwrap(); *state = (*queued_state).clone(); state.queued_state = Some(queued_state); - let _ = node.send_remote_signal("commit_toplevel", &serialize(&*state).unwrap()); + debug!(state = ?&state.mapped.then_some(&*state), "Commit toplevel"); + let Some(node) = self.node.upgrade() else { return }; + let _ = node.send_remote_signal( + "commit_toplevel", + &serialize(&state.mapped.then_some(&*state)).unwrap(), + ); } pub fn recommend_toplevel_state(&self, state: RecommendedState) { @@ -633,27 +536,21 @@ impl PanelItem { let cursor_size = surface .and_then(|c| CoreSurface::from_wl_surface(c)) - .and_then(|c| c.with_data(|data| data.size)); + .and_then(|c| c.size()); if let Some(size) = cursor_size { data = serialize((size, (hotspot_x, hotspot_y))).unwrap(); } let _ = node.send_remote_signal("set_cursor", &data); + *self.cursor.lock() = surface.map(|surf| surf.downgrade()); } pub fn on_drop(&self) { - let Ok(toplevel_surface) = self.toplevel_surface.upgrade() else { return; }; - let Some(focused_surface) = self.seat_data.pointer_focused_surface() else { return; }; + let Ok(toplevel) = self.toplevel.upgrade() else { return; }; + self.seat_data.drop_panel_item(&toplevel); debug!("Drop panel item"); - - if focused_surface.id() == toplevel_surface.id() { - let Some(pointer) = self.seat_data.pointer() else { return }; - pointer.leave(SERIAL_COUNTER.inc(), &toplevel_surface); - pointer.frame(); - *self.seat_data.pointer_focus.lock() = None; - } } } impl ItemSpecialization for PanelItem { @@ -662,7 +559,7 @@ impl ItemSpecialization for PanelItem { let cursor_size = cursor .as_ref() .and_then(|c| CoreSurface::from_wl_surface(&c)) - .and_then(|c| c.with_data(|data| data.size)); + .and_then(|c| c.size()); let cursor_hotspot = cursor .and_then(|c| { compositor::with_states(&c, |data| data.data_map.get::>().cloned()) @@ -671,13 +568,9 @@ impl ItemSpecialization for PanelItem { let toplevel_state = self.toplevel_state(); let toplevel_state = toplevel_state.as_ref().map(|state| state.lock()); - serialize(( - id, - ( - toplevel_state.and_then(|state| state.mapped.then_some(state.clone())), - cursor_size.zip(cursor_hotspot), - ), - )) - .unwrap() + let toplevel_state = toplevel_state.and_then(|state| { + (state.mapped && state.size.x > 0 && state.size.y > 0).then_some(state.clone()) + }); + serialize((id, (toplevel_state, cursor_size.zip(cursor_hotspot)))).unwrap() } } diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index c7c6d66..0483942 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -9,35 +9,70 @@ use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; use parking_lot::Mutex; +use rand::{seq::IteratorRandom, thread_rng}; +use rustc_hash::{FxHashMap, FxHashSet}; use smithay::{ input::keyboard::{KeymapFile, ModifiersState}, - reexports::wayland_server::{ - backend::{ClientId, GlobalId}, - protocol::{ - wl_keyboard::{self, KeyState, WlKeyboard}, - wl_pointer::{self, WlPointer}, - wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, - wl_surface::WlSurface, - wl_touch::{self, WlTouch}, + reexports::{ + wayland_protocols::xdg::shell::server::xdg_toplevel::XdgToplevel, + wayland_server::{ + backend::{ClientId, GlobalId, ObjectId}, + protocol::{ + wl_keyboard::{self, KeyState, WlKeyboard}, + wl_pointer::{self, Axis, ButtonState, WlPointer}, + wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, + wl_surface::WlSurface, + wl_touch::{self, WlTouch}, + }, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, + Weak as WlWeak, }, - Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak as WlWeak, }, wayland::compositor, }; use std::{ + collections::VecDeque, ops::Deref, sync::{Arc, Weak}, + time::{Duration, Instant}, }; +use tracing::{debug, warn}; use xkbcommon::xkb::{self, Keymap}; -pub struct Cursor { - pub hotspot: Vector2, +#[derive(Clone)] +pub struct SeatData(Arc); +impl SeatData { + pub fn new(dh: &DisplayHandle, client: ClientId) -> Self { + let seat_data = SeatData(Arc::new(SeatDataInner { + client, + global_id: OnceCell::new(), + panels: Mutex::new(FxHashMap::default()), + pointer: OnceCell::new(), + keyboard: OnceCell::new(), + touch: OnceCell::new(), + })); + + seat_data + .global_id + .set(dh.create_global::(7, seat_data.clone())) + .unwrap(); + + seat_data + } +} +impl Deref for SeatData { + type Target = SeatDataInner; + + fn deref(&self) -> &Self::Target { + &self.0 + } } pub struct KeyboardInfo { - pub keymap: KeymapFile, - pub state: xkb::State, - pub mods: ModifiersState, + keymap: KeymapFile, + state: xkb::State, + mods: ModifiersState, + keys: FxHashSet, } impl KeyboardInfo { pub fn new(keymap: &Keymap) -> Self { @@ -45,9 +80,10 @@ impl KeyboardInfo { state: xkb::State::new(keymap), keymap: KeymapFile::new(keymap, None), mods: ModifiersState::default(), + keys: FxHashSet::default(), } } - pub fn process(&mut self, key: u32, state: u32, keyboard: &WlKeyboard) -> Result<()> { + pub fn process(&mut self, key: u32, state: u32, keyboard: &WlKeyboard) -> Result { let wl_key_state = match state { 0 => KeyState::Released, 1 => KeyState::Pressed, @@ -70,81 +106,343 @@ impl KeyboardInfo { ); } keyboard.key(SERIAL_COUNTER.inc(), 0, key, wl_key_state); - Ok(()) + match wl_key_state { + KeyState::Pressed => { + self.keys.insert(key); + } + KeyState::Released => { + self.keys.remove(&key); + } + _ => unimplemented!(), + } + Ok(self.keys.len()) } } unsafe impl Send for KeyboardInfo {} -#[derive(Clone)] -pub struct SeatData(Arc); -impl SeatData { - pub fn new(dh: &DisplayHandle, client: ClientId) -> Self { - let seat_data = SeatData(Arc::new(SeatDataInner { - client, - global_id: OnceCell::new(), - panel_item: OnceCell::new(), - pointer: OnceCell::new(), - pointer_focus: Mutex::new(None), - keyboard: OnceCell::new(), - keyboard_info: Mutex::new(None), - touch: OnceCell::new(), - })); - - seat_data - .global_id - .set(dh.create_global::(7, seat_data.clone())) - .unwrap(); - - seat_data - } +#[derive(Debug, Clone, Copy)] +pub enum PointerEvent { + Motion(Vector2), + Button { + button: u32, + state: u32, + }, + Scroll { + axis_continuous: Option>, + axis_discrete: Option>, + }, +} +#[derive(Debug, Clone)] +pub enum KeyboardEvent { + Keymap, + Key { key: u32, state: u32 }, } -impl Deref for SeatData { - type Target = SeatDataInner; - fn deref(&self) -> &Self::Target { - &self.0 +const POINTER_EVENT_TIMEOUT: Duration = Duration::from_secs(1); +struct PanelInfo { + panel_item: Weak, + toplevel: WlWeak, + focus: WlWeak, + pointer_queue: Option>, + pointer_latest_event: Instant, + keyboard_queue: Option>, + keyboard_info: Option, +} +impl PanelInfo { + fn new(panel_item: &Arc, toplevel: &XdgToplevel, focus: &WlSurface) -> Self { + PanelInfo { + toplevel: toplevel.downgrade(), + panel_item: Arc::downgrade(panel_item), + focus: focus.downgrade(), + pointer_queue: None, + pointer_latest_event: Instant::now(), + keyboard_queue: None, + keyboard_info: None, + } + } + pub fn set_pointer_active(&mut self, seat_data: &SeatDataInner, active: bool) { + if active && self.pointer_queue.is_none() { + self.pointer_queue.replace(Default::default()); + } + + if !active && self.pointer_queue.is_some() { + self.pointer_queue.take(); + let Ok(focus) = self.focus.upgrade() else {return}; + let Some((pointer, pointer_focus)) = seat_data.pointer.get() else {return}; + if &*pointer_focus.lock() == &Some(self.toplevel.id()) { + pointer.leave(SERIAL_COUNTER.inc(), &focus); + } + } + } + fn handle_pointer_events(&mut self, pointer: &WlPointer, mut locked: bool) -> bool { + let Ok(focus) = self.focus.upgrade() else { return false; }; + let Some(pointer_queue) = self.pointer_queue.as_mut() else { return false; }; + let Some(core_surface) = CoreSurface::from_wl_surface(&focus) else { return false; }; + let Some(focus_size) = core_surface.size() else { return false; }; + + if !pointer_queue.is_empty() { + self.pointer_latest_event = Instant::now(); + } + while let Some(event) = pointer_queue.pop_front() { + match (locked, event) { + (false, PointerEvent::Motion(pos)) => { + pointer.enter( + SERIAL_COUNTER.inc(), + &focus, + pos.x.clamp(0.0, focus_size.x as f64), + pos.y.clamp(0.0, focus_size.y as f64), + ); + locked = true; + } + (true, PointerEvent::Motion(pos)) => { + pointer.motion( + 0, + pos.x.clamp(0.0, focus_size.x as f64), + pos.y.clamp(0.0, focus_size.y as f64), + ); + pointer.frame(); + } + (true, PointerEvent::Button { button, state }) => { + pointer.button( + 0, + 0, + button, + match state { + 0 => ButtonState::Released, + 1 => ButtonState::Pressed, + _ => continue, + }, + ); + pointer.frame(); + } + ( + true, + PointerEvent::Scroll { + axis_continuous, + axis_discrete, + }, + ) => { + if let Some(axis_continuous) = axis_continuous { + pointer.axis(0, Axis::HorizontalScroll, axis_continuous.x as f64); + pointer.axis(0, Axis::VerticalScroll, axis_continuous.y as f64); + } + if let Some(axis_discrete) = axis_discrete { + pointer.axis_discrete(Axis::HorizontalScroll, axis_discrete.x as i32); + pointer.axis_discrete(Axis::VerticalScroll, axis_discrete.y as i32); + } + if axis_discrete.is_none() && axis_continuous.is_none() { + pointer.axis_stop(0, Axis::HorizontalScroll); + pointer.axis_stop(0, Axis::VerticalScroll); + } + pointer.frame(); + } + (locked, event) => { + warn!(locked, ?event, "Invalid pointer event!"); + } + } + } + if self.pointer_latest_event.elapsed() > POINTER_EVENT_TIMEOUT { + pointer.leave(SERIAL_COUNTER.inc(), &focus); + locked = false; + } + + locked + } + pub fn set_keyboard_active(&mut self, seat_data: &SeatDataInner, active: bool) { + if active && self.keyboard_queue.is_none() { + self.keyboard_queue.replace(Default::default()); + } + if !active && self.keyboard_queue.is_some() { + self.keyboard_queue.take(); + let Ok(focus) = self.focus.upgrade() else {return}; + let Some((keyboard, keyboard_focus)) = seat_data.keyboard.get() else {return}; + if &*keyboard_focus.lock() == &Some(self.toplevel.id()) { + keyboard.leave(SERIAL_COUNTER.inc(), &focus); + } + } + } + fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool { + let Ok(focus) = self.focus.upgrade() else { return false; }; + let Some(keyboard_queue) = self.keyboard_queue.as_mut() else { return false; }; + let Some(info) = self.keyboard_info.as_mut() else { return true; }; + + if !locked { + keyboard.enter(0, &focus, vec![]); + keyboard.repeat_info(0, 0); + locked = info.keymap.send(keyboard).is_ok(); + } + while let Some(event) = keyboard_queue.pop_front() { + debug!(locked, ?event, "Process keyboard event"); + match (locked, event) { + (true, KeyboardEvent::Keymap) => { + let _ = info.keymap.send(keyboard); + } + (true, KeyboardEvent::Key { key, state }) => { + if let Ok(key_count) = info.process(key, state, keyboard) { + if key_count == 0 { + keyboard.leave(SERIAL_COUNTER.inc(), &focus); + return false; + } + } + } + (locked, event) => { + warn!(locked, ?event, "Invalid keyboard event!"); + } + } + } + locked } } pub struct SeatDataInner { client: ClientId, global_id: OnceCell, - pub panel_item: OnceCell>, - pointer: OnceCell, - pub pointer_focus: Mutex>>, - keyboard: OnceCell, - pub keyboard_info: Mutex>, + panels: Mutex>, + pointer: OnceCell<(WlPointer, Mutex>)>, + keyboard: OnceCell<(WlKeyboard, Mutex>)>, touch: OnceCell, } impl SeatDataInner { - pub fn pointer(&self) -> Option<&WlPointer> { - self.pointer.get() + // pub fn set_focus(&self, toplevel: &WlSurface, focus: &WlSurface) { + // if let Some(panel_info) = self.panels.lock().get_mut(&toplevel.id()) { + // panel_info.focus = focus.downgrade(); + // match panel_info.pointer_queue.back() { + // None => (), + // Some(&PointerEvent::Leave) => (), + // _ => panel_info.pointer_queue.push_back(PointerEvent::Leave), + // }; + // match panel_info.keyboard_queue.back() { + // None => (), + // Some(&KeyboardEvent::Leave) => (), + // _ => panel_info.keyboard_queue.push_back(KeyboardEvent::Leave), + // }; + // } + // } + pub fn set_pointer_active(&self, toplevel: &XdgToplevel, active: bool) { + let mut panels = self.panels.lock(); + let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return}; + panel_info.set_pointer_active(self, active); } - pub fn pointer_active(&self) -> bool { - self.pointer_focus.lock().is_some() + pub fn set_keyboard_active(&self, toplevel: &XdgToplevel, active: bool) { + let mut panels = self.panels.lock(); + let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return}; + panel_info.set_keyboard_active(self, active); } - pub fn pointer_focused_surface(&self) -> Option { - self.pointer_focus - .lock() - .as_ref() - .and_then(|focus| focus.upgrade().ok()) - } - pub fn keyboard(&self) -> Option<&WlKeyboard> { - self.keyboard.get() - } - #[allow(dead_code)] - pub fn touch(&self) -> Option<&WlTouch> { - self.touch.get() + pub fn set_keymap(&self, toplevel: &XdgToplevel, keymap: &Keymap) { + let mut panels = self.panels.lock(); + let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return}; + panel_info.keyboard_info.replace(KeyboardInfo::new(keymap)); + + let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return}; + let Some((_, focus)) = self.keyboard.get() else {return}; + let Some(id) = &*focus.lock() else {return}; + if id == &toplevel.id() { + keyboard_queue.push_back(KeyboardEvent::Keymap); + } } - pub fn cursor(&self) -> Option { - self.panel_item - .get()? - .upgrade()? - .cursor + pub fn pointer_event(&self, toplevel: &XdgToplevel, event: PointerEvent) { + let mut panels = self.panels.lock(); + let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return}; + let Some(pointer_queue) = panel_info.pointer_queue.as_mut() else {return}; + pointer_queue.push_back(event); + drop(panels); + self.handle_pointer_events(); + } + pub fn keyboard_event(&self, toplevel: &XdgToplevel, event: KeyboardEvent) { + let mut panels = self.panels.lock(); + let Some(panel_info) = panels.get_mut(&toplevel.id()) else {return}; + let Some(keyboard_queue) = panel_info.keyboard_queue.as_mut() else {return}; + keyboard_queue.push_back(event); + drop(panels); + self.handle_keyboard_events(); + } + + fn handle_pointer_events(&self) { + let mut panels = self.panels.lock(); + let Some((pointer, pointer_focus)) = self.pointer.get() else {return}; + let mut pointer_focus = pointer_focus.lock(); + + loop { + let locked = pointer_focus.is_some(); + // Pick a pointer to focus on if there is none + if pointer_focus.is_none() { + *pointer_focus = panels + .iter() + .filter(|(_k, v)| v.pointer_queue.is_some()) + .filter(|(_k, v)| !v.pointer_queue.as_ref().unwrap().is_empty()) + .map(|(k, _v)| k) + .choose(&mut thread_rng()) + .cloned(); + } + if pointer_focus.is_none() { + // If there's still none, guess we're done with pointer events for the time being + break; + } + let Some(panel_info) = panels.get_mut(pointer_focus.as_ref().unwrap()) else {break}; + if panel_info.handle_pointer_events(pointer, locked) { + // We haven't gotten to a point where we can switch the focus + break; + } else { + pointer_focus.take(); + } + } + } + fn handle_keyboard_events(&self) { + let mut panels = self.panels.lock(); + let Some((keyboard, keyboard_focus)) = self.keyboard.get() else {return}; + let mut keyboard_focus = keyboard_focus.lock(); + loop { + let locked = keyboard_focus.is_some(); + // Pick a keyboard to focus on if there is none + if keyboard_focus.is_none() { + *keyboard_focus = panels + .iter() + .filter(|(_k, v)| v.keyboard_info.is_some()) + .filter(|(_k, v)| v.keyboard_queue.is_some()) + .filter(|(_k, v)| !v.keyboard_queue.as_ref().unwrap().is_empty()) + .map(|(k, _v)| k) + .choose(&mut thread_rng()) + .cloned(); + } + if keyboard_focus.is_none() { + // If there's still none, guess we're done with keyboard events for the time being + break; + } + let Some(panel_info) = panels.get_mut(keyboard_focus.as_ref().unwrap()) else {break}; + if panel_info.handle_keyboard_events(keyboard, locked) { + // We haven't gotten to a point where we can switch the focus + break; + } else { + keyboard_focus.take(); + } + } + } + + pub fn new_panel_item( + &self, + panel_item: &Arc, + toplevel: &XdgToplevel, + focus: &WlSurface, + ) { + self.panels .lock() - .as_ref() - .and_then(|c| c.upgrade().ok()) + .insert(toplevel.id(), PanelInfo::new(panel_item, toplevel, focus)); + } + pub fn drop_panel_item(&self, toplevel: &XdgToplevel) { + self.panels.lock().remove(&toplevel.id()); + if let Some((_, pointer_focus)) = self.pointer.get() { + let mut pointer_focus = pointer_focus.lock(); + if *pointer_focus == Some(toplevel.id()) { + pointer_focus.take(); + } + } + if let Some((_, keyboard_focus)) = self.keyboard.get() { + let mut keyboard_focus = keyboard_focus.lock(); + if *keyboard_focus == Some(toplevel.id()) { + keyboard_focus.take(); + } + } } } impl Drop for SeatDataInner { @@ -184,19 +482,20 @@ impl Dispatch for WaylandState { _state: &mut WaylandState, _client: &Client, _resource: &WlSeat, - request: ::Request, + request: wl_seat::Request, data: &SeatData, _dh: &DisplayHandle, data_init: &mut DataInit<'_, WaylandState>, ) { match request { wl_seat::Request::GetPointer { id } => { - let _ = data.0.pointer.set(data_init.init(id, data.clone())); + let pointer = data_init.init(id, data.clone()); + let _ = data.0.pointer.set((pointer, Mutex::new(None))); } wl_seat::Request::GetKeyboard { id } => { let keyboard = data_init.init(id, data.clone()); keyboard.repeat_info(0, 0); - let _ = data.0.keyboard.set(keyboard); + let _ = data.0.keyboard.set((keyboard, Mutex::new(None))); } wl_seat::Request::GetTouch { id } => { let _ = data.0.touch.set(data_init.init(id, data.clone())); @@ -207,6 +506,9 @@ impl Dispatch for WaylandState { } } +pub struct Cursor { + pub hotspot: Vector2, +} impl Dispatch for WaylandState { fn request( state: &mut WaylandState, @@ -241,10 +543,13 @@ impl Dispatch for WaylandState { }) } - if let Some(panel_item) = seat_data.panel_item.get().and_then(|i| i.upgrade()) { - panel_item.set_cursor(surface.as_ref(), hotspot_x, hotspot_y); - *panel_item.cursor.lock() = surface.as_ref().map(|surf| surf.downgrade()); - } + let Some((_, focus)) = seat_data.pointer.get() else {return}; + let focus = focus.lock(); + let Some(id) = &*focus else {return}; + let panels = seat_data.panels.lock(); + let Some(panel_info) = panels.get(&id) else {return}; + let Some(panel_item) = panel_info.panel_item.upgrade() else {return}; + panel_item.set_cursor(surface.as_ref(), hotspot_x, hotspot_y); } wl_pointer::Request::Release => (), _ => unreachable!(), diff --git a/src/wayland/surface.rs b/src/wayland/surface.rs index 85835b4..0c93b80 100644 --- a/src/wayland/surface.rs +++ b/src/wayland/surface.rs @@ -174,21 +174,6 @@ impl CoreSurface { if let Some(material_offset) = self.material_offset.lock().delta() { sk_mat.set_queue_offset(sk, *material_offset as i32); } - // if let Some(geometry) = self.geometry.lock().delta().cloned().unwrap_or_default() { - // let buffer_size = renderer_surface_state.buffer_size().unwrap(); - // let surface_size = dbg!(vec2(buffer_size.w as f32, buffer_size.h as f32)); - // let geometry_origin = - // dbg!(vec2(geometry.origin.x as f32, geometry.origin.y as f32)); - // let geometry_size = dbg!(vec2(geometry.size.x as f32, geometry.size.y as f32)); - // sk_mat.set_parameter( - // "uv_offset", - // &Vector2::from(dbg!(geometry_origin / surface_size)), - // ); - // sk_mat.set_parameter( - // "uv_scale", - // &Vector2::from(dbg!(geometry_size / surface_size)), - // ); - // } let surface_size = renderer_surface_state.surface_size().unwrap(); let new_mapped_data = CoreSurfaceData { @@ -196,7 +181,6 @@ impl CoreSurface { wl_tex: Some(SendWrapper::new(smithay_tex)), }; *mapped_data = Some(new_mapped_data); - // } }); self.apply_surface_materials(); @@ -209,10 +193,6 @@ impl CoreSurface { ); } - pub fn mapped(&self) -> bool { - self.mapped_data.lock().is_some() - } - pub fn set_material_offset(&self, material_offset: u32) { *self.material_offset.lock().value_mut() = material_offset; } @@ -248,11 +228,8 @@ impl CoreSurface { .map(|wl_surface| compositor::with_states(&wl_surface, f)) } - pub fn with_data(&self, f: F) -> Option - where - F: FnOnce(&mut CoreSurfaceData) -> T, - { - self.mapped_data.lock().as_mut().map(f) + pub fn size(&self) -> Option> { + self.mapped_data.lock().as_ref().map(|d| d.size) } pub fn flush_clients(&self) { diff --git a/src/wayland/xdg_shell.rs b/src/wayland/xdg_shell.rs index 7752aa2..a354acc 100644 --- a/src/wayland/xdg_shell.rs +++ b/src/wayland/xdg_shell.rs @@ -238,6 +238,7 @@ impl Dispatch for WaylandState { let (node, item) = PanelItem::create( toplevel, + data.wl_surface.upgrade().unwrap(), client.get_credentials(&state.display_handle).ok(), state.seats.get(&client.id()).unwrap().clone(), );