From 42cb73cd8c305734e9d91830ee77d2d2598ec232 Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Tue, 14 Jan 2020 13:13:42 +0000 Subject: [PATCH] submission: Handle submitting strings --- src/action.rs | 3 +- src/imservice.c | 5 +++ src/imservice.rs | 30 +++++++++++++---- src/keyboard.rs | 14 ++++++++ src/layout.rs | 12 ++----- src/submission.rs | 85 +++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 126 insertions(+), 23 deletions(-) diff --git a/src/action.rs b/src/action.rs index 97e32dc7..5045007a 100644 --- a/src/action.rs +++ b/src/action.rs @@ -31,7 +31,8 @@ pub enum Action { SetModifier(Modifier), /// Submit some text Submit { - /// Text to submit with input-method + /// Text to submit with input-method. + /// If None, then keys are to be submitted instead. text: Option, /// The key events this symbol submits when submitting text is not possible keys: Vec, diff --git a/src/imservice.c b/src/imservice.c index e9683da6..9a92be67 100644 --- a/src/imservice.c +++ b/src/imservice.c @@ -55,6 +55,11 @@ eek_input_method_commit_string(struct zwp_input_method_v2 *zwp_input_method_v2, zwp_input_method_v2_commit_string(zwp_input_method_v2, text); } +void +eek_input_method_commit(struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t serial) +{ + zwp_input_method_v2_commit(zwp_input_method_v2, serial); +} /// Declared explicitly because _destroy is inline, /// making it unavailable in Rust diff --git a/src/imservice.rs b/src/imservice.rs index 547b494e..2adf9717 100644 --- a/src/imservice.rs +++ b/src/imservice.rs @@ -1,5 +1,9 @@ +/*! Manages zwp_input_method_v2 protocol. + * + * Library module. + */ + use std::boxed::Box; -use std::ffi; use std::ffi::CString; use std::num::Wrapping; use std::string::String; @@ -31,7 +35,7 @@ pub mod c { #[allow(improper_ctypes)] // IMService will never be dereferenced in C pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); pub fn eek_input_method_commit_string(im: *mut InputMethod, text: *const c_char); - + pub fn eek_input_method_commit(im: *mut InputMethod, serial: u32); fn eekboard_context_service_set_hint_purpose(state: *const StateManager, hint: u32, purpose: u32); fn server_context_service_show_keyboard(imservice: *const UIManager); fn server_context_service_hide_keyboard(imservice: *const UIManager); @@ -332,8 +336,6 @@ pub struct IMService { pub enum SubmitError { /// The input method had not been activated NotActive, - /// Submitted text has null bytes - NullBytes(ffi::NulError), } impl IMService { @@ -361,10 +363,9 @@ impl IMService { imservice } - pub fn commit_string(&self, text: &str) -> Result<(), SubmitError> { + pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> { match self.current.active { true => { - let text = CString::new(text).map_err(SubmitError::NullBytes)?; unsafe { c::eek_input_method_commit_string(self.im, text.as_ptr()) } @@ -373,4 +374,21 @@ impl IMService { false => Err(SubmitError::NotActive), } } + + pub fn commit(&mut self) -> Result<(), SubmitError> { + match self.current.active { + true => { + unsafe { + c::eek_input_method_commit(self.im, self.serial.0) + } + self.serial += Wrapping(1u32); + Ok(()) + }, + false => Err(SubmitError::NotActive), + } + } + + pub fn is_active(&self) -> bool { + self.current.active + } } diff --git a/src/keyboard.rs b/src/keyboard.rs index 79cd36c4..87c26e0d 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,13 +1,16 @@ /*! State of the emulated keyboard and keys. * Regards the keyboard as if it was composed of switches. */ +use std::cell::RefCell; use std::collections::HashMap; use std::fmt; use std::io; +use std::rc::Rc; use std::string::FromUtf8Error; use ::action::Action; +// Traits use std::io::Write; use std::iter::{ FromIterator, IntoIterator }; @@ -19,6 +22,11 @@ pub enum PressType { pub type KeyCode = u32; +/// When the submitted actions of keys need to be tracked, +/// they need a stable, comparable ID +#[derive(PartialEq)] +pub struct KeyStateId(*const KeyState); + #[derive(Debug, Clone)] pub struct KeyState { pub pressed: PressType, @@ -48,6 +56,12 @@ impl KeyState { ..self } } + + /// KeyStates instances are the unique identifiers of pressed keys, + /// and the actions submitted with them. + pub fn get_id(keystate: &Rc>) -> KeyStateId { + KeyStateId(keystate.as_ptr() as *const KeyState) + } } /// Sorts an iterator by converting it to a Vector and back diff --git a/src/layout.rs b/src/layout.rs index 413e924a..8f8b0055 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -861,11 +861,7 @@ mod seat { eprintln!("Warning: key {:?} was already pressed", rckey); } let mut key = rckey.borrow_mut(); - submission.virtual_keyboard.switch( - &key.keycodes, - PressType::Pressed, - time, - ); + submission.handle_press(&key, KeyState::get_id(rckey), time); key.pressed = PressType::Pressed; } @@ -893,11 +889,7 @@ mod seat { match action { Action::Submit { text: _, keys: _ } => { unstick_locks(layout).apply(); - submission.virtual_keyboard.switch( - &key.keycodes, - PressType::Released, - time, - ); + submission.handle_release(KeyState::get_id(rckey), time); }, Action::SetView(view) => { try_set_view(layout, view) diff --git a/src/submission.rs b/src/submission.rs index db7e6cc4..01dd979b 100644 --- a/src/submission.rs +++ b/src/submission.rs @@ -16,8 +16,11 @@ * The text-input interface may be enabled and disabled at arbitrary times, * and those events SHOULD NOT cause any lost events. * */ - + +use ::action::Action; +use ::imservice; use ::imservice::IMService; +use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType }; use ::vkeyboard::VirtualKeyboard; /// Gathers stuff defined in C or called by C @@ -57,6 +60,7 @@ pub mod c { Submission { imservice, virtual_keyboard: VirtualKeyboard(vk), + pressed: Vec::new(), } )) } @@ -92,9 +96,78 @@ pub mod c { #[derive(Clone, Copy)] pub struct Timestamp(pub u32); -pub struct Submission { - // used by C callbacks internally, TODO: make use with virtual keyboard - #[allow(dead_code)] - imservice: Option>, - pub virtual_keyboard: VirtualKeyboard, +enum SubmittedAction { + /// A collection of keycodes that were pressed + VirtualKeyboard(Vec), + IMService, +} + +pub struct Submission { + imservice: Option>, + virtual_keyboard: VirtualKeyboard, + pressed: Vec<(KeyStateId, SubmittedAction)>, +} + +impl Submission { + /// Sends a submit text event if possible; + /// otherwise sends key press and makes a note of it + pub fn handle_press( + &mut self, + key: &KeyState, key_id: KeyStateId, + time: Timestamp, + ) { + let key_string = match &key.action { + Action::Submit { text, keys: _ } => text, + _ => { + eprintln!("BUG: Submitted key with action other than Submit"); + return; + }, + }; + + let text_was_committed = match (&mut self.imservice, key_string) { + (Some(imservice), Some(text)) => { + let submit_result = imservice.commit_string(text) + .and_then(|_| imservice.commit()); + match submit_result { + Ok(()) => true, + Err(imservice::SubmitError::NotActive) => false, + } + }, + (_, _) => false, + }; + + let submit_action = match text_was_committed { + true => SubmittedAction::IMService, + false => { + self.virtual_keyboard.switch( + &key.keycodes, + PressType::Pressed, + time, + ); + SubmittedAction::VirtualKeyboard(key.keycodes.clone()) + }, + }; + + self.pressed.push((key_id, submit_action)); + } + + pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) { + let index = self.pressed.iter().position(|(id, _)| *id == key_id); + if let Some(index) = index { + let (_id, action) = self.pressed.remove(index); + match action { + // string already sent, nothing to do + SubmittedAction::IMService => {}, + // no matter if the imservice got activated, + // keys must be released + SubmittedAction::VirtualKeyboard(keycodes) => { + self.virtual_keyboard.switch( + &keycodes, + PressType::Released, + time, + ) + }, + } + }; + } }