feat(wayland): keyboard support
This commit is contained in:
@@ -26,6 +26,7 @@ send_wrapper = "0.6.0"
|
|||||||
prisma = "0.1.1"
|
prisma = "0.1.1"
|
||||||
slog = "2.7.0"
|
slog = "2.7.0"
|
||||||
slog-stdlog = "4.1.1"
|
slog-stdlog = "4.1.1"
|
||||||
|
xkbcommon = { version = "0.5.0", default-features = false }
|
||||||
|
|
||||||
[dependencies.libstardustxr]
|
[dependencies.libstardustxr]
|
||||||
path = "../libstardustxr-rs"
|
path = "../libstardustxr-rs"
|
||||||
@@ -37,4 +38,5 @@ default-features = false
|
|||||||
features = ["linux-egl"]
|
features = ["linux-egl"]
|
||||||
|
|
||||||
[dependencies.smithay]
|
[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);
|
let (global_destroy_queue_in, global_destroy_queue) = mpsc::channel(8);
|
||||||
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
|
GLOBAL_DESTROY_QUEUE.set(global_destroy_queue_in).unwrap();
|
||||||
|
|
||||||
let join_handle =
|
let join_handle = Wayland::start_loop(display.clone(), state, global_destroy_queue)?;
|
||||||
Wayland::start_loop(display.clone(), state.clone(), global_destroy_queue)?;
|
|
||||||
|
|
||||||
Ok(Wayland {
|
Ok(Wayland {
|
||||||
log,
|
log,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use super::{seat::SeatData, surface::CoreSurface};
|
use super::{
|
||||||
|
seat::{KeyboardInfo, SeatData},
|
||||||
|
surface::CoreSurface,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
client::{Client, INTERNAL_CLIENT},
|
client::{Client, INTERNAL_CLIENT},
|
||||||
@@ -20,18 +23,13 @@ use libstardustxr::{
|
|||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{
|
||||||
protocol::{
|
protocol::wl_pointer::{Axis, ButtonState},
|
||||||
wl_keyboard::KeyState,
|
|
||||||
wl_pointer::{Axis, ButtonState},
|
|
||||||
},
|
|
||||||
Resource,
|
Resource,
|
||||||
},
|
},
|
||||||
wayland::{compositor::SurfaceData, shell::xdg::XdgToplevelSurfaceData},
|
wayland::{compositor::SurfaceData, shell::xdg::XdgToplevelSurfaceData},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::sync::{Arc, Weak};
|
||||||
convert::TryInto,
|
use xkbcommon::xkb::{self, ffi::XKB_KEYMAP_FORMAT_TEXT_V1, Keymap};
|
||||||
sync::{Arc, Weak},
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ITEM_TYPE_INFO_PANEL: TypeInfo = TypeInfo {
|
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("pointerScroll", PanelItem::pointer_scroll_flex);
|
||||||
node.add_local_signal("pointerButton", PanelItem::pointer_button_flex);
|
node.add_local_signal("pointerButton", PanelItem::pointer_button_flex);
|
||||||
node.add_local_signal("pointerMotion", PanelItem::pointer_motion_flex);
|
node.add_local_signal("pointerMotion", PanelItem::pointer_motion_flex);
|
||||||
node.add_local_signal("keyboardSetActive", PanelItem::keyboard_set_active_flex);
|
|
||||||
node.add_local_signal(
|
node.add_local_signal(
|
||||||
"keyboardSetKeyState",
|
"keyboardActivateString",
|
||||||
PanelItem::keyboard_set_key_state_flex,
|
PanelItem::keyboard_activate_string_flex,
|
||||||
);
|
);
|
||||||
node.add_local_signal(
|
node.add_local_signal(
|
||||||
"keyboardSetModifiers",
|
"keyboardActivateNames",
|
||||||
PanelItem::keyboard_set_modifiers_flex,
|
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
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,23 +359,83 @@ impl PanelItem {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyboard_set_active_flex(
|
fn keyboard_activate_string_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> 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 ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization {
|
||||||
if let Some(keyboard) = panel_item.seat_data.keyboard() {
|
if let Some(keyboard) = panel_item.seat_data.keyboard() {
|
||||||
if let Some(core_surface) = panel_item.core_surface.upgrade() {
|
if let Some(core_surface) = panel_item.core_surface.upgrade() {
|
||||||
let mut keyboard_active = panel_item.seat_data.keyboard_active.lock();
|
let mut keyboard_info = panel_item.seat_data.keyboard_info.lock();
|
||||||
let active = flexbuffers::Reader::get_root(data)?.get_bool()?;
|
if keyboard_info.is_none() {
|
||||||
if *keyboard_active != active {
|
keyboard.enter(0, &core_surface.wl_surface(), vec![]);
|
||||||
if active {
|
keyboard.repeat_info(0, 0);
|
||||||
keyboard.enter(0, &core_surface.wl_surface(), vec![]);
|
}
|
||||||
} else {
|
keyboard_info.replace(KeyboardInfo::new(keymap));
|
||||||
keyboard.leave(0, &core_surface.wl_surface());
|
keyboard_info.as_ref().unwrap().keymap.send(keyboard)?;
|
||||||
}
|
}
|
||||||
*keyboard_active = active;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyboard_set_key_state_flex(
|
fn keyboard_key_state_flex(
|
||||||
node: &Node,
|
node: &Node,
|
||||||
_calling_client: Arc<Client>,
|
_calling_client: Arc<Client>,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization {
|
if let ItemType::Panel(panel_item) = &node.item.get().unwrap().specialization {
|
||||||
if let Some(keyboard) = panel_item.seat_data.keyboard() {
|
if let Some(keyboard) = panel_item.seat_data.keyboard() {
|
||||||
let active = *panel_item.seat_data.keyboard_active.lock();
|
let mut keyboard_info = panel_item.seat_data.keyboard_info.lock();
|
||||||
if active {
|
if let Some(keyboard_info) = &mut *keyboard_info {
|
||||||
let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?;
|
let flex_vec = flexbuffers::Reader::get_root(data)?.get_vector()?;
|
||||||
let key = flex_vec.index(0)?.get_u64()? as u32;
|
let key = flex_vec.index(0)?.get_u64()? as u32;
|
||||||
let state: KeyState = (flex_vec.index(1)?.as_u64() as u32)
|
let state = flex_vec.index(1)?.get_u64()? as u32;
|
||||||
.try_into()
|
keyboard_info.process(key, state, &keyboard)?;
|
||||||
.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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
use super::{state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE};
|
use super::{state::WaylandState, surface::CoreSurface, GLOBAL_DESTROY_QUEUE};
|
||||||
use crate::nodes::item::Item;
|
use crate::nodes::item::Item;
|
||||||
|
use anyhow::Result;
|
||||||
use mint::Vector2;
|
use mint::Vector2;
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
|
input::keyboard::{KeymapFile, ModifiersState},
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{
|
||||||
backend::{ClientId, GlobalId},
|
backend::{ClientId, GlobalId},
|
||||||
delegate_dispatch, delegate_global_dispatch,
|
delegate_dispatch, delegate_global_dispatch,
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_keyboard::{self, WlKeyboard},
|
wl_keyboard::{self, KeyState, WlKeyboard},
|
||||||
wl_pointer::{self, WlPointer},
|
wl_pointer::{self, WlPointer},
|
||||||
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
|
wl_seat::{self, Capability, WlSeat, EVT_NAME_SINCE},
|
||||||
wl_touch::{self, WlTouch},
|
wl_touch::{self, WlTouch},
|
||||||
@@ -20,12 +22,54 @@ use smithay::{
|
|||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{ops::Deref, sync::Weak};
|
use std::{ops::Deref, sync::Weak};
|
||||||
|
use xkbcommon::xkb::{self, Keymap};
|
||||||
|
|
||||||
pub struct Cursor {
|
pub struct Cursor {
|
||||||
pub core_surface: Weak<CoreSurface>,
|
pub core_surface: Weak<CoreSurface>,
|
||||||
pub hotspot: Vector2<i32>,
|
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;
|
pub struct SeatDelegate;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -41,7 +85,7 @@ impl SeatData {
|
|||||||
pointer: OnceCell::new(),
|
pointer: OnceCell::new(),
|
||||||
pointer_active: Mutex::new(false),
|
pointer_active: Mutex::new(false),
|
||||||
keyboard: OnceCell::new(),
|
keyboard: OnceCell::new(),
|
||||||
keyboard_active: Mutex::new(false),
|
keyboard_info: Mutex::new(None),
|
||||||
touch: OnceCell::new(),
|
touch: OnceCell::new(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -70,7 +114,7 @@ pub struct SeatDataInner {
|
|||||||
pointer: OnceCell<WlPointer>,
|
pointer: OnceCell<WlPointer>,
|
||||||
pub pointer_active: Mutex<bool>,
|
pub pointer_active: Mutex<bool>,
|
||||||
keyboard: OnceCell<WlKeyboard>,
|
keyboard: OnceCell<WlKeyboard>,
|
||||||
pub keyboard_active: Mutex<bool>,
|
pub keyboard_info: Mutex<Option<KeyboardInfo>>,
|
||||||
touch: OnceCell<WlTouch>,
|
touch: OnceCell<WlTouch>,
|
||||||
}
|
}
|
||||||
impl SeatDataInner {
|
impl SeatDataInner {
|
||||||
@@ -130,7 +174,9 @@ impl Dispatch<WlSeat, SeatData, WaylandState> for SeatDelegate {
|
|||||||
let _ = data.0.pointer.set(data_init.init(id, data.clone()));
|
let _ = data.0.pointer.set(data_init.init(id, data.clone()));
|
||||||
}
|
}
|
||||||
wl_seat::Request::GetKeyboard { id } => {
|
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 } => {
|
wl_seat::Request::GetTouch { id } => {
|
||||||
let _ = data.0.touch.set(data_init.init(id, data.clone()));
|
let _ = data.0.touch.set(data_init.init(id, data.clone()));
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ use parking_lot::Mutex;
|
|||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
delegate_output, delegate_shm,
|
delegate_output, delegate_shm,
|
||||||
|
output::{Output, Scale, Subpixel},
|
||||||
reexports::wayland_server::{
|
reexports::wayland_server::{
|
||||||
backend::{ClientData, ClientId, DisconnectReason},
|
backend::{ClientData, ClientId, DisconnectReason},
|
||||||
protocol::wl_output::Subpixel,
|
|
||||||
Display, DisplayHandle,
|
Display, DisplayHandle,
|
||||||
},
|
},
|
||||||
utils::Size,
|
utils::Size,
|
||||||
wayland::{
|
wayland::{
|
||||||
buffer::BufferHandler,
|
buffer::BufferHandler,
|
||||||
compositor::CompositorState,
|
compositor::CompositorState,
|
||||||
output::{Output, OutputManagerState, Scale},
|
output::OutputManagerState,
|
||||||
shell::xdg::{decoration::XdgDecorationState, XdgShellState},
|
shell::xdg::{decoration::XdgDecorationState, XdgShellState},
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
},
|
},
|
||||||
@@ -62,7 +62,7 @@ impl WaylandState {
|
|||||||
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
|
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
|
||||||
let output = Output::new(
|
let output = Output::new(
|
||||||
"1x".to_owned(),
|
"1x".to_owned(),
|
||||||
smithay::wayland::output::PhysicalProperties {
|
smithay::output::PhysicalProperties {
|
||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
subpixel: Subpixel::None,
|
subpixel: Subpixel::None,
|
||||||
make: "Virtual XR Display".to_owned(),
|
make: "Virtual XR Display".to_owned(),
|
||||||
|
|||||||
Reference in New Issue
Block a user