From 9987e5eb08c5b85fe57043a5d9223d9787c3dbdb Mon Sep 17 00:00:00 2001 From: Nova Date: Tue, 13 Sep 2022 06:46:30 -0400 Subject: [PATCH] feat(wayland): keyboard support --- Cargo.toml | 4 +- src/wayland/mod.rs | 3 +- src/wayland/panel_item.rs | 143 +++++++++++++++++++++++--------------- src/wayland/seat.rs | 54 ++++++++++++-- src/wayland/state.rs | 6 +- 5 files changed, 145 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 97226ca..7d73bed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ send_wrapper = "0.6.0" prisma = "0.1.1" slog = "2.7.0" slog-stdlog = "4.1.1" +xkbcommon = { version = "0.5.0", default-features = false } [dependencies.libstardustxr] path = "../libstardustxr-rs" @@ -37,4 +38,5 @@ default-features = false features = ["linux-egl"] [dependencies.smithay] -git = "https://github.com/Smithay/smithay.git" +# git = "https://github.com/Smithay/smithay.git" +path = "../smithay" diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 654c63c..c0c68a5 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -91,8 +91,7 @@ impl Wayland { let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8); GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap(); - let join_handle = - Wayland::start_loop(display.clone(), state.clone(), global_destroy_queue)?; + let join_handle = Wayland::start_loop(display.clone(), state, global_destroy_queue)?; Ok(Wayland { log, diff --git a/src/wayland/panel_item.rs b/src/wayland/panel_item.rs index d696d46..2425cb1 100644 --- a/src/wayland/panel_item.rs +++ b/src/wayland/panel_item.rs @@ -1,4 +1,7 @@ -use super::{seat::SeatData, surface::CoreSurface}; +use super::{ + seat::{KeyboardInfo, SeatData}, + surface::CoreSurface, +}; use crate::{ core::{ client::{Client, INTERNAL_CLIENT}, @@ -20,18 +23,13 @@ use libstardustxr::{ use nanoid::nanoid; use smithay::{ reexports::wayland_server::{ - protocol::{ - wl_keyboard::KeyState, - wl_pointer::{Axis, ButtonState}, - }, + protocol::wl_pointer::{Axis, ButtonState}, Resource, }, wayland::{compositor::SurfaceData, shell::xdg::XdgToplevelSurfaceData}, }; -use std::{ - convert::TryInto, - sync::{Arc, Weak}, -}; +use std::sync::{Arc, Weak}; +use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap}; lazy_static! { static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo { @@ -97,15 +95,16 @@ impl PanelItem { node.add_local_signal("pointerScroll", PanelItem::pointer_scroll_flex); node.add_local_signal("pointerButton", PanelItem::pointer_button_flex); node.add_local_signal("pointerMotion", PanelItem::pointer_motion_flex); - node.add_local_signal("keyboardSetActive", PanelItem::keyboard_set_active_flex); node.add_local_signal( - "keyboardSetKeyState", - PanelItem::keyboard_set_key_state_flex, + "keyboardActivateString", + PanelItem::keyboard_activate_string_flex, ); node.add_local_signal( - "keyboardSetModifiers", - PanelItem::keyboard_set_modifiers_flex, + "keyboardActivateNames", + PanelItem::keyboard_activate_names_flex, ); + node.add_local_signal("keyboardDeactivate", PanelItem::keyboard_deactivate_flex); + node.add_local_signal("keyboardKeyState", PanelItem::keyboard_key_state_flex); node } @@ -360,23 +359,83 @@ impl PanelItem { Ok(()) } - fn keyboard_set_active_flex( + fn keyboard_activate_string_flex( node: &Node, _calling_client: Arc, data: &[u8], ) -> Result<()> { + let keymap_str = flexbuffers::Reader::get_root(data)?.get_str()?; + let context = xkb::Context::new(0); + let keymap = Keymap::new_from_string( + &context, + keymap_str.to_string(), + XKB_KEYMAP_FORMAT_TEXT_V1, + 0, + ) + .ok_or_else(|| anyhow!("Keymap is not valid"))?; + + PanelItem::keyboard_activate_flex(node, &keymap) + } + + fn keyboard_activate_names_flex( + node: &Node, + _calling_client: Arc, + data: &[u8], + ) -> Result<()> { + let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; + let rules = flex_vec.idx(0).as_str(); + let model = flex_vec.idx(1).as_str(); + let layout = flex_vec.idx(2).as_str(); + let variant = flex_vec.idx(3).as_str(); + let options = match flex_vec.index(4).ok() { + Some(options) => Some(options.get_str()?.to_string()), + None => None, + }; + let context = xkb::Context::new(0); + let keymap = Keymap::new_from_names( + &context, + rules, + model, + layout, + variant, + options, + XKB_KEYMAP_FORMAT_TEXT_V1, + ) + .ok_or_else(|| anyhow!("Keymap is not valid"))?; + + PanelItem::keyboard_activate_flex(node, &keymap) + } + + fn keyboard_activate_flex(node: &Node, keymap: &Keymap) -> Result<()> { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(keyboard) = panel_item.seat_data.keyboard() { if let Some(core_surface) = panel_item.core_surface.upgrade() { - let mut keyboard_active = panel_item.seat_data.keyboard_active.lock(); - let active = flexbuffers::Reader::get_root(data)?.get_bool()?; - if *keyboard_active != active { - if active { - keyboard.enter(0, &core_surface.wl_surface(), vec![]); - } else { - keyboard.leave(0, &core_surface.wl_surface()); - } - *keyboard_active = active; + let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); + if keyboard_info.is_none() { + keyboard.enter(0, &core_surface.wl_surface(), vec![]); + keyboard.repeat_info(0, 0); + } + keyboard_info.replace(KeyboardInfo::new(keymap)); + keyboard_info.as_ref().unwrap().keymap.send(keyboard)?; + } + } + } + + Ok(()) + } + + fn keyboard_deactivate_flex( + node: &Node, + _calling_client: Arc, + _data: &[u8], + ) -> Result<()> { + if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { + if let Some(keyboard) = panel_item.seat_data.keyboard() { + if let Some(core_surface) = panel_item.core_surface.upgrade() { + let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); + if keyboard_info.is_some() { + keyboard.leave(0, &core_surface.wl_surface()); + *keyboard_info = None; } } } @@ -385,45 +444,19 @@ impl PanelItem { Ok(()) } - fn keyboard_set_key_state_flex( + fn keyboard_key_state_flex( node: &Node, _calling_client: Arc, data: &[u8], ) -> Result<()> { if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { if let Some(keyboard) = panel_item.seat_data.keyboard() { - let active = *panel_item.seat_data.keyboard_active.lock(); - if active { + let mut keyboard_info = panel_item.seat_data.keyboard_info.lock(); + if let Some(keyboard_info) = &mut *keyboard_info { let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; let key = flex_vec.index(0)?.get_u64()? as u32; - let state: KeyState = (flex_vec.index(1)?.as_u64() as u32) - .try_into() - .map_err(|_| anyhow!("Invalid key state"))?; - keyboard.key(0, 0, key, state); - } - } - } - - Ok(()) - } - - fn keyboard_set_modifiers_flex( - node: &Node, - _calling_client: Arc, - data: &[u8], - ) -> Result<()> { - if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization { - if let Some(keyboard) = panel_item.seat_data.keyboard() { - let active = *panel_item.seat_data.keyboard_active.lock(); - if active { - let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?; - keyboard.modifiers( - 0, - flex_vec.index(0)?.get_u64()? as u32, - flex_vec.index(1)?.get_u64()? as u32, - flex_vec.index(2)?.get_u64()? as u32, - flex_vec.index(3)?.get_u64()? as u32, - ); + let state = flex_vec.index(1)?.get_u64()? as u32; + keyboard_info.process(key, state, &keyboard)?; } } } diff --git a/src/wayland/seat.rs b/src/wayland/seat.rs index 77d60f2..c65f538 100644 --- a/src/wayland/seat.rs +++ b/src/wayland/seat.rs @@ -1,15 +1,17 @@ use super::{state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE}; use crate::nodes::item::Item; +use anyhow::Result; use mint::Vector2; use nanoid::nanoid; use once_cell::sync::OnceCell; use parking_lot::Mutex; use smithay::{ + input::keyboard::{KeymapFile, ModifiersState}, reexports::wayland_server::{ backend::{ClientId, GlobalId}, delegate_dispatch, delegate_global_dispatch, protocol::{ - wl_keyboard::{self, WlKeyboard}, + wl_keyboard::{self, KeyState, WlKeyboard}, wl_pointer::{self, WlPointer}, wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE}, wl_touch::{self, WlTouch}, @@ -20,12 +22,54 @@ use smithay::{ }; use std::sync::Arc; use std::{ops::Deref, sync::Weak}; +use xkbcommon::xkb::{self, Keymap}; pub struct Cursor { pub core_surface: Weak, pub hotspot: Vector2, } +pub struct KeyboardInfo { + pub keymap: KeymapFile, + pub state: xkb::State, + pub mods: ModifiersState, +} +impl KeyboardInfo { + pub fn new(keymap: &Keymap) -> Self { + KeyboardInfo { + state: xkb::State::new(keymap), + keymap: KeymapFile::new(keymap, None), + mods: ModifiersState::default(), + } + } + pub fn process(&mut self, key: u32, state: u32, keyboard: &WlKeyboard) -> Result<()> { + let wl_state = match state { + 0 => KeyState::Released, + 1 => KeyState::Pressed, + _ => anyhow::bail!("Invalid key state!"), + }; + let xkb_state = match state { + 0 => xkb::KeyDirection::Up, + 1 => xkb::KeyDirection::Down, + _ => anyhow::bail!("Invalid key state!"), + }; + let state_components = self.state.update_key(key, xkb_state); + if state_components != 0 { + self.mods.update_with(&self.state); + keyboard.modifiers( + 0, + self.mods.serialized.depressed, + self.mods.serialized.latched, + self.mods.serialized.locked, + 0, + ); + } + keyboard.key(0, 0, key, wl_state); + Ok(()) + } +} +unsafe impl Send for KeyboardInfo {} + pub struct SeatDelegate; #[derive(Clone)] @@ -41,7 +85,7 @@ impl SeatData { pointer: OnceCell::new(), pointer_active: Mutex::new(false), keyboard: OnceCell::new(), - keyboard_active: Mutex::new(false), + keyboard_info: Mutex::new(None), touch: OnceCell::new(), })); @@ -70,7 +114,7 @@ pub struct SeatDataInner { pointer: OnceCell, pub pointer_active: Mutex, keyboard: OnceCell, - pub keyboard_active: Mutex, + pub keyboard_info: Mutex>, touch: OnceCell, } impl SeatDataInner { @@ -130,7 +174,9 @@ impl Dispatch for SeatDelegate { let _ = data.0.pointer.set(data_init.init(id, data.clone())); } wl_seat::Request::GetKeyboard { id } => { - let _ = data.0.keyboard.set(data_init.init(id, data.clone())); + let keyboard = data_init.init(id, data.clone()); + keyboard.repeat_info(0, 0); + let _ = data.0.keyboard.set(keyboard); } wl_seat::Request::GetTouch { id } => { let _ = data.0.touch.set(data_init.init(id, data.clone())); diff --git a/src/wayland/state.rs b/src/wayland/state.rs index f4e15e1..43e752f 100644 --- a/src/wayland/state.rs +++ b/src/wayland/state.rs @@ -4,16 +4,16 @@ use parking_lot::Mutex; use slog::Logger; use smithay::{ delegate_output, delegate_shm, + output::{Output, Scale, Subpixel}, reexports::wayland_server::{ backend::{ClientData, ClientId, DisconnectReason}, - protocol::wl_output::Subpixel, Display, DisplayHandle, }, utils::Size, wayland::{ buffer::BufferHandler, compositor::CompositorState, - output::{Output, OutputManagerState, Scale}, + output::OutputManagerState, shell::xdg::{decoration::XdgDecorationState, XdgShellState}, shm::{ShmHandler, ShmState}, }, @@ -62,7 +62,7 @@ impl WaylandState { let output_manager_state = OutputManagerState::new_with_xdg_output::(&display_handle); let output = Output::new( "1x".to_owned(), - smithay::wayland::output::PhysicalProperties { + smithay::output::PhysicalProperties { size: Size::default(), subpixel: Subpixel::None, make: "Virtual XR Display".to_owned(),