wayland: implement virtual-keyboard
This commit is contained in:
parent
826f40adca
commit
6c0e3a4fff
20 changed files with 689 additions and 14 deletions
|
|
@ -145,6 +145,7 @@ Jay supports the following wayland protocols:
|
||||||
| zwp_pointer_constraints_v1 | 1 | |
|
| zwp_pointer_constraints_v1 | 1 | |
|
||||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||||
|
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||||
| zxdg_decoration_manager_v1 | 1 | |
|
| zxdg_decoration_manager_v1 | 1 | |
|
||||||
| zxdg_output_manager_v1 | 3 | |
|
| zxdg_output_manager_v1 | 3 | |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
- Add support for wp-alpha-modifier.
|
- Add support for wp-alpha-modifier.
|
||||||
- Add support for per-device keymaps.
|
- Add support for per-device keymaps.
|
||||||
|
- Add support for virtual-keyboard-unstable-v1.
|
||||||
|
|
||||||
# 1.0.3 (2024-04-11)
|
# 1.0.3 (2024-04-11)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ use {
|
||||||
wl_registry::WlRegistry,
|
wl_registry::WlRegistry,
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
zwp_pointer_constraints_v1::ZwpPointerConstraintsV1Global,
|
zwp_pointer_constraints_v1::ZwpPointerConstraintsV1Global,
|
||||||
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1Global, WlSeatGlobal,
|
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1Global,
|
||||||
|
zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1Global, WlSeatGlobal,
|
||||||
},
|
},
|
||||||
wl_shm::WlShmGlobal,
|
wl_shm::WlShmGlobal,
|
||||||
wl_subcompositor::WlSubcompositorGlobal,
|
wl_subcompositor::WlSubcompositorGlobal,
|
||||||
|
|
@ -175,6 +176,7 @@ impl Globals {
|
||||||
add_singleton!(XdgToplevelDragManagerV1Global);
|
add_singleton!(XdgToplevelDragManagerV1Global);
|
||||||
add_singleton!(ZwlrDataControlManagerV1Global);
|
add_singleton!(ZwlrDataControlManagerV1Global);
|
||||||
add_singleton!(WpAlphaModifierV1Global);
|
add_singleton!(WpAlphaModifierV1Global);
|
||||||
|
add_singleton!(ZwpVirtualKeyboardManagerV1Global);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ pub mod wl_touch;
|
||||||
pub mod zwp_pointer_constraints_v1;
|
pub mod zwp_pointer_constraints_v1;
|
||||||
pub mod zwp_relative_pointer_manager_v1;
|
pub mod zwp_relative_pointer_manager_v1;
|
||||||
pub mod zwp_relative_pointer_v1;
|
pub mod zwp_relative_pointer_v1;
|
||||||
|
pub mod zwp_virtual_keyboard_manager_v1;
|
||||||
|
pub mod zwp_virtual_keyboard_v1;
|
||||||
|
|
||||||
pub use event_handling::NodeSeatState;
|
pub use event_handling::NodeSeatState;
|
||||||
use {
|
use {
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,7 @@ impl WlSeatGlobal {
|
||||||
self.pointer_owner.button(self, time_usec, button, state);
|
self.pointer_owner.button(self, time_usec, button, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_event(&self, time_usec: u64, key: u32, key_state: KeyState) {
|
pub(super) fn key_event(&self, time_usec: u64, key: u32, key_state: KeyState) {
|
||||||
let (state, xkb_dir) = {
|
let (state, xkb_dir) = {
|
||||||
let mut pk = self.pressed_keys.borrow_mut();
|
let mut pk = self.pressed_keys.borrow_mut();
|
||||||
match key_state {
|
match key_state {
|
||||||
|
|
@ -411,6 +411,25 @@ impl WlSeatGlobal {
|
||||||
node.node_on_mods(self, mods);
|
node.node_on_mods(self, mods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_modifiers(
|
||||||
|
&self,
|
||||||
|
mods_depressed: u32,
|
||||||
|
mods_latched: u32,
|
||||||
|
mods_locked: u32,
|
||||||
|
group: u32,
|
||||||
|
) {
|
||||||
|
let new_mods =
|
||||||
|
self.kb_state
|
||||||
|
.borrow_mut()
|
||||||
|
.set(mods_depressed, mods_latched, mods_locked, group);
|
||||||
|
if let Some(mods) = new_mods {
|
||||||
|
self.state.for_each_seat_tester(|t| {
|
||||||
|
t.send_modifiers(self.id, &mods);
|
||||||
|
});
|
||||||
|
self.keyboard_node.get().node_on_mods(self, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlSeatGlobal {
|
impl WlSeatGlobal {
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ pub const REPEAT_INFO_SINCE: Version = Version(4);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const NO_KEYMAP: u32 = 0;
|
const NO_KEYMAP: u32 = 0;
|
||||||
pub(super) const XKB_V1: u32 = 1;
|
pub const XKB_V1: u32 = 1;
|
||||||
|
|
||||||
pub(super) const RELEASED: u32 = 0;
|
pub const RELEASED: u32 = 0;
|
||||||
pub(super) const PRESSED: u32 = 1;
|
pub const PRESSED: u32 = 1;
|
||||||
|
|
||||||
pub struct WlKeyboard {
|
pub struct WlKeyboard {
|
||||||
id: WlKeyboardId,
|
id: WlKeyboardId,
|
||||||
|
|
|
||||||
108
src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs
Normal file
108
src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::{Global, GlobalName},
|
||||||
|
ifs::wl_seat::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{zwp_virtual_keyboard_manager_v1::*, ZwpVirtualKeyboardManagerV1Id},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ZwpVirtualKeyboardManagerV1Global {
|
||||||
|
pub name: GlobalName,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ZwpVirtualKeyboardManagerV1 {
|
||||||
|
pub id: ZwpVirtualKeyboardManagerV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZwpVirtualKeyboardManagerV1Global {
|
||||||
|
pub fn new(name: GlobalName) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_(
|
||||||
|
self: Rc<Self>,
|
||||||
|
id: ZwpVirtualKeyboardManagerV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
version: Version,
|
||||||
|
) -> Result<(), ZwpVirtualKeyboardManagerV1Error> {
|
||||||
|
let obj = Rc::new(ZwpVirtualKeyboardManagerV1 {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
track!(client, obj);
|
||||||
|
client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_base!(
|
||||||
|
ZwpVirtualKeyboardManagerV1Global,
|
||||||
|
ZwpVirtualKeyboardManagerV1,
|
||||||
|
ZwpVirtualKeyboardManagerV1Error
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Global for ZwpVirtualKeyboardManagerV1Global {
|
||||||
|
fn singleton(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn secure(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_global!(ZwpVirtualKeyboardManagerV1Global);
|
||||||
|
|
||||||
|
impl ZwpVirtualKeyboardManagerV1RequestHandler for ZwpVirtualKeyboardManagerV1 {
|
||||||
|
type Error = ZwpVirtualKeyboardManagerV1Error;
|
||||||
|
|
||||||
|
fn create_virtual_keyboard(
|
||||||
|
&self,
|
||||||
|
req: CreateVirtualKeyboard,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let seat = self.client.lookup(req.seat)?;
|
||||||
|
let kb = Rc::new(ZwpVirtualKeyboardV1 {
|
||||||
|
id: req.id,
|
||||||
|
client: self.client.clone(),
|
||||||
|
seat: seat.global.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version: self.version,
|
||||||
|
keymap_id: Default::default(),
|
||||||
|
keymap: Default::default(),
|
||||||
|
});
|
||||||
|
track!(self.client, kb);
|
||||||
|
self.client.add_client_obj(&kb)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = ZwpVirtualKeyboardManagerV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for ZwpVirtualKeyboardManagerV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(ZwpVirtualKeyboardManagerV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ZwpVirtualKeyboardManagerV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
}
|
||||||
|
efrom!(ZwpVirtualKeyboardManagerV1Error, ClientError);
|
||||||
131
src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs
Normal file
131
src/ifs/wl_seat/zwp_virtual_keyboard_v1.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
client::{Client, ClientError},
|
||||||
|
clientmem::{ClientMem, ClientMemError},
|
||||||
|
ifs::wl_seat::{wl_keyboard, WlSeatGlobal},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
utils::clonecell::CloneCell,
|
||||||
|
wire::{zwp_virtual_keyboard_v1::*, ZwpVirtualKeyboardV1Id},
|
||||||
|
xkbcommon::{KeymapId, XkbCommonError, XkbKeymap},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ZwpVirtualKeyboardV1 {
|
||||||
|
pub id: ZwpVirtualKeyboardV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub seat: Rc<WlSeatGlobal>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
pub keymap_id: Cell<Option<KeymapId>>,
|
||||||
|
pub keymap: CloneCell<Option<Rc<XkbKeymap>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZwpVirtualKeyboardV1 {
|
||||||
|
fn ensure_keymap(&self) {
|
||||||
|
if let Some(id) = self.keymap_id.get() {
|
||||||
|
if id == self.seat.effective_kb_map_id.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let Some(keymap) = self.keymap.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.seat.set_effective_keymap(&keymap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
|
||||||
|
type Error = ZwpVirtualKeyboardV1Error;
|
||||||
|
|
||||||
|
fn keymap(&self, req: Keymap, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
if req.format != wl_keyboard::XKB_V1 {
|
||||||
|
return Err(ZwpVirtualKeyboardV1Error::UnsupportedFormat(req.format));
|
||||||
|
}
|
||||||
|
if req.size == 0 {
|
||||||
|
return Err(ZwpVirtualKeyboardV1Error::InvalidKeymap);
|
||||||
|
}
|
||||||
|
const MAX_SIZE: u32 = 1024 * 1024;
|
||||||
|
if req.size > MAX_SIZE {
|
||||||
|
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
||||||
|
}
|
||||||
|
let client_mem = ClientMem::new(req.fd.raw(), req.size as usize - 1, true)
|
||||||
|
.map(Rc::new)
|
||||||
|
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
||||||
|
let mut map = vec![];
|
||||||
|
client_mem
|
||||||
|
.offset(0)
|
||||||
|
.read(&mut map)
|
||||||
|
.map_err(ZwpVirtualKeyboardV1Error::ReadKeymap)?;
|
||||||
|
let map = self
|
||||||
|
.client
|
||||||
|
.state
|
||||||
|
.xkb_ctx
|
||||||
|
.keymap_from_str(&map)
|
||||||
|
.map_err(ZwpVirtualKeyboardV1Error::ParseKeymap)?;
|
||||||
|
self.keymap_id.set(Some(map.id));
|
||||||
|
self.keymap.set(Some(map));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key(&self, req: Key, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.ensure_keymap();
|
||||||
|
let time_usec = (req.time as u64) * 1000;
|
||||||
|
let state = match req.state {
|
||||||
|
wl_keyboard::RELEASED => KeyState::Released,
|
||||||
|
wl_keyboard::PRESSED => KeyState::Pressed,
|
||||||
|
_ => return Err(ZwpVirtualKeyboardV1Error::UnknownState(req.state)),
|
||||||
|
};
|
||||||
|
self.seat.key_event(time_usec, req.key, state);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modifiers(&self, req: Modifiers, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.ensure_keymap();
|
||||||
|
self.seat.set_modifiers(
|
||||||
|
req.mods_depressed,
|
||||||
|
req.mods_latched,
|
||||||
|
req.mods_locked,
|
||||||
|
req.group,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = ZwpVirtualKeyboardV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for ZwpVirtualKeyboardV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(ZwpVirtualKeyboardV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ZwpVirtualKeyboardV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("Unknown key state {0}")]
|
||||||
|
UnknownState(u32),
|
||||||
|
#[error("Unsupported keymap format {0}")]
|
||||||
|
UnsupportedFormat(u32),
|
||||||
|
#[error("Keymap is invalid")]
|
||||||
|
InvalidKeymap,
|
||||||
|
#[error("Keymap is too large")]
|
||||||
|
OversizedKeymap,
|
||||||
|
#[error("Could not map the keymap")]
|
||||||
|
MapKeymap(#[source] ClientMemError),
|
||||||
|
#[error("Could not read the keymap")]
|
||||||
|
ReadKeymap(#[source] ClientMemError),
|
||||||
|
#[error("Could not parse the keymap")]
|
||||||
|
ParseKeymap(#[source] XkbCommonError),
|
||||||
|
}
|
||||||
|
efrom!(ZwpVirtualKeyboardV1Error, ClientError);
|
||||||
|
|
@ -41,6 +41,8 @@ pub mod test_toplevel_drag;
|
||||||
pub mod test_toplevel_drag_manager;
|
pub mod test_toplevel_drag_manager;
|
||||||
pub mod test_viewport;
|
pub mod test_viewport;
|
||||||
pub mod test_viewporter;
|
pub mod test_viewporter;
|
||||||
|
pub mod test_virtual_keyboard;
|
||||||
|
pub mod test_virtual_keyboard_manager;
|
||||||
pub mod test_xdg_activation;
|
pub mod test_xdg_activation;
|
||||||
pub mod test_xdg_activation_token;
|
pub mod test_xdg_activation_token;
|
||||||
pub mod test_xdg_base;
|
pub mod test_xdg_base;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
test_error::TestResult, test_object::TestObject, test_transport::TestTransport,
|
test_error::TestResult, test_object::TestObject, test_transport::TestTransport,
|
||||||
test_utils::test_expected_event::TEEH, testrun::ParseFull,
|
test_utils::test_expected_event::TEEH, testrun::ParseFull,
|
||||||
},
|
},
|
||||||
utils::{buffd::MsgParser, clonecell::CloneCell, once::Once},
|
utils::{buffd::MsgParser, clonecell::CloneCell, numcell::NumCell, once::Once},
|
||||||
wire::{wl_keyboard::*, WlKeyboardId, WlSurfaceId},
|
wire::{wl_keyboard::*, WlKeyboardId, WlSurfaceId},
|
||||||
},
|
},
|
||||||
std::rc::Rc,
|
std::rc::Rc,
|
||||||
|
|
@ -22,8 +22,12 @@ pub struct TestKeyboard {
|
||||||
pub tran: Rc<TestTransport>,
|
pub tran: Rc<TestTransport>,
|
||||||
pub server: CloneCell<Option<Rc<WlKeyboard>>>,
|
pub server: CloneCell<Option<Rc<WlKeyboard>>>,
|
||||||
pub destroyed: Once,
|
pub destroyed: Once,
|
||||||
|
pub keymap: TEEH<(usize, Keymap)>,
|
||||||
|
pub key: TEEH<(usize, Key)>,
|
||||||
|
pub modifiers: TEEH<(usize, Modifiers)>,
|
||||||
pub enter: TEEH<TestEnterEvent>,
|
pub enter: TEEH<TestEnterEvent>,
|
||||||
pub leave: TEEH<Leave>,
|
pub leave: TEEH<Leave>,
|
||||||
|
pub event_id: NumCell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestKeyboard {
|
impl TestKeyboard {
|
||||||
|
|
@ -35,7 +39,8 @@ impl TestKeyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_keymap(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
fn handle_keymap(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
let _ev = Keymap::parse_full(parser)?;
|
let ev = Keymap::parse_full(parser)?;
|
||||||
|
self.keymap.push((self.event_id.fetch_add(1), ev));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,12 +61,14 @@ impl TestKeyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
fn handle_key(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
let _ev = Key::parse_full(parser)?;
|
let ev = Key::parse_full(parser)?;
|
||||||
|
self.key.push((self.event_id.fetch_add(1), ev));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_modifiers(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
fn handle_modifiers(&self, parser: MsgParser<'_, '_>) -> TestResult {
|
||||||
let _ev = Modifiers::parse_full(parser)?;
|
let ev = Modifiers::parse_full(parser)?;
|
||||||
|
self.modifiers.push((self.event_id.fetch_add(1), ev));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,9 @@ use {
|
||||||
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
||||||
test_subcompositor::TestSubcompositor, test_syncobj_manager::TestSyncobjManager,
|
test_subcompositor::TestSubcompositor, test_syncobj_manager::TestSyncobjManager,
|
||||||
test_toplevel_drag_manager::TestToplevelDragManager,
|
test_toplevel_drag_manager::TestToplevelDragManager,
|
||||||
test_viewporter::TestViewporter, test_xdg_activation::TestXdgActivation,
|
test_viewporter::TestViewporter,
|
||||||
test_xdg_base::TestXdgWmBase,
|
test_virtual_keyboard_manager::TestVirtualKeyboardManager,
|
||||||
|
test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase,
|
||||||
},
|
},
|
||||||
test_object::TestObject,
|
test_object::TestObject,
|
||||||
test_transport::TestTransport,
|
test_transport::TestTransport,
|
||||||
|
|
@ -52,6 +53,7 @@ pub struct TestRegistrySingletons {
|
||||||
pub zwp_linux_dmabuf_v1: u32,
|
pub zwp_linux_dmabuf_v1: u32,
|
||||||
pub xdg_toplevel_drag_manager_v1: u32,
|
pub xdg_toplevel_drag_manager_v1: u32,
|
||||||
pub wp_alpha_modifier_v1: u32,
|
pub wp_alpha_modifier_v1: u32,
|
||||||
|
pub zwp_virtual_keyboard_manager_v1: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestRegistry {
|
pub struct TestRegistry {
|
||||||
|
|
@ -76,6 +78,7 @@ pub struct TestRegistry {
|
||||||
pub dmabuf: CloneCell<Option<Rc<TestDmabuf>>>,
|
pub dmabuf: CloneCell<Option<Rc<TestDmabuf>>>,
|
||||||
pub drag_manager: CloneCell<Option<Rc<TestToplevelDragManager>>>,
|
pub drag_manager: CloneCell<Option<Rc<TestToplevelDragManager>>>,
|
||||||
pub alpha_modifier: CloneCell<Option<Rc<TestAlphaModifier>>>,
|
pub alpha_modifier: CloneCell<Option<Rc<TestAlphaModifier>>>,
|
||||||
|
pub virtual_keyboard_manager: CloneCell<Option<Rc<TestVirtualKeyboardManager>>>,
|
||||||
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +147,7 @@ impl TestRegistry {
|
||||||
zwp_linux_dmabuf_v1,
|
zwp_linux_dmabuf_v1,
|
||||||
xdg_toplevel_drag_manager_v1,
|
xdg_toplevel_drag_manager_v1,
|
||||||
wp_alpha_modifier_v1,
|
wp_alpha_modifier_v1,
|
||||||
|
zwp_virtual_keyboard_manager_v1,
|
||||||
};
|
};
|
||||||
self.singletons.set(Some(singletons.clone()));
|
self.singletons.set(Some(singletons.clone()));
|
||||||
Ok(singletons)
|
Ok(singletons)
|
||||||
|
|
@ -238,6 +242,13 @@ impl TestRegistry {
|
||||||
1,
|
1,
|
||||||
TestAlphaModifier
|
TestAlphaModifier
|
||||||
);
|
);
|
||||||
|
create_singleton!(
|
||||||
|
get_virtual_keyboard_manager,
|
||||||
|
virtual_keyboard_manager,
|
||||||
|
zwp_virtual_keyboard_manager_v1,
|
||||||
|
1,
|
||||||
|
TestVirtualKeyboardManager
|
||||||
|
);
|
||||||
|
|
||||||
pub fn bind<O: TestObject>(
|
pub fn bind<O: TestObject>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,12 @@ impl TestSeat {
|
||||||
tran: self.tran.clone(),
|
tran: self.tran.clone(),
|
||||||
server: Default::default(),
|
server: Default::default(),
|
||||||
destroyed: Default::default(),
|
destroyed: Default::default(),
|
||||||
|
keymap: Default::default(),
|
||||||
|
key: Default::default(),
|
||||||
|
modifiers: Default::default(),
|
||||||
enter: Default::default(),
|
enter: Default::default(),
|
||||||
leave: Default::default(),
|
leave: Default::default(),
|
||||||
|
event_id: Default::default(),
|
||||||
});
|
});
|
||||||
self.tran.add_obj(kb.clone())?;
|
self.tran.add_obj(kb.clone())?;
|
||||||
self.tran.sync().await;
|
self.tran.sync().await;
|
||||||
|
|
|
||||||
86
src/it/test_ifs/test_virtual_keyboard.rs
Normal file
86
src/it/test_ifs/test_virtual_keyboard.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
ifs::wl_seat::wl_keyboard,
|
||||||
|
it::{test_error::TestError, test_object::TestObject, test_transport::TestTransport},
|
||||||
|
time::now_usec,
|
||||||
|
wire::{zwp_virtual_keyboard_v1::*, ZwpVirtualKeyboardV1Id},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, io::Write, rc::Rc},
|
||||||
|
uapi::c,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TestVirtualKeyboard {
|
||||||
|
pub id: ZwpVirtualKeyboardV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
pub destroyed: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestVirtualKeyboard {
|
||||||
|
pub fn destroy(&self) -> Result<(), TestError> {
|
||||||
|
if !self.destroyed.replace(true) {
|
||||||
|
self.tran.send(Destroy { self_id: self.id })?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_keymap(&self, map: &str) -> Result<(), TestError> {
|
||||||
|
let mut memfd =
|
||||||
|
uapi::memfd_create("keymap", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
||||||
|
memfd.write_all(map.as_bytes()).unwrap();
|
||||||
|
memfd.write_all(&[0]).unwrap();
|
||||||
|
uapi::lseek(memfd.raw(), 0, c::SEEK_SET).unwrap();
|
||||||
|
uapi::fcntl_add_seals(
|
||||||
|
memfd.raw(),
|
||||||
|
c::F_SEAL_SEAL | c::F_SEAL_GROW | c::F_SEAL_SHRINK | c::F_SEAL_WRITE,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.tran.send(Keymap {
|
||||||
|
self_id: self.id,
|
||||||
|
format: wl_keyboard::XKB_V1,
|
||||||
|
fd: Rc::new(memfd),
|
||||||
|
size: map.len() as _,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key(&self, key: u32, state: KeyState) -> Result<(), TestError> {
|
||||||
|
let state = match state {
|
||||||
|
KeyState::Released => wl_keyboard::RELEASED,
|
||||||
|
KeyState::Pressed => wl_keyboard::PRESSED,
|
||||||
|
};
|
||||||
|
self.tran.send(Key {
|
||||||
|
self_id: self.id,
|
||||||
|
time: (now_usec() / 1000) as u32,
|
||||||
|
key,
|
||||||
|
state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modifiers(
|
||||||
|
&self,
|
||||||
|
mods_depressed: u32,
|
||||||
|
mods_latched: u32,
|
||||||
|
mods_locked: u32,
|
||||||
|
group: u32,
|
||||||
|
) -> Result<(), TestError> {
|
||||||
|
self.tran.send(Modifiers {
|
||||||
|
self_id: self.id,
|
||||||
|
mods_depressed,
|
||||||
|
mods_latched,
|
||||||
|
mods_locked,
|
||||||
|
group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestVirtualKeyboard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestVirtualKeyboard, ZwpVirtualKeyboardV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestObject for TestVirtualKeyboard {}
|
||||||
49
src/it/test_ifs/test_virtual_keyboard_manager.rs
Normal file
49
src/it/test_ifs/test_virtual_keyboard_manager.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
it::{
|
||||||
|
test_error::TestResult,
|
||||||
|
test_ifs::{test_seat::TestSeat, test_virtual_keyboard::TestVirtualKeyboard},
|
||||||
|
test_object::TestObject,
|
||||||
|
test_transport::TestTransport,
|
||||||
|
},
|
||||||
|
wire::{zwp_virtual_keyboard_manager_v1::*, ZwpVirtualKeyboardManagerV1Id},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct TestVirtualKeyboardManager {
|
||||||
|
pub id: ZwpVirtualKeyboardManagerV1Id,
|
||||||
|
pub tran: Rc<TestTransport>,
|
||||||
|
pub destroyed: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestVirtualKeyboardManager {
|
||||||
|
pub fn new(tran: &Rc<TestTransport>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: tran.id(),
|
||||||
|
tran: tran.clone(),
|
||||||
|
destroyed: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_virtual_keyboard(&self, seat: &TestSeat) -> TestResult<Rc<TestVirtualKeyboard>> {
|
||||||
|
let obj = Rc::new(TestVirtualKeyboard {
|
||||||
|
id: self.tran.id(),
|
||||||
|
tran: self.tran.clone(),
|
||||||
|
destroyed: Cell::new(false),
|
||||||
|
});
|
||||||
|
self.tran.add_obj(obj.clone())?;
|
||||||
|
self.tran.send(CreateVirtualKeyboard {
|
||||||
|
self_id: self.id,
|
||||||
|
seat: seat.id,
|
||||||
|
id: obj.id,
|
||||||
|
})?;
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_object! {
|
||||||
|
TestVirtualKeyboardManager, ZwpVirtualKeyboardManagerV1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestObject for TestVirtualKeyboardManager {}
|
||||||
|
|
@ -69,6 +69,7 @@ impl TestTransport {
|
||||||
dmabuf: Default::default(),
|
dmabuf: Default::default(),
|
||||||
drag_manager: Default::default(),
|
drag_manager: Default::default(),
|
||||||
alpha_modifier: Default::default(),
|
alpha_modifier: Default::default(),
|
||||||
|
virtual_keyboard_manager: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
});
|
});
|
||||||
self.send(wl_display::GetRegistry {
|
self.send(wl_display::GetRegistry {
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ mod t0036_idle;
|
||||||
mod t0037_toplevel_drag;
|
mod t0037_toplevel_drag;
|
||||||
mod t0038_subsurface_parent_state;
|
mod t0038_subsurface_parent_state;
|
||||||
mod t0039_alpha_modifier;
|
mod t0039_alpha_modifier;
|
||||||
|
mod t0040_virtual_keyboard;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -129,5 +130,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0037_toplevel_drag,
|
t0037_toplevel_drag,
|
||||||
t0038_subsurface_parent_state,
|
t0038_subsurface_parent_state,
|
||||||
t0039_alpha_modifier,
|
t0039_alpha_modifier,
|
||||||
|
t0040_virtual_keyboard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
188
src/it/tests/t0040_virtual_keyboard.rs
Normal file
188
src/it/tests/t0040_virtual_keyboard.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
backend::KeyState,
|
||||||
|
clientmem::ClientMem,
|
||||||
|
it::{test_error::TestResult, testrun::TestRun},
|
||||||
|
xkbcommon::XkbContext,
|
||||||
|
},
|
||||||
|
bstr::ByteSlice,
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
testcase!();
|
||||||
|
|
||||||
|
async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
|
let virtual_keymap_str = {
|
||||||
|
let xkb = XkbContext::new()?;
|
||||||
|
let map = xkb.keymap_from_str(VIRTUAL_KEYMAP).unwrap();
|
||||||
|
read_keymap(map.map.raw(), map.map_len)
|
||||||
|
};
|
||||||
|
|
||||||
|
let ds = run.create_default_setup().await?;
|
||||||
|
|
||||||
|
let s_client = run.create_client().await?;
|
||||||
|
let s_seat = s_client.get_default_seat().await?;
|
||||||
|
let s_win = s_client.create_window().await?;
|
||||||
|
s_win.map2().await?;
|
||||||
|
s_client.sync().await;
|
||||||
|
|
||||||
|
let s_keymap = s_seat.kb.keymap.expect()?;
|
||||||
|
let s_key = s_seat.kb.key.expect()?;
|
||||||
|
let s_modifiers = s_seat.kb.modifiers.expect()?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let v_client = run.create_client().await?;
|
||||||
|
let v_seat = v_client.get_default_seat().await?;
|
||||||
|
let v_kb = v_client
|
||||||
|
.registry
|
||||||
|
.get_virtual_keyboard_manager()
|
||||||
|
.await?
|
||||||
|
.create_virtual_keyboard(&v_seat.seat)?;
|
||||||
|
v_kb.set_keymap(VIRTUAL_KEYMAP)?;
|
||||||
|
v_kb.key(10, KeyState::Pressed)?;
|
||||||
|
v_kb.key(10, KeyState::Released)?;
|
||||||
|
v_kb.modifiers(1, 2, 3, 0)?;
|
||||||
|
v_kb.key(10, KeyState::Pressed)?;
|
||||||
|
v_kb.key(10, KeyState::Released)?;
|
||||||
|
v_kb.modifiers(0, 0, 0, 1)?;
|
||||||
|
v_client.sync().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_client.sync().await;
|
||||||
|
let (start, keymap) = s_keymap.next().expect("virtual keymap");
|
||||||
|
tassert_eq!(
|
||||||
|
&read_keymap(keymap.fd.raw(), keymap.size as _),
|
||||||
|
&virtual_keymap_str
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let (pos, mods) = s_modifiers.next().expect("mods 0");
|
||||||
|
tassert_eq!(pos, start + 1);
|
||||||
|
tassert_eq!(
|
||||||
|
(
|
||||||
|
mods.mods_depressed,
|
||||||
|
mods.mods_latched,
|
||||||
|
mods.mods_locked,
|
||||||
|
mods.group
|
||||||
|
),
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 1");
|
||||||
|
tassert_eq!(pos, start + 2);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 2");
|
||||||
|
tassert_eq!(pos, start + 3);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, mods) = s_modifiers.next().expect("mods 1");
|
||||||
|
tassert_eq!(pos, start + 4);
|
||||||
|
tassert_eq!(
|
||||||
|
(
|
||||||
|
mods.mods_depressed,
|
||||||
|
mods.mods_latched,
|
||||||
|
mods.mods_locked,
|
||||||
|
mods.group
|
||||||
|
),
|
||||||
|
(1, 2, 3, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 3");
|
||||||
|
tassert_eq!(pos, start + 5);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 4");
|
||||||
|
tassert_eq!(pos, start + 6);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 0));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, mods) = s_modifiers.next().expect("mods 2");
|
||||||
|
tassert_eq!(pos, start + 7);
|
||||||
|
tassert_eq!(
|
||||||
|
(
|
||||||
|
mods.mods_depressed,
|
||||||
|
mods.mods_latched,
|
||||||
|
mods.mods_locked,
|
||||||
|
mods.group
|
||||||
|
),
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ds.kb.press(10);
|
||||||
|
|
||||||
|
s_client.sync().await;
|
||||||
|
let (pos, keymap) = s_keymap.next().expect("seat keymap");
|
||||||
|
tassert_eq!(pos, start + 8);
|
||||||
|
tassert!(read_keymap(keymap.fd.raw(), keymap.size as _) != virtual_keymap_str);
|
||||||
|
{
|
||||||
|
let (pos, mods) = s_modifiers.next().expect("mods 0");
|
||||||
|
tassert_eq!(pos, start + 9);
|
||||||
|
tassert_eq!(
|
||||||
|
(
|
||||||
|
mods.mods_depressed,
|
||||||
|
mods.mods_latched,
|
||||||
|
mods.mods_locked,
|
||||||
|
mods.group
|
||||||
|
),
|
||||||
|
(0, 0, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 5");
|
||||||
|
tassert_eq!(pos, start + 10);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (pos, key) = s_key.next().expect("key 6");
|
||||||
|
tassert_eq!(pos, start + 11);
|
||||||
|
tassert_eq!((key.key, key.state), (10, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_keymap(fd: i32, size: usize) -> String {
|
||||||
|
let client_mem = ClientMem::new(fd, size - 1, true).unwrap();
|
||||||
|
let client_mem = Rc::new(client_mem).offset(0);
|
||||||
|
let mut v = vec![];
|
||||||
|
client_mem.read(&mut v).unwrap();
|
||||||
|
v.as_bstr().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
const VIRTUAL_KEYMAP: &str = r#"
|
||||||
|
xkb_keymap {
|
||||||
|
xkb_keycodes {
|
||||||
|
<2> = 10; # 1
|
||||||
|
<29> = 37; # LEFTCTRL
|
||||||
|
};
|
||||||
|
|
||||||
|
xkb_types {
|
||||||
|
type "TWO_LEVEL" {
|
||||||
|
modifiers = Control;
|
||||||
|
map[Control] = Level2;
|
||||||
|
level_name[Level1] = "Base";
|
||||||
|
level_name[Level2] = "Control";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
xkb_compatibility {
|
||||||
|
interpret.repeat = False;
|
||||||
|
interpret.locking = False;
|
||||||
|
interpret Control_L {
|
||||||
|
action = SetMods(modifiers=Control);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
xkb_symbols {
|
||||||
|
key <2> { [ 2, at ] };
|
||||||
|
key <29> { [ Control_L ] };
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
"#;
|
||||||
|
|
@ -37,6 +37,7 @@ type xkb_keycode_t = u32;
|
||||||
type xkb_layout_index_t = u32;
|
type xkb_layout_index_t = u32;
|
||||||
type xkb_level_index_t = u32;
|
type xkb_level_index_t = u32;
|
||||||
type xkb_keysym_t = u32;
|
type xkb_keysym_t = u32;
|
||||||
|
type xkb_mod_mask_t = u32;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct xkb_rule_names {
|
struct xkb_rule_names {
|
||||||
|
|
@ -97,6 +98,15 @@ extern "C" {
|
||||||
fn xkb_state_serialize_mods(state: *mut xkb_state, components: xkb_state_component) -> u32;
|
fn xkb_state_serialize_mods(state: *mut xkb_state, components: xkb_state_component) -> u32;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn xkb_state_serialize_layout(state: *mut xkb_state, components: xkb_state_component) -> u32;
|
fn xkb_state_serialize_layout(state: *mut xkb_state, components: xkb_state_component) -> u32;
|
||||||
|
fn xkb_state_update_mask(
|
||||||
|
state: *mut xkb_state,
|
||||||
|
depressed_mods: xkb_mod_mask_t,
|
||||||
|
latched_mods: xkb_mod_mask_t,
|
||||||
|
locked_mods: xkb_mod_mask_t,
|
||||||
|
depressed_layout: xkb_layout_index_t,
|
||||||
|
latched_layout: xkb_layout_index_t,
|
||||||
|
locked_layout: xkb_layout_index_t,
|
||||||
|
) -> xkb_state_component;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct XkbContext {
|
pub struct XkbContext {
|
||||||
|
|
@ -257,10 +267,8 @@ impl XkbState {
|
||||||
self.mods
|
self.mods
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn fetch(&mut self, changes: xkb_state_component) -> Option<ModifierState> {
|
||||||
pub fn update(&mut self, key: u32, direction: XkbKeyDirection) -> Option<ModifierState> {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let changes = xkb_state_update_key(self.state, key + 8, direction.raw() as _);
|
|
||||||
if changes != 0 {
|
if changes != 0 {
|
||||||
self.mods.mods_depressed =
|
self.mods.mods_depressed =
|
||||||
xkb_state_serialize_mods(self.state, XKB_STATE_MODS_DEPRESSED.raw() as _);
|
xkb_state_serialize_mods(self.state, XKB_STATE_MODS_DEPRESSED.raw() as _);
|
||||||
|
|
@ -279,6 +287,34 @@ impl XkbState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, key: u32, direction: XkbKeyDirection) -> Option<ModifierState> {
|
||||||
|
unsafe {
|
||||||
|
let changes = xkb_state_update_key(self.state, key + 8, direction.raw() as _);
|
||||||
|
self.fetch(changes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(
|
||||||
|
&mut self,
|
||||||
|
mods_depressed: u32,
|
||||||
|
mods_latched: u32,
|
||||||
|
mods_locked: u32,
|
||||||
|
group: u32,
|
||||||
|
) -> Option<ModifierState> {
|
||||||
|
unsafe {
|
||||||
|
let changes = xkb_state_update_mask(
|
||||||
|
self.state,
|
||||||
|
mods_depressed,
|
||||||
|
mods_latched,
|
||||||
|
mods_locked,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
group,
|
||||||
|
);
|
||||||
|
self.fetch(changes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unmodified_keysyms(&self, key: u32) -> &[xkb_keysym_t] {
|
pub fn unmodified_keysyms(&self, key: u32) -> &[xkb_keysym_t] {
|
||||||
let mut res = ptr::null();
|
let mut res = ptr::null();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
||||||
4
wire/zwp_virtual_keyboard_manager_v1.txt
Normal file
4
wire/zwp_virtual_keyboard_manager_v1.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
request create_virtual_keyboard {
|
||||||
|
seat: id(wl_seat),
|
||||||
|
id: id(zwp_virtual_keyboard_v1),
|
||||||
|
}
|
||||||
21
wire/zwp_virtual_keyboard_v1.txt
Normal file
21
wire/zwp_virtual_keyboard_v1.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
request keymap {
|
||||||
|
format: u32,
|
||||||
|
fd: fd,
|
||||||
|
size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request key {
|
||||||
|
time: u32,
|
||||||
|
key: u32,
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request modifiers {
|
||||||
|
mods_depressed: u32,
|
||||||
|
mods_latched: u32,
|
||||||
|
mods_locked: u32,
|
||||||
|
group: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request destroy {
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue