wayland: implement virtual-keyboard
This commit is contained in:
parent
826f40adca
commit
6c0e3a4fff
20 changed files with 689 additions and 14 deletions
|
|
@ -41,6 +41,8 @@ pub mod test_toplevel_drag;
|
|||
pub mod test_toplevel_drag_manager;
|
||||
pub mod test_viewport;
|
||||
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_token;
|
||||
pub mod test_xdg_base;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use {
|
|||
test_error::TestResult, test_object::TestObject, test_transport::TestTransport,
|
||||
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},
|
||||
},
|
||||
std::rc::Rc,
|
||||
|
|
@ -22,8 +22,12 @@ pub struct TestKeyboard {
|
|||
pub tran: Rc<TestTransport>,
|
||||
pub server: CloneCell<Option<Rc<WlKeyboard>>>,
|
||||
pub destroyed: Once,
|
||||
pub keymap: TEEH<(usize, Keymap)>,
|
||||
pub key: TEEH<(usize, Key)>,
|
||||
pub modifiers: TEEH<(usize, Modifiers)>,
|
||||
pub enter: TEEH<TestEnterEvent>,
|
||||
pub leave: TEEH<Leave>,
|
||||
pub event_id: NumCell<usize>,
|
||||
}
|
||||
|
||||
impl TestKeyboard {
|
||||
|
|
@ -35,7 +39,8 @@ impl TestKeyboard {
|
|||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -56,12 +61,14 @@ impl TestKeyboard {
|
|||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ use {
|
|||
test_single_pixel_buffer_manager::TestSinglePixelBufferManager,
|
||||
test_subcompositor::TestSubcompositor, test_syncobj_manager::TestSyncobjManager,
|
||||
test_toplevel_drag_manager::TestToplevelDragManager,
|
||||
test_viewporter::TestViewporter, test_xdg_activation::TestXdgActivation,
|
||||
test_xdg_base::TestXdgWmBase,
|
||||
test_viewporter::TestViewporter,
|
||||
test_virtual_keyboard_manager::TestVirtualKeyboardManager,
|
||||
test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase,
|
||||
},
|
||||
test_object::TestObject,
|
||||
test_transport::TestTransport,
|
||||
|
|
@ -52,6 +53,7 @@ pub struct TestRegistrySingletons {
|
|||
pub zwp_linux_dmabuf_v1: u32,
|
||||
pub xdg_toplevel_drag_manager_v1: u32,
|
||||
pub wp_alpha_modifier_v1: u32,
|
||||
pub zwp_virtual_keyboard_manager_v1: u32,
|
||||
}
|
||||
|
||||
pub struct TestRegistry {
|
||||
|
|
@ -76,6 +78,7 @@ pub struct TestRegistry {
|
|||
pub dmabuf: CloneCell<Option<Rc<TestDmabuf>>>,
|
||||
pub drag_manager: CloneCell<Option<Rc<TestToplevelDragManager>>>,
|
||||
pub alpha_modifier: CloneCell<Option<Rc<TestAlphaModifier>>>,
|
||||
pub virtual_keyboard_manager: CloneCell<Option<Rc<TestVirtualKeyboardManager>>>,
|
||||
pub seats: CopyHashMap<GlobalName, Rc<WlSeatGlobal>>,
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +147,7 @@ impl TestRegistry {
|
|||
zwp_linux_dmabuf_v1,
|
||||
xdg_toplevel_drag_manager_v1,
|
||||
wp_alpha_modifier_v1,
|
||||
zwp_virtual_keyboard_manager_v1,
|
||||
};
|
||||
self.singletons.set(Some(singletons.clone()));
|
||||
Ok(singletons)
|
||||
|
|
@ -238,6 +242,13 @@ impl TestRegistry {
|
|||
1,
|
||||
TestAlphaModifier
|
||||
);
|
||||
create_singleton!(
|
||||
get_virtual_keyboard_manager,
|
||||
virtual_keyboard_manager,
|
||||
zwp_virtual_keyboard_manager_v1,
|
||||
1,
|
||||
TestVirtualKeyboardManager
|
||||
);
|
||||
|
||||
pub fn bind<O: TestObject>(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -42,8 +42,12 @@ impl TestSeat {
|
|||
tran: self.tran.clone(),
|
||||
server: Default::default(),
|
||||
destroyed: Default::default(),
|
||||
keymap: Default::default(),
|
||||
key: Default::default(),
|
||||
modifiers: Default::default(),
|
||||
enter: Default::default(),
|
||||
leave: Default::default(),
|
||||
event_id: Default::default(),
|
||||
});
|
||||
self.tran.add_obj(kb.clone())?;
|
||||
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(),
|
||||
drag_manager: Default::default(),
|
||||
alpha_modifier: Default::default(),
|
||||
virtual_keyboard_manager: Default::default(),
|
||||
seats: Default::default(),
|
||||
});
|
||||
self.send(wl_display::GetRegistry {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ mod t0036_idle;
|
|||
mod t0037_toplevel_drag;
|
||||
mod t0038_subsurface_parent_state;
|
||||
mod t0039_alpha_modifier;
|
||||
mod t0040_virtual_keyboard;
|
||||
|
||||
pub trait TestCase: Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
|
|
@ -129,5 +130,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
|||
t0037_toplevel_drag,
|
||||
t0038_subsurface_parent_state,
|
||||
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 ] };
|
||||
};
|
||||
|
||||
};
|
||||
"#;
|
||||
Loading…
Add table
Add a link
Reference in a new issue