blob: 250158b819da96802a119e9e01e179fc05464bee [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{Context as _, Error};
use fidl::encoding::Decodable;
use fidl_fuchsia_ui_input2 as ui_input;
use futures::lock::Mutex;
use futures::TryStreamExt;
use serde::{Deserialize, Deserializer};
use serde_json::{self as json};
use std::collections::HashMap;
use std::fs;
use std::sync::Arc;
const DEFAULT_LAYOUT_PATH: &'static str = "/pkg/data/us.json";
#[derive(Clone)]
pub struct KeymapService {
keymap: Arc<Mutex<Keymap>>,
}
impl KeymapService {
pub fn new() -> Result<KeymapService, Error> {
let data = fs::read_to_string(DEFAULT_LAYOUT_PATH)?;
let keymap: Keymap = json::from_str(&data)?;
Ok(KeymapService { keymap: Arc::new(Mutex::new(keymap)) })
}
}
#[derive(Deserialize, Debug, Clone)]
enum Modifiers {
Shift,
LeftShift,
RightShift,
Control,
LeftControl,
RightControl,
Alt,
LeftAlt,
RightAlt,
Meta,
LeftMeta,
RightMeta,
CapsLock,
NumLock,
ScrollLock,
}
impl Into<ui_input::Modifiers> for Modifiers {
fn into(self) -> ui_input::Modifiers {
match self {
Modifiers::Shift => ui_input::Modifiers::Shift,
Modifiers::LeftShift => ui_input::Modifiers::LeftShift,
Modifiers::RightShift => ui_input::Modifiers::RightShift,
Modifiers::Control => ui_input::Modifiers::Control,
Modifiers::LeftControl => ui_input::Modifiers::LeftControl,
Modifiers::RightControl => ui_input::Modifiers::RightControl,
Modifiers::Alt => ui_input::Modifiers::Alt,
Modifiers::LeftAlt => ui_input::Modifiers::LeftAlt,
Modifiers::RightAlt => ui_input::Modifiers::RightAlt,
Modifiers::Meta => ui_input::Modifiers::Meta,
Modifiers::LeftMeta => ui_input::Modifiers::LeftMeta,
Modifiers::RightMeta => ui_input::Modifiers::RightMeta,
Modifiers::CapsLock => ui_input::Modifiers::CapsLock,
Modifiers::NumLock => ui_input::Modifiers::NumLock,
Modifiers::ScrollLock => ui_input::Modifiers::ScrollLock,
}
}
}
type Key = u32;
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
enum Entry {
Symbol(String),
#[serde(with = "hex_serde")]
Action(u32),
}
#[derive(Deserialize, Debug, Clone)]
struct Layout {
modifiers: Vec<Modifiers>,
optional_modifiers: Vec<Modifiers>,
#[serde(deserialize_with = "deserialize_entry")]
entries: HashMap<Key, Entry>,
}
#[derive(Deserialize, Debug)]
struct Keymap {
name: String,
layouts: Vec<Layout>,
}
fn deserialize_entry<'de, D>(deserializer: D) -> Result<HashMap<Key, Entry>, D::Error>
where
D: Deserializer<'de>,
{
fn key_from_hex<'de, D>(deserializer: D) -> Result<Key, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let s_lower = s.to_lowercase();
let without_prefix = s_lower.trim_start_matches("0x");
let index = Key::from_str_radix(&without_prefix, 16).map_err(serde::de::Error::custom);
index.map(|i| {
let key = ui_input::Key::from_primitive(i);
if key.is_none() {
panic!("Keymap uses unknown Key index: {:}", s);
};
i
})
}
#[derive(Deserialize, Hash, Eq, PartialEq)]
struct Wrapper(#[serde(deserialize_with = "key_from_hex")] Key);
let v = HashMap::<Wrapper, Entry>::deserialize(deserializer)?;
Ok(v.into_iter().map(|(Wrapper(k), v)| (k, v)).collect())
}
impl Keymap {
fn get_layout(&self) -> Result<ui_input::KeyboardLayout, Error> {
let semantic_key_map: Option<Vec<_>> = Some(
self.layouts
.iter()
.cloned()
.map(|layout| {
let modifiers = from_modifiers(&layout.modifiers);
let optional_modifiers = from_modifiers(&layout.optional_modifiers);
let entries = from_entries(&layout.entries);
ui_input::SemanticKeyMap {
entries,
modifiers,
optional_modifiers,
..ui_input::SemanticKeyMap::EMPTY
}
})
.collect(),
);
Ok(ui_input::KeyboardLayout {
key_map: None,
semantic_key_map,
..ui_input::KeyboardLayout::EMPTY
})
}
}
fn from_modifiers(modifiers: &Vec<Modifiers>) -> Option<ui_input::Modifiers> {
if modifiers.len() == 0 {
return None;
}
let modifiers = modifiers
.iter()
.cloned()
.fold(<ui_input::Modifiers as Decodable>::new_empty(), |acc, m| acc | m.into());
Some(modifiers)
}
fn from_entries(entries: &HashMap<Key, Entry>) -> Option<Vec<ui_input::SemanticKeyMapEntry>> {
if entries.len() == 0 {
return None;
};
let entries = entries
.iter()
.map(|(&key, entry)| {
let key = ui_input::Key::from_primitive(key).unwrap();
let semantic_key = match entry {
Entry::Symbol(s) => ui_input::SemanticKey::Symbol(s.to_string()),
Entry::Action(i) => ui_input::SemanticKey::Action(
ui_input::SemanticKeyAction::from_primitive(*i).unwrap_or_else(|| {
panic!("Unable to parse semantic key action {:?}", entry)
}),
),
};
ui_input::SemanticKeyMapEntry { key, semantic_key }
})
.collect();
Some(entries)
}
pub async fn handle_watch_keymap(
mut stream: ui_input::KeyboardLayoutStateRequestStream,
keymap_service: KeymapService,
) -> Result<(), Error> {
// Since keymap never changes, respond to the first keymap request only.
if let Some(ui_input::KeyboardLayoutStateRequest::Watch { responder }) =
stream.try_next().await.context("error handling keymap requests")?
{
let keymap = keymap_service.keymap.lock().await;
responder.send(keymap.get_layout()?).context("error sending keymap response")?;
};
while let Some(ui_input::KeyboardLayoutStateRequest::Watch { responder, .. }) =
stream.try_next().await.context("failed handling no-op requests")?
{
// Dropping responed without shutdown to keep it hanging for an update.
// Future versions of IME will dispatch updates to keymap using same interface.
responder.drop_without_shutdown();
}
Ok(())
}
mod hex_serde {
use serde::Deserialize;
pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer).map_err(serde::de::Error::custom)?;
let s_lower = s.to_lowercase();
let without_prefix = s_lower.trim_start_matches("0x");
u32::from_str_radix(&without_prefix, 16).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_async as fasync;
const TEST_LAYOUT_NAME: &str = "U.S.";
const TEST_SYMBOL: &str = "a";
const TEST_KEY: ui_input::Key = ui_input::Key::A;
const TEST_KEY_CODE: u32 = 0x00000001;
#[fasync::run_singlethreaded(test)]
async fn populate_keymap_test() {
let service = KeymapService::new().expect("new keymap service");
let keymap = service.keymap.lock().await;
assert_eq!(keymap.name, TEST_LAYOUT_NAME);
assert_eq!(keymap.layouts.len(), 6);
let layout = keymap.layouts.get(0).expect("layout page populated");
assert_eq!(layout.entries.len(), 28);
let entry = layout.entries.get(&TEST_KEY_CODE).expect("test key defined");
assert_eq!(*entry, Entry::Symbol(TEST_SYMBOL.to_string()));
let layout = keymap.get_layout().expect("layout defined");
assert_eq!(layout.key_map, None);
let semantic_key_map = layout.semantic_key_map.expect("has semantic key map");
let semantic_key_page = semantic_key_map.get(0).expect("semantic key map is defined");
assert_eq!(
semantic_key_page.optional_modifiers,
Some(
ui_input::Modifiers::NumLock
| ui_input::Modifiers::ScrollLock
| ui_input::Modifiers::Control
| ui_input::Modifiers::Alt
| ui_input::Modifiers::Meta
)
);
let semantic_key_page_entries = semantic_key_page.entries.as_ref().expect("has entries");
let semantic_key = semantic_key_page_entries
.iter()
.find_map(
|ui_input::SemanticKeyMapEntry { key, semantic_key }| {
if key == &TEST_KEY {
Some(semantic_key)
} else {
None
}
},
)
.expect("contains TEST_KEY");
assert_eq!(semantic_key, &ui_input::SemanticKey::Symbol(TEST_SYMBOL.to_string()));
}
}