virtual_keyboard: Submit multi-codepoint strings
This commit is contained in:
151
src/data.rs
151
src/data.rs
@ -131,11 +131,15 @@ fn load_layout(
|
||||
fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) {
|
||||
match attempt {
|
||||
Some((
|
||||
LoadError::BadData(Error::Missing(_e)),
|
||||
DataSource::File(_file)
|
||||
LoadError::BadData(Error::Missing(e)),
|
||||
DataSource::File(file)
|
||||
)) => {
|
||||
eprintln!(
|
||||
"Tried file {:?}, but it's missing: {}",
|
||||
file, e
|
||||
);
|
||||
// Missing file, not to worry. TODO: print in debug logging level
|
||||
}
|
||||
},
|
||||
Some((e, source)) => {
|
||||
eprintln!(
|
||||
"Failed to load layout from {}: {}, trying builtin",
|
||||
@ -300,32 +304,56 @@ impl Layout {
|
||||
let button_names: HashSet<&str>
|
||||
= HashSet::from_iter(button_names);
|
||||
|
||||
let keycodes = generate_keycodes(
|
||||
button_names.iter()
|
||||
.map(|name| *name)
|
||||
.filter(|name| {
|
||||
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(
|
||||
let button_actions: Vec<(&str, ::action::Action)>
|
||||
= button_names.iter().map(|name| {(
|
||||
*name,
|
||||
create_action(
|
||||
&self.buttons,
|
||||
name,
|
||||
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 =
|
||||
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
|
||||
}
|
||||
|
||||
let keysym = match &symbol_meta.action {
|
||||
Some(_) => None,
|
||||
None => Some(match &symbol_meta.keysym {
|
||||
Some(keysym) => match keysym_valid(keysym.as_str()) {
|
||||
let keysyms = match &symbol_meta.action {
|
||||
// Non-submit action
|
||||
Some(_) => Vec::new(),
|
||||
// Submit action
|
||||
None => match &symbol_meta.keysym {
|
||||
// Keysym given explicitly
|
||||
Some(keysym) => vec!(match keysym_valid(keysym.as_str()) {
|
||||
true => keysym.clone(),
|
||||
false => {
|
||||
eprintln!("Keysym name invalid: {}", keysym);
|
||||
"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) {
|
||||
true => String::from(name),
|
||||
false => match name.chars().count() {
|
||||
1 => format!("U{:04X}", name.chars().next().unwrap() as u32),
|
||||
// If the name is longer than 1 char,
|
||||
// then it's not a single Unicode char,
|
||||
// but was trying to be an identifier
|
||||
_ => {
|
||||
eprintln!(
|
||||
"Could not derive a valid keysym for key {}",
|
||||
name
|
||||
);
|
||||
"space".into() // placeholder
|
||||
// Button name is actually a valid xkb name
|
||||
true => vec!(String::from(name)),
|
||||
// Button name is not a valid xkb name,
|
||||
// so assume it's a literal string to be submitted
|
||||
false => {
|
||||
if name.chars().count() == 0 {
|
||||
// A name read from yaml with no valid Unicode.
|
||||
// Highly improbable, but let's be safe.
|
||||
eprintln!("Key {} doesn't have any characters", name);
|
||||
vec!("space".into()) // placeholder
|
||||
} else {
|
||||
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 {
|
||||
@ -459,9 +499,7 @@ fn create_action(
|
||||
},
|
||||
None => ::action::Action::Submit {
|
||||
text: None,
|
||||
keys: vec!(
|
||||
::action::KeySym(keysym.unwrap()),
|
||||
),
|
||||
keys: keysyms.into_iter().map(::action::KeySym).collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -632,7 +670,24 @@ mod tests {
|
||||
::layout::Label::Text(CString::new("test").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// 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]
|
||||
fn parsing_fallback() {
|
||||
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
|
||||
|
||||
Reference in New Issue
Block a user