feat(wayland): keyboard support
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Client>,
|
||||
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<Client>,
|
||||
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<Client>,
|
||||
_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<Client>,
|
||||
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<Client>,
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CoreSurface>,
|
||||
pub hotspot: Vector2<i32>,
|
||||
}
|
||||
|
||||
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<WlPointer>,
|
||||
pub pointer_active: Mutex<bool>,
|
||||
keyboard: OnceCell<WlKeyboard>,
|
||||
pub keyboard_active: Mutex<bool>,
|
||||
pub keyboard_info: Mutex<Option<KeyboardInfo>>,
|
||||
touch: OnceCell<WlTouch>,
|
||||
}
|
||||
impl SeatDataInner {
|
||||
@@ -130,7 +174,9 @@ impl Dispatch<WlSeat, SeatData, WaylandState> 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()));
|
||||
|
||||
@@ -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::<Self>(&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(),
|
||||
|
||||
Reference in New Issue
Block a user