virtual_keyboard: Fix desynced modifiers state

This ensures that keymap switches leave modifiers and virtual keys in a known state.
This commit is contained in:
Dorota Czaplejewicz
2020-07-01 11:36:47 +00:00
parent 762e12431d
commit 9512fd8436
6 changed files with 65 additions and 15 deletions

View File

@ -43,7 +43,7 @@ bitflags!{
/// When the submitted actions of keys need to be tracked,
/// they need a stable, comparable ID
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub struct KeyStateId(*const KeyState);
#[derive(Debug, Clone)]

View File

@ -15,5 +15,5 @@ struct submission* get_submission(struct zwp_input_method_manager_v2 *immanager,
// Defined in Rust
struct submission* submission_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk, EekboardContextService *state);
void submission_set_ui(struct submission *self, ServerContextService *ui_context);
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard);
void submission_set_keyboard(struct submission *self, LevelKeyboard *keyboard, uint32_t time);
#endif

View File

@ -23,6 +23,7 @@ use ::action::Modifier;
use ::imservice;
use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::layout::c::LevelKeyboard;
use ::util::vec_remove;
use ::vkeyboard::VirtualKeyboard;
@ -36,7 +37,6 @@ pub mod c {
use std::os::raw::c_void;
use ::imservice::c::InputMethod;
use ::layout::c::LevelKeyboard;
use ::vkeyboard::c::ZwpVirtualKeyboardV1;
// The following defined in C
@ -91,18 +91,23 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn submission_set_keyboard(submission: *mut Submission, keyboard: LevelKeyboard) {
fn submission_set_keyboard(
submission: *mut Submission,
keyboard: LevelKeyboard,
time: u32,
) {
if submission.is_null() {
panic!("Null submission pointer");
}
let submission: &mut Submission = unsafe { &mut *submission };
submission.virtual_keyboard.update_keymap(keyboard);
submission.update_keymap(keyboard, Timestamp(time));
}
}
#[derive(Clone, Copy)]
pub struct Timestamp(pub u32);
#[derive(Clone)]
enum SubmittedAction {
/// A collection of keycodes that were pressed
VirtualKeyboard(Vec<KeyCode>),
@ -243,4 +248,44 @@ impl Submission {
self.modifiers_active.iter().map(|(_id, m)| m.clone())
)
}
fn clear_all_modifiers(&mut self) {
// Looks like an optimization,
// but preemptive cleaning is needed before setting a new keymap,
// so removing this check would break keymap setting.
if self.modifiers_active.is_empty() {
return;
}
self.modifiers_active = Vec::new();
self.virtual_keyboard.set_modifiers_state(Modifiers::empty())
}
fn release_all_virtual_keys(&mut self, time: Timestamp) {
let virtual_pressed = self.pressed
.clone().into_iter()
.filter_map(|(id, action)| {
match action {
SubmittedAction::VirtualKeyboard(_) => Some(id),
_ => None,
}
});
for id in virtual_pressed {
self.handle_release(id, time);
}
}
/// Changes keymap and clears pressed keys and modifiers.
///
/// It's not obvious if clearing is the right thing to do,
/// but keymap update may (or may not) do that,
/// possibly putting self.modifiers_active and self.pressed out of sync,
/// so a consistent stance is adopted to avoid that.
/// Alternatively, modifiers could be restored on the new keymap.
/// That approach might be difficult
/// due to modifiers meaning different things in different keymaps.
pub fn update_keymap(&mut self, keyboard: LevelKeyboard, time: Timestamp) {
self.clear_all_modifiers();
self.release_all_virtual_keys(time);
self.virtual_keyboard.update_keymap(keyboard);
}
}