submission: Handle submitting strings

This commit is contained in:
Dorota Czaplejewicz
2020-01-14 13:13:42 +00:00
parent d1bc23e9d8
commit 42cb73cd8c
6 changed files with 126 additions and 23 deletions

View File

@ -31,7 +31,8 @@ pub enum Action {
SetModifier(Modifier), SetModifier(Modifier),
/// Submit some text /// Submit some text
Submit { Submit {
/// Text to submit with input-method /// Text to submit with input-method.
/// If None, then keys are to be submitted instead.
text: Option<CString>, text: Option<CString>,
/// The key events this symbol submits when submitting text is not possible /// The key events this symbol submits when submitting text is not possible
keys: Vec<KeySym>, keys: Vec<KeySym>,

View File

@ -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); 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, /// Declared explicitly because _destroy is inline,
/// making it unavailable in Rust /// making it unavailable in Rust

View File

@ -1,5 +1,9 @@
/*! Manages zwp_input_method_v2 protocol.
*
* Library module.
*/
use std::boxed::Box; use std::boxed::Box;
use std::ffi;
use std::ffi::CString; use std::ffi::CString;
use std::num::Wrapping; use std::num::Wrapping;
use std::string::String; use std::string::String;
@ -31,7 +35,7 @@ pub mod c {
#[allow(improper_ctypes)] // IMService will never be dereferenced in C #[allow(improper_ctypes)] // IMService will never be dereferenced in C
pub fn imservice_connect_listeners(im: *mut InputMethod, imservice: *const IMService); 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_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 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_show_keyboard(imservice: *const UIManager);
fn server_context_service_hide_keyboard(imservice: *const UIManager); fn server_context_service_hide_keyboard(imservice: *const UIManager);
@ -332,8 +336,6 @@ pub struct IMService {
pub enum SubmitError { pub enum SubmitError {
/// The input method had not been activated /// The input method had not been activated
NotActive, NotActive,
/// Submitted text has null bytes
NullBytes(ffi::NulError),
} }
impl IMService { impl IMService {
@ -361,10 +363,9 @@ impl IMService {
imservice imservice
} }
pub fn commit_string(&self, text: &str) -> Result<(), SubmitError> { pub fn commit_string(&self, text: &CString) -> Result<(), SubmitError> {
match self.current.active { match self.current.active {
true => { true => {
let text = CString::new(text).map_err(SubmitError::NullBytes)?;
unsafe { unsafe {
c::eek_input_method_commit_string(self.im, text.as_ptr()) c::eek_input_method_commit_string(self.im, text.as_ptr())
} }
@ -373,4 +374,21 @@ impl IMService {
false => Err(SubmitError::NotActive), 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
}
} }

View File

@ -1,13 +1,16 @@
/*! State of the emulated keyboard and keys. /*! State of the emulated keyboard and keys.
* Regards the keyboard as if it was composed of switches. */ * Regards the keyboard as if it was composed of switches. */
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::io; use std::io;
use std::rc::Rc;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use ::action::Action; use ::action::Action;
// Traits
use std::io::Write; use std::io::Write;
use std::iter::{ FromIterator, IntoIterator }; use std::iter::{ FromIterator, IntoIterator };
@ -19,6 +22,11 @@ pub enum PressType {
pub type KeyCode = u32; 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)] #[derive(Debug, Clone)]
pub struct KeyState { pub struct KeyState {
pub pressed: PressType, pub pressed: PressType,
@ -48,6 +56,12 @@ impl KeyState {
..self ..self
} }
} }
/// KeyStates instances are the unique identifiers of pressed keys,
/// and the actions submitted with them.
pub fn get_id(keystate: &Rc<RefCell<KeyState>>) -> KeyStateId {
KeyStateId(keystate.as_ptr() as *const KeyState)
}
} }
/// Sorts an iterator by converting it to a Vector and back /// Sorts an iterator by converting it to a Vector and back

View File

@ -861,11 +861,7 @@ mod seat {
eprintln!("Warning: key {:?} was already pressed", rckey); eprintln!("Warning: key {:?} was already pressed", rckey);
} }
let mut key = rckey.borrow_mut(); let mut key = rckey.borrow_mut();
submission.virtual_keyboard.switch( submission.handle_press(&key, KeyState::get_id(rckey), time);
&key.keycodes,
PressType::Pressed,
time,
);
key.pressed = PressType::Pressed; key.pressed = PressType::Pressed;
} }
@ -893,11 +889,7 @@ mod seat {
match action { match action {
Action::Submit { text: _, keys: _ } => { Action::Submit { text: _, keys: _ } => {
unstick_locks(layout).apply(); unstick_locks(layout).apply();
submission.virtual_keyboard.switch( submission.handle_release(KeyState::get_id(rckey), time);
&key.keycodes,
PressType::Released,
time,
);
}, },
Action::SetView(view) => { Action::SetView(view) => {
try_set_view(layout, view) try_set_view(layout, view)

View File

@ -16,8 +16,11 @@
* The text-input interface may be enabled and disabled at arbitrary times, * The text-input interface may be enabled and disabled at arbitrary times,
* and those events SHOULD NOT cause any lost events. * and those events SHOULD NOT cause any lost events.
* */ * */
use ::action::Action;
use ::imservice;
use ::imservice::IMService; use ::imservice::IMService;
use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
use ::vkeyboard::VirtualKeyboard; use ::vkeyboard::VirtualKeyboard;
/// Gathers stuff defined in C or called by C /// Gathers stuff defined in C or called by C
@ -57,6 +60,7 @@ pub mod c {
Submission { Submission {
imservice, imservice,
virtual_keyboard: VirtualKeyboard(vk), virtual_keyboard: VirtualKeyboard(vk),
pressed: Vec::new(),
} }
)) ))
} }
@ -92,9 +96,78 @@ pub mod c {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Timestamp(pub u32); pub struct Timestamp(pub u32);
pub struct Submission { enum SubmittedAction {
// used by C callbacks internally, TODO: make use with virtual keyboard /// A collection of keycodes that were pressed
#[allow(dead_code)] VirtualKeyboard(Vec<KeyCode>),
imservice: Option<Box<IMService>>, IMService,
pub virtual_keyboard: VirtualKeyboard, }
pub struct Submission {
imservice: Option<Box<IMService>>,
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,
)
},
}
};
}
} }