submission: Handle submitting strings
This commit is contained in:
@ -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>,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user