virtual_keyboard: Submit multi-codepoint strings

This commit is contained in:
Dorota Czaplejewicz
2019-10-09 15:08:41 +00:00
parent 212e55d7cf
commit 9cd439767e
4 changed files with 157 additions and 71 deletions

View File

@ -131,11 +131,15 @@ fn load_layout(
fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) { fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) {
match attempt { match attempt {
Some(( Some((
LoadError::BadData(Error::Missing(_e)), LoadError::BadData(Error::Missing(e)),
DataSource::File(_file) DataSource::File(file)
)) => { )) => {
eprintln!(
"Tried file {:?}, but it's missing: {}",
file, e
);
// Missing file, not to worry. TODO: print in debug logging level // Missing file, not to worry. TODO: print in debug logging level
} },
Some((e, source)) => { Some((e, source)) => {
eprintln!( eprintln!(
"Failed to load layout from {}: {}, trying builtin", "Failed to load layout from {}: {}, trying builtin",
@ -300,32 +304,56 @@ impl Layout {
let button_names: HashSet<&str> let button_names: HashSet<&str>
= HashSet::from_iter(button_names); = HashSet::from_iter(button_names);
let keycodes = generate_keycodes( let button_actions: Vec<(&str, ::action::Action)>
button_names.iter() = button_names.iter().map(|name| {(
.map(|name| *name) *name,
.filter(|name| { create_action(
match self.buttons.get(*name) {
// buttons with defined action can't emit keysyms
// and so don't need keycodes
Some(ButtonMeta { action: Some(_), .. }) => false,
_ => true,
}
})
);
let button_states = button_names.iter().map(|name| {(
String::from(*name),
Rc::new(RefCell::new(KeyState {
pressed: false,
locked: false,
keycode: keycodes.get(*name).map(|k| *k),
action: create_action(
&self.buttons, &self.buttons,
name, name,
self.views.keys().collect() self.views.keys().collect()
), )
})) )}).collect();
)});
let keymap: HashMap<String, u32> = generate_keycodes(
button_actions.iter()
.filter_map(|(_name, action)| {
match action {
::action::Action::Submit {
text: _, keys,
} => Some(keys),
_ => None,
}
})
.flatten()
.map(|named_keysym| named_keysym.0.as_str())
);
let button_states = button_actions.into_iter().map(|(name, action)| {
let keycodes = match &action {
::action::Action::Submit { text: _, keys } => {
keys.iter().map(|named_keycode| {
*keymap.get(named_keycode.0.as_str())
.expect(
format!(
"keycode {} in key {} missing from keymap",
named_keycode.0,
name
).as_str()
)
}).collect()
},
_ => Vec::new(),
};
(
name.into(),
Rc::new(RefCell::new(KeyState {
pressed: false,
locked: false,
keycodes,
action,
}))
)
});
let button_states = let button_states =
HashMap::<String, Rc<RefCell<KeyState>>>::from_iter( HashMap::<String, Rc<RefCell<KeyState>>>::from_iter(
@ -410,33 +438,45 @@ fn create_action(
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
} }
let keysym = match &symbol_meta.action { let keysyms = match &symbol_meta.action {
Some(_) => None, // Non-submit action
None => Some(match &symbol_meta.keysym { Some(_) => Vec::new(),
Some(keysym) => match keysym_valid(keysym.as_str()) { // Submit action
None => match &symbol_meta.keysym {
// Keysym given explicitly
Some(keysym) => vec!(match keysym_valid(keysym.as_str()) {
true => keysym.clone(), true => keysym.clone(),
false => { false => {
eprintln!("Keysym name invalid: {}", keysym); eprintln!("Keysym name invalid: {}", keysym);
"space".into() // placeholder "space".into() // placeholder
}, },
}, }),
// Keysyms left open to derive
// TODO: when button name is meant diretly as xkb keysym name,
// mark it so, e.g. with a "#"
None => match keysym_valid(name) { None => match keysym_valid(name) {
true => String::from(name), // Button name is actually a valid xkb name
false => match name.chars().count() { true => vec!(String::from(name)),
1 => format!("U{:04X}", name.chars().next().unwrap() as u32), // Button name is not a valid xkb name,
// If the name is longer than 1 char, // so assume it's a literal string to be submitted
// then it's not a single Unicode char, false => {
// but was trying to be an identifier if name.chars().count() == 0 {
_ => { // A name read from yaml with no valid Unicode.
eprintln!( // Highly improbable, but let's be safe.
"Could not derive a valid keysym for key {}", eprintln!("Key {} doesn't have any characters", name);
name vec!("space".into()) // placeholder
); } else {
"space".into() // placeholder name.chars().map(|codepoint| {
let codepoint_string = codepoint.to_string();
match keysym_valid(codepoint_string.as_str()) {
true => codepoint_string,
false => format!("U{:04X}", codepoint as u32),
}
}).collect()
} }
}, },
}, },
}), },
}; };
match &symbol_meta.action { match &symbol_meta.action {
@ -459,9 +499,7 @@ fn create_action(
}, },
None => ::action::Action::Submit { None => ::action::Action::Submit {
text: None, text: None,
keys: vec!( keys: keysyms.into_iter().map(::action::KeySym).collect(),
::action::KeySym(keysym.unwrap()),
),
}, },
} }
} }
@ -633,6 +671,23 @@ mod tests {
); );
} }
/// Test multiple codepoints
#[test]
fn test_layout_unicode_multi() {
let out = Layout::from_file(PathBuf::from("tests/layout_key3.yaml"))
.unwrap()
.build()
.unwrap();
assert_eq!(
out.views["base"]
.rows[0]
.buttons[0]
.state.borrow()
.keycodes.len(),
2
);
}
#[test] #[test]
fn parsing_fallback() { fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME) assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)

