From 1f66d7dee424a611a7f9eb45e50b9aac48ecd923 Mon Sep 17 00:00:00 2001 From: Nova Date: Sun, 27 Aug 2023 17:13:20 -0400 Subject: [PATCH] fix(wayland): new API --- src/nodes/data.rs | 44 ++++++++++++++++- src/nodes/items/panel.rs | 97 +++++++++---------------------------- src/wayland/seat.rs | 54 +++++++++++++++++---- src/wayland/xdg_shell.rs | 101 +++++++++++++++++++-------------------- src/wayland/xwayland.rs | 34 +++++++------ 5 files changed, 178 insertions(+), 152 deletions(-) diff --git a/src/nodes/data.rs b/src/nodes/data.rs index 74dee2c..eb5a490 100644 --- a/src/nodes/data.rs +++ b/src/nodes/data.rs @@ -7,15 +7,22 @@ use crate::core::node_collections::LifeLinkedNodeMap; use crate::core::registry::Registry; use crate::nodes::fields::{find_field, FIELD_ALIAS_INFO}; use crate::nodes::spatial::find_spatial_parent; -use color_eyre::eyre::{ensure, eyre, Result}; +use color_eyre::eyre::{bail, ensure, eyre, Result}; use glam::vec3a; +use lazy_static::lazy_static; use mint::{Quaternion, Vector3}; use nanoid::nanoid; +use parking_lot::Mutex; +use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use stardust_xr::schemas::flex::{deserialize, flexbuffers, serialize}; use stardust_xr::values::Transform; use std::sync::{Arc, Weak}; +lazy_static! { + pub static ref KEYMAPS: Mutex> = Mutex::new(FxHashMap::default()); +} + static PULSE_SENDER_REGISTRY: Registry = Registry::new(); pub static PULSE_RECEIVER_REGISTRY: Registry = Registry::new(); @@ -231,6 +238,8 @@ pub fn create_interface(client: &Arc) -> Result<()> { let node = Node::create(client, "", "data", false); node.add_local_signal("create_pulse_sender", create_pulse_sender_flex); node.add_local_signal("create_pulse_receiver", create_pulse_receiver_flex); + node.add_local_method("register_keymap", register_keymap_flex); + node.add_local_method("get_keymap", get_keymap_flex); node.add_to_scenegraph().map(|_| ()) } @@ -286,3 +295,36 @@ pub fn create_pulse_receiver_flex( PulseReceiver::add_to(&node, field, mask)?; Ok(()) } + +pub fn register_keymap_flex( + _node: &Node, + _calling_client: Arc, + message: Message, +) -> Result { + let keymap: String = deserialize(message.as_ref())?; + let mut keymaps = KEYMAPS.lock(); + if let Some(found_keymap_id) = keymaps + .iter() + .filter(|(_k, v)| *v == &keymap) + .map(|(k, _v)| k) + .last() + { + return Ok(serialize(found_keymap_id)?.into()); + } + + let generated_id = nanoid!(); + keymaps.insert(generated_id.clone(), keymap); + + Ok(serialize(generated_id)?.into()) +} +pub fn get_keymap_flex( + _node: &Node, + _calling_client: Arc, + message: Message, +) -> Result { + let keymap_id: &str = deserialize(message.as_ref())?; + let keymaps = KEYMAPS.lock(); + let Some(keymap) = keymaps.get(keymap_id) else {bail!("Could not find keymap. Try registering it")}; + + Ok(serialize(keymap)?.into()) +} diff --git a/src/nodes/items/panel.rs b/src/nodes/items/panel.rs index d0970e3..8e070c8 100644 --- a/src/nodes/items/panel.rs +++ b/src/nodes/items/panel.rs @@ -15,12 +15,12 @@ use glam::Mat4; use lazy_static::lazy_static; use mint::Vector2; use nanoid::nanoid; +use rustc_hash::FxHashMap; use serde::{ de::{Deserializer, Error, SeqAccess, Visitor}, ser::Serializer, Deserialize, Serialize, }; -use serde_repr::{Deserialize_repr, Serialize_repr}; use stardust_xr::schemas::flex::{deserialize, serialize}; use std::sync::{Arc, Weak}; use tracing::debug; @@ -29,20 +29,16 @@ lazy_static! { pub static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { type_name: "panel", aliased_local_signals: vec![ + "start_data", "apply_surface_material", "close_toplevel", "auto_size_toplevel", - "set_toplevel_size_changed", - "set_toplevel_state", - "set_toplevel_tiling", - "set_toplevel_bounds", - "set_maximize_enabled", - "set_minimize_enabled", - "set_fullscreen_enabled", - "set_window_menu_enabled", - "pointer_scroll", - "pointer_button", + "set_toplevel_size", + "set_toplevel_focused_visuals", "pointer_motion", + "pointer_button", + "pointer_scroll", + "keyboard_keymap", "keyboard_key", ], aliased_local_methods: vec![], @@ -50,8 +46,7 @@ lazy_static! { "toplevel_parent_changed", "toplevel_title_changed", "toplevel_app_id_changed", - "toplevel_window_menu", - "recommend_toplevel_state", + "toplevel_fullscreen_active", "toplevel_move_request", "toplevel_resize_request", "toplevel_size_changed", @@ -155,40 +150,13 @@ pub struct ToplevelInfo { pub logical_rectangle: Geometry, } -#[repr(u32)] -#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)] -pub enum ToplevelState { - Floating, - Minimized, - UnMinimized, - Maximized, - UnMaximized, - Fullscreen, - UnFullscreen, -} - /// Data on positioning a child #[derive(Debug, Clone, Serialize)] pub struct ChildInfo { - pub uid: String, pub parent: SurfaceID, pub geometry: Geometry, } -#[derive(Debug, Clone, Deserialize_repr)] -#[repr(u32)] -pub enum Edge { - None = 0, - Top = 1, - Bottom = 2, - Left = 4, - TopLeft = 5, - BottomLeft = 6, - Right = 8, - TopRight = 9, - BottomRight = 10, -} - /// The init data for the panel item. #[derive(Debug, Clone, Serialize)] pub struct PanelItemInitData { @@ -197,7 +165,7 @@ pub struct PanelItemInitData { /// Size of the toplevel surface in pixels. pub toplevel: ToplevelInfo, /// Vector of childs that already exist - pub children: Vec, + pub children: FxHashMap, /// The surface, if any, that has exclusive input to the pointer. pub pointer_grab: Option, /// The surface, if any, that has exclusive input to the keyboard. @@ -223,8 +191,7 @@ pub trait Backend: Send + Sync + 'static { scroll_steps: Option>, ); - fn keyboard_keymap(&self, surface: &SurfaceID, keymap_id: &str); - fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool); + fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec); } pub fn panel_item_from_node(node: &Node) -> Option> { @@ -243,7 +210,7 @@ pub struct PanelItem { pub backend: Box, } impl PanelItem { - pub fn create(backend: Box, pid: Option) -> (Arc, Arc>) { + pub fn create(backend: Box, pid: Option) -> Arc> { debug!(?pid, "Create panel item"); let startup_settings = pid @@ -294,10 +261,9 @@ impl PanelItem { node.add_local_signal("pointer_button", Self::pointer_button_flex); node.add_local_signal("pointer_scroll", Self::pointer_scroll_flex); - node.add_local_signal("keyboard_keymap", Self::keyboard_keymap_flex); - node.add_local_signal("keyboard_key", Self::keyboard_key_flex); + node.add_local_signal("keyboard_key", Self::keyboard_keys_flex); - (node, panel_item) + panel_item } pub fn drop_toplevel(&self) { let Some(node) = self.node.upgrade() else {return}; @@ -319,10 +285,6 @@ impl PanelItem { let Some(node) = self.node.upgrade() else {return}; let _ = node.send_remote_signal("toplevel_app_id_changed", serialize(app_id).unwrap()); } - pub fn toplevel_window_menu(&self, offset: Vector2) { - let Some(node) = self.node.upgrade() else {return}; - let _ = node.send_remote_signal("toplevel_window_menu", serialize(offset).unwrap()); - } pub fn toplevel_fullscreen_active(&self, active: bool) { let Some(node) = self.node.upgrade() else {return}; let _ = node.send_remote_signal("toplevel_fullscreen_active", serialize(active).unwrap()); @@ -348,9 +310,9 @@ impl PanelItem { let _ = node.send_remote_signal("set_cursor", serialize(geometry).unwrap()); } - pub fn new_child(&self, info: ChildInfo) { + pub fn new_child(&self, uid: &str, info: ChildInfo) { let Some(node) = self.node.upgrade() else {return}; - let _ = node.send_remote_signal("new_child", serialize(info).unwrap()); + let _ = node.send_remote_signal("new_child", serialize((uid, info)).unwrap()); } pub fn reposition_child(&self, uid: &str, geometry: Geometry) { let Some(node) = self.node.upgrade() else {return}; @@ -460,29 +422,17 @@ impl PanelItem { Ok(()) } - fn keyboard_keymap_flex( + fn keyboard_keys_flex( node: &Node, _calling_client: Arc, message: Message, ) -> Result<()> { let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; - let (surface_id, keymap): (SurfaceID, &str) = deserialize(message.as_ref())?; - debug!(?surface_id, keymap, "Set keyboard keymap"); + let (surface_id, keymap_id, keys): (SurfaceID, &str, Vec) = + deserialize(message.as_ref())?; + debug!(?keys, "Set keyboard key state"); - panel_item.keyboard_keymap(&surface_id, keymap); - - Ok(()) - } - fn keyboard_key_flex( - node: &Node, - _calling_client: Arc, - message: Message, - ) -> Result<()> { - let Some(panel_item) = panel_item_from_node(node) else { return Ok(()) }; - let (surface_id, key, state): (SurfaceID, u32, u32) = deserialize(message.as_ref())?; - debug!(key, state, "Set keyboard key state"); - - panel_item.keyboard_key(&surface_id, key, state == 0); + panel_item.keyboard_keys(&surface_id, keymap_id, keys); Ok(()) } @@ -540,11 +490,8 @@ impl Backend for PanelItem { .pointer_scroll(surface, scroll_distance, scroll_steps) } - fn keyboard_keymap(&self, surface: &SurfaceID, keymap_id: &str) { - self.backend.keyboard_keymap(surface, keymap_id) - } - fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) { - self.backend.keyboard_key(surface, key, state) + fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec) { + self.backend.keyboard_keys(surface, keymap_id, keys) } } impl Drop for PanelItem { diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index 2217f9a..9554d2b 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -7,7 +7,7 @@ use crate::{ core::task, nodes::items::panel::{Backend, Geometry, PanelItem}, }; -use color_eyre::eyre::Result; +use color_eyre::eyre::{bail, eyre, Result}; use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; @@ -15,7 +15,7 @@ use parking_lot::Mutex; use rand::{seq::IteratorRandom, thread_rng}; use rustc_hash::{FxHashMap, FxHashSet}; use smithay::{ - input::keyboard::ModifiersState, + input::keyboard::{KeymapFile, ModifiersState}, reexports::wayland_server::{ backend::{ClientId, GlobalId, ObjectId}, protocol::{ @@ -36,7 +36,7 @@ use std::{ }; use tokio::sync::watch; use tracing::{debug, warn}; -use xkbcommon::xkb::{self, Context, Keymap}; +use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; pub fn handle_cursor( panel_item: &Arc>, @@ -53,14 +53,18 @@ pub fn handle_cursor( } pub struct KeyboardInfo { + keymap_string: String, + keymap: KeymapFile, state: xkb::State, mods: ModifiersState, keys: FxHashSet, } impl KeyboardInfo { - pub fn new(keymap: &Keymap) -> Self { + pub fn new(keymap_string: String, keymap: &Keymap) -> Self { KeyboardInfo { + keymap_string, state: xkb::State::new(keymap), + keymap: KeymapFile::new(keymap), mods: ModifiersState::default(), keys: FxHashSet::default(), } @@ -116,6 +120,7 @@ pub enum PointerEvent { } #[derive(Debug, Clone)] pub enum KeyboardEvent { + Keymap, Key { key: u32, state: u32 }, } @@ -126,7 +131,7 @@ struct SurfaceInfo { pointer_queue: VecDeque, pointer_latest_event: Instant, keyboard_queue: VecDeque, - keyboard_info: KeyboardInfo, + keyboard_info: Option, } impl SurfaceInfo { fn new(wl_surface: &WlSurface, cursor_sender: watch::Sender>) -> Self { @@ -136,9 +141,7 @@ impl SurfaceInfo { pointer_queue: VecDeque::new(), pointer_latest_event: Instant::now(), keyboard_queue: VecDeque::new(), - keyboard_info: KeyboardInfo::new( - &Keymap::new_from_names(&Context::new(0), "", "", "", "", None, 0).unwrap(), - ), + keyboard_info: None, } } fn flush(&self) { @@ -233,20 +236,25 @@ impl SurfaceInfo { locked } - fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, locked: bool) -> bool { + fn handle_keyboard_events(&mut self, keyboard: &WlKeyboard, mut locked: bool) -> bool { let Ok(focus) = self.wl_surface.upgrade() else { return false; }; + let Some(info) = self.keyboard_info.as_mut() else { return true; }; if !locked { keyboard.enter(0, &focus, vec![]); if keyboard.version() >= wl_keyboard::EVT_REPEAT_INFO_SINCE { keyboard.repeat_info(0, 0); } + locked = info.keymap.send(keyboard).is_ok(); } while let Some(event) = self.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) = self.keyboard_info.process(key, state, keyboard) { + if let Ok(key_count) = info.process(key, state, keyboard) { if key_count == 0 { keyboard.leave(SERIAL_COUNTER.inc(), &focus); return false; @@ -290,6 +298,31 @@ impl SeatData { seat_data } + pub fn set_keymap(&self, keymap_str: String, surfaces: Vec) -> Result<()> { + let context = xkb::Context::new(0); + let keymap = + Keymap::new_from_string(&context, keymap_str.clone(), XKB_KEYMAP_FORMAT_TEXT_V1, 0) + .ok_or_else(|| eyre!("Keymap is not valid"))?; + let mut panels = self.surfaces.lock(); + let Some((_, focus)) = self.keyboard.get() else {bail!("Could not get keyboard")}; + for surface in surfaces { + let Some(surface_info) = panels.get_mut(&surface.id()) else {continue}; + if let Some(keyboard_info) = &mut surface_info.keyboard_info { + if &keyboard_info.keymap_string == &keymap_str { + continue; + } + } + surface_info + .keyboard_info + .replace(KeyboardInfo::new(keymap_str.clone(), &keymap)); + + if *focus.lock() == surface.id() { + surface_info.keyboard_queue.push_back(KeyboardEvent::Keymap); + } + } + Ok(()) + } + pub fn pointer_event(&self, surface: &WlSurface, event: PointerEvent) { let mut surfaces = self.surfaces.lock(); let Some(surface_info) = surfaces.get_mut(&surface.id()) else {return}; @@ -345,6 +378,7 @@ impl SeatData { if keyboard_focus.is_null() { *keyboard_focus = surfaces .iter() + .filter(|(_k, v)| v.keyboard_info.is_some()) .filter(|(_k, v)| !v.keyboard_queue.is_empty()) .map(|(k, _v)| k) .choose(&mut thread_rng()) diff --git a/src/wayland/xdg_shell.rs b/src/wayland/xdg_shell.rs index 172f4d0..2e785ae 100644 --- a/src/wayland/xdg_shell.rs +++ b/src/wayland/xdg_shell.rs @@ -6,11 +6,11 @@ use super::{ }; use crate::{ nodes::{ + data::KEYMAPS, drawable::model::ModelPart, items::panel::{ Backend, ChildInfo, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo, }, - Node, }, wayland::seat::handle_cursor, }; @@ -378,11 +378,10 @@ impl Dispatch, WaylandState> for WaylandState xdg_surface_data.lock().surface_id = SurfaceID::Toplevel; let Some(backend) = XDGBackend::create(toplevel.clone(), seat_data.clone()) else {return}; - let (node, panel_item) = PanelItem::create( + let panel_item = PanelItem::create( Box::new(backend), client_credentials.map(|c| c.pid), ); - toplevel_data.lock().panel_item_node.replace(node); xdg_surface_data.lock().panel_item = Arc::downgrade(&panel_item); handle_cursor(&panel_item, panel_item.backend.cursor.clone()); } @@ -509,7 +508,6 @@ impl Dispatch, WaylandState> for WaylandState #[derive(Debug, Clone)] pub struct ToplevelData { - panel_item_node: Option>, xdg_surface: WlWeak, parent: Option>, title: Option, @@ -520,7 +518,6 @@ pub struct ToplevelData { impl ToplevelData { fn new(xdg_surface: &XdgSurface) -> Self { ToplevelData { - panel_item_node: None, xdg_surface: xdg_surface.downgrade(), parent: None, title: None, @@ -543,6 +540,12 @@ impl ToplevelData { xdg_surface_data.panel_item() } } +impl Drop for ToplevelData { + fn drop(&mut self) { + let Some(panel_item) = self.panel_item() else {return}; + panel_item.drop_toplevel(); + } +} impl Dispatch, WaylandState> for WaylandState { fn request( _state: &mut WaylandState, @@ -578,15 +581,6 @@ impl Dispatch, WaylandState> for WaylandState { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.toplevel_app_id_changed(&app_id); } - xdg_toplevel::Request::ShowWindowMenu { - seat: _, - serial: _, - x, - y, - } => { - let Some(panel_item) = data.lock().panel_item() else {return}; - panel_item.toplevel_window_menu([x, y].into()); - } xdg_toplevel::Request::Move { seat, serial } => { debug!(?xdg_toplevel, ?seat, serial, "XDG Toplevel move request"); let Some(panel_item) = data.lock().panel_item() else {return}; @@ -629,8 +623,6 @@ impl Dispatch, WaylandState> for WaylandState { data.lock().min_size = (width > 1 || height > 1) .then_some(Vector2::from([width as u32, height as u32])); } - xdg_toplevel::Request::SetMaximized => {} - xdg_toplevel::Request::UnsetMaximized => {} xdg_toplevel::Request::SetFullscreen { output: _ } => { let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.backend.toplevel_state.lock().fullscreen = true; @@ -641,13 +633,12 @@ impl Dispatch, WaylandState> for WaylandState { panel_item.backend.toplevel_state.lock().fullscreen = false; panel_item.backend.configure(None); } - xdg_toplevel::Request::SetMinimized => {} xdg_toplevel::Request::Destroy => { debug!(?xdg_toplevel, "Destroy XDG Toplevel"); let Some(panel_item) = data.lock().panel_item() else {return}; panel_item.drop_toplevel(); } - _ => unreachable!(), + _ => {} } } } @@ -863,16 +854,18 @@ impl XDGBackend { popup: &XdgPopup, data: &PopupData, ) { - let uid = data.uid.clone(); - - self.popups.lock().insert(uid.clone(), popup.downgrade()); + self.popups + .lock() + .insert(data.uid.clone(), popup.downgrade()); let Some(positioner_data) = data.positioner_data() else {return}; - panel_item.new_child(ChildInfo { - uid, - parent: data.parent_id.clone(), - geometry: positioner_data.into(), - }) + panel_item.new_child( + &data.uid, + ChildInfo { + parent: data.parent_id.clone(), + geometry: positioner_data.into(), + }, + ) } pub fn reposition_popup(&self, panel_item: &PanelItem, popup_state: &PopupData) { let Some(positioner_data) = popup_state.positioner_data() else {return}; @@ -890,21 +883,19 @@ impl XDGBackend { self.seat.drop_surface(&wl_surface); } - fn child_data(&self) -> Vec { - self.popups - .lock() - .values() - .filter_map(|v| { - let popup = v.upgrade().ok()?; - let data = PopupData::get(&popup)?; - let data_lock = data.lock(); - Some(ChildInfo { - uid: data_lock.uid.clone(), + fn child_data(&self) -> FxHashMap { + FxHashMap::from_iter(self.popups.lock().values().filter_map(|v| { + let popup = v.upgrade().ok()?; + let data = PopupData::get(&popup)?; + let data_lock = data.lock(); + Some(( + data_lock.uid.clone(), + ChildInfo { parent: data_lock.parent_id.clone(), geometry: data_lock.positioner_data()?.into(), - }) - }) - .collect::>() + }, + )) + })) } fn flush_client(&self) { @@ -952,7 +943,10 @@ impl Backend for XDGBackend { .lock() .geometry .clone() - .unwrap(), + .unwrap_or_else(|| Geometry { + origin: [0, 0].into(), + size, + }), }; Ok(PanelItemInitData { @@ -1017,18 +1011,21 @@ impl Backend for XDGBackend { ) } - fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) { + fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; - self.seat.keyboard_event( - &surface, - KeyboardEvent::Key { - key, - state: if state { 1 } else { 0 }, - }, - ) - } - - fn keyboard_keymap(&self, _surface: &SurfaceID, _keymap_id: &str) { - todo!() + let keymaps = KEYMAPS.lock(); + let Some(keymap) = keymaps.get(keymap_id).cloned() else {return}; + if self.seat.set_keymap(keymap, vec![surface.clone()]).is_err() { + return; + } + for key in keys { + self.seat.keyboard_event( + &surface, + KeyboardEvent::Key { + key: key.abs() as u32, + state: if key < 0 { 1 } else { 0 }, + }, + ); + } } } diff --git a/src/wayland/xwayland.rs b/src/wayland/xwayland.rs index 0e59567..bb7f578 100644 --- a/src/wayland/xwayland.rs +++ b/src/wayland/xwayland.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::{ nodes::{ + data::KEYMAPS, drawable::model::ModelPart, items::panel::{Backend, Geometry, PanelItem, PanelItemInitData, SurfaceID, ToplevelInfo}, }, @@ -13,6 +14,7 @@ use color_eyre::eyre::Result; use mint::Vector2; use once_cell::sync::OnceCell; use parking_lot::Mutex; +use rustc_hash::FxHashMap; use smithay::{ reexports::{ calloop::{EventLoop, LoopSignal}, @@ -42,7 +44,7 @@ impl XWaylandState { let (tx, rx) = oneshot::channel(); - tokio::task::spawn_blocking(move || { + std::thread::spawn(move || { let mut event_loop: EventLoop = EventLoop::try_new()?; let (xwayland, connection) = XWayland::new(&dh); let handle = event_loop.handle(); @@ -144,7 +146,7 @@ impl XwmHandler for XWaylandHandler { let Some(wl_surface) = window.wl_surface() else {return}; let seat = seat.clone(); window.user_data().insert_if_missing_threadsafe(|| { - let (_node, panel_item) = PanelItem::create( + let panel_item = PanelItem::create( Box::new(X11Backend { toplevel_parent: None, toplevel: window.clone(), @@ -357,7 +359,7 @@ impl Backend for X11Backend { .into(), }, }, - children: vec![], + children: FxHashMap::default(), pointer_grab: self._pointer_grab.lock().clone(), keyboard_grab: self._keyboard_grab.lock().clone(), }) @@ -415,17 +417,21 @@ impl Backend for X11Backend { ) } - fn keyboard_keymap(&self, surface: &SurfaceID, keymap_id: &str) { - todo!() - } - fn keyboard_key(&self, surface: &SurfaceID, key: u32, state: bool) { + fn keyboard_keys(&self, surface: &SurfaceID, keymap_id: &str, keys: Vec) { let Some(surface) = self.wl_surface_from_id(surface) else {return}; - self.seat.keyboard_event( - &surface, - KeyboardEvent::Key { - key, - state: if state { 1 } else { 0 }, - }, - ) + let keymaps = KEYMAPS.lock(); + let Some(keymap) = keymaps.get(keymap_id).cloned() else {return}; + if self.seat.set_keymap(keymap, vec![surface.clone()]).is_err() { + return; + } + for key in keys { + self.seat.keyboard_event( + &surface, + KeyboardEvent::Key { + key: key.abs() as u32, + state: if key < 0 { 1 } else { 0 }, + }, + ); + } } }