View File

@ -78,12 +78,30 @@ pub mod c {
let mut key = key.borrow_mut(); let mut key = key.borrow_mut();
key.pressed = press != 0; key.pressed = press != 0;
if let Some(keycode) = key.keycode { let keycodes_count = key.keycodes.len();
for keycode in key.keycodes.iter() {
let keycode = keycode - 8; let keycode = keycode - 8;
unsafe { match (key.pressed, keycodes_count) {
eek_virtual_keyboard_v1_key( // Pressing a key made out of a single keycode is simple:
virtual_keyboard, timestamp, keycode, press // press on press, release on release.
); (_, 1) => unsafe {
eek_virtual_keyboard_v1_key(
virtual_keyboard, timestamp, keycode, press
);
},
// A key made of multiple keycodes
// has to submit them one after the other
(true, _) => unsafe {
eek_virtual_keyboard_v1_key(
virtual_keyboard, timestamp, keycode, 1
);
eek_virtual_keyboard_v1_key(
virtual_keyboard, timestamp, keycode, 0
);
},
// Design choice here: submit multiple all at press time
// and do nothing at release time
(false, _) => {},
} }
} }
} }
@ -93,7 +111,8 @@ pub mod c {
pub struct KeyState { pub struct KeyState {
pub pressed: bool, pub pressed: bool,
pub locked: bool, pub locked: bool,
pub keycode: Option<u32>, /// A cache of raw keycodes derived from Action::Sumbit given a keymap
pub keycodes: Vec<u32>,
/// Static description of what the key does when pressed or released /// Static description of what the key does when pressed or released
pub action: Action, pub action: Action,
} }
@ -147,22 +166,16 @@ pub fn generate_keymap(
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
let state = state.borrow(); let state = state.borrow();
if let Action::Submit { text: _, keys } = &state.action { if let Action::Submit { text: _, keys } = &state.action {
match keys.len() { if let 0 = keys.len() { eprintln!("Key {} has no keysyms", name); };
0 => eprintln!("Key {} has no keysyms", name), for (named_keysym, keycode) in keys.iter().zip(&state.keycodes) {
a => { write!(
// TODO: don't ignore any keysyms buf,
if a > 1 { "
eprintln!("Key {} multiple keysyms", name);
}
write!(
buf,
"
<{}> = {};", <{}> = {};",
keys[0].0, named_keysym.0,
state.keycode.unwrap() keycode,
)?; )?;
}, }
};
} }
} }
@ -179,7 +192,7 @@ pub fn generate_keymap(
for (name, state) in keystates.iter() { for (name, state) in keystates.iter() {
if let Action::Submit { text: _, keys } = &state.borrow().action { if let Action::Submit { text: _, keys } = &state.borrow().action {
if let Some(keysym) = keys.iter().next() { for keysym in keys.iter() {
write!( write!(
buf, buf,
" "
@ -209,5 +222,6 @@ pub fn generate_keymap(
}};" }};"
)?; )?;
//println!("{}", String::from_utf8(buf.clone()).unwrap());
String::from_utf8(buf).map_err(FormattingError::Utf) String::from_utf8(buf).map_err(FormattingError::Utf)
} }

View File

@ -484,7 +484,7 @@ pub mod c {
Rc::new(RefCell::new(::keyboard::KeyState { Rc::new(RefCell::new(::keyboard::KeyState {
pressed: false, pressed: false,
locked: false, locked: false,
keycode: None, keycodes: Vec::new(),
action: Action::SetLevel("default".into()), action: Action::SetLevel("default".into()),
})) }))
} }

17
tests/layout_key3.yaml Normal file
View File

@ -0,0 +1,17 @@
---
# punctuation
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
views:
base:
- "か゚" # 2 codepoints
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }