keymap を別ファイルにうつした #155

This commit is contained in:
Tokuhiro Matsuno
2023-01-18 23:21:31 +09:00
parent b1c0ef9735
commit a75ee4f3b6
10 changed files with 296 additions and 264 deletions

View File

@ -3,5 +3,6 @@ DATADIR ?= $(PREFIX)/share
install:
install -m 0644 -v -D -t $(DATADIR)/akaza/romkan romkan/*
install -m 0644 -v -D -t $(DATADIR)/akaza/keymap keymap/*
$(MAKE) -C ibus-akaza install

View File

@ -11,3 +11,4 @@
だくせい /諾成/
とーかないず /トーカナイズ/
いしのうえにもさんねん /石の上にも三年/
かなにゅうりょく /かな入力/仮名入力/

View File

@ -10,6 +10,7 @@ exec 2>&1
export AKAZA_DATA_DIR="$BASEDIR/../akaza-data/data/"
export AKAZA_ROMKAN_DIR="$BASEDIR/../romkan/"
export AKAZA_KEYMAP_DIR="$BASEDIR/../keymap/"
export RUST_BACKTRACE=4

View File

@ -42,6 +42,7 @@ use libakaza::engine::base::HenkanEngine;
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngine;
use libakaza::extend_clause::{extend_left, extend_right};
use libakaza::graph::candidate::Candidate;
use libakaza::keymap::KeyState;
use libakaza::lm::system_bigram::MarisaSystemBigramLM;
use libakaza::lm::system_unigram_lm::MarisaSystemUnigramLM;
use libakaza::romkan::RomKanConverter;
@ -53,16 +54,6 @@ use crate::input_mode::{
};
use crate::keymap::KeyMap;
#[derive(Debug, Hash, PartialEq, Copy, Clone)]
pub enum KeyState {
// 何も入力されていない状態。
PreComposition,
// 変換処理に入る前。ひらがなを入力している段階。
Composition,
// 変換中
Conversion,
}
#[repr(C)]
pub struct AkazaContext {
pub(crate) input_mode: InputMode,
@ -189,7 +180,7 @@ impl AkazaContext {
is_invalidate: false,
cursor_moved: false,
node_selected: HashMap::new(),
keymap: KeyMap::new(),
keymap: KeyMap::new()?,
force_selected_clause: Vec::new(),
prop_list,
input_mode_prop,

View File

@ -1,21 +1,14 @@
use std::collections::HashMap;
use std::ffi::CString;
use log::trace;
use anyhow::bail;
use log::{error, info, trace};
use ibus_sys::core::{IBusModifierType_IBUS_CONTROL_MASK, IBusModifierType_IBUS_SHIFT_MASK};
use ibus_sys::ibus_key::{
IBUS_KEY_BackSpace, IBUS_KEY_Down, IBUS_KEY_Escape, IBUS_KEY_Hangul, IBUS_KEY_Hangul_Hanja,
IBUS_KEY_Henkan, IBUS_KEY_KP_Down, IBUS_KEY_KP_Enter, IBUS_KEY_KP_Left, IBUS_KEY_KP_Page_Down,
IBUS_KEY_KP_Page_Up, IBUS_KEY_KP_Right, IBUS_KEY_KP_Up, IBUS_KEY_Left, IBUS_KEY_Muhenkan,
IBUS_KEY_Page_Down, IBUS_KEY_Page_Up, IBUS_KEY_Return, IBUS_KEY_Right, IBUS_KEY_Up,
IBUS_KEY_colon, IBUS_KEY_h, IBUS_KEY_j, IBUS_KEY_k, IBUS_KEY_l, IBUS_KEY_space, IBUS_KEY_0,
IBUS_KEY_1, IBUS_KEY_2, IBUS_KEY_3, IBUS_KEY_4, IBUS_KEY_5, IBUS_KEY_6, IBUS_KEY_7, IBUS_KEY_8,
IBUS_KEY_9, IBUS_KEY_F10, IBUS_KEY_F6, IBUS_KEY_F7, IBUS_KEY_F8, IBUS_KEY_F9, IBUS_KEY_KP_0,
IBUS_KEY_KP_1, IBUS_KEY_KP_2, IBUS_KEY_KP_3, IBUS_KEY_KP_4, IBUS_KEY_KP_5, IBUS_KEY_KP_6,
IBUS_KEY_KP_7, IBUS_KEY_KP_8, IBUS_KEY_KP_9,
};
use crate::context::KeyState;
use ibus_sys::glib::guint;
use ibus_sys::ibus_key::IBUS_KEY_VoidSymbol;
use ibus_sys::keys::ibus_keyval_from_name;
use libakaza::keymap::{KeyState, Keymap};
#[derive(Hash, PartialEq)]
struct KeyPattern {
@ -36,255 +29,67 @@ impl KeyPattern {
}
}
struct KeyMapBuilder {
keymap: HashMap<KeyPattern, String>,
}
impl KeyMapBuilder {
fn new() -> Self {
KeyMapBuilder {
keymap: HashMap::new(),
}
}
fn insert(&mut self, key_states: &[KeyState], keyvals: &[u32], modifier: u32, func_name: &str) {
trace!(
"INSERT KEY: {:?} {:?} {:?} {:?}",
key_states,
keyvals,
modifier,
func_name
);
for key_state in key_states {
for keyval in keyvals {
self.keymap.insert(
KeyPattern::new(*key_state, *keyval, modifier),
func_name.to_string(),
);
}
}
}
}
pub struct KeyMap {
keymap: HashMap<KeyPattern, String>,
}
impl KeyMap {
pub(crate) fn new() -> Self {
let mut builder = KeyMapBuilder::new();
/// - first: modifier
/// - second: keyval
/// ただし、keyval が不明なものの場合は IBUS_KEY_VoidSymbol になる。
fn split_key(key: &str) -> anyhow::Result<(u32, u32)> {
fn p(s: &str) -> guint {
let cs = CString::new(s.to_string()).unwrap();
unsafe { ibus_keyval_from_name(cs.as_ptr()) }
}
let mut modifier = 0_u32;
if key.contains('-') {
let keys = key.split('-').collect::<Vec<_>>();
for m in &keys[0..keys.len() - 1] {
match *m {
"C" => {
modifier |= IBusModifierType_IBUS_CONTROL_MASK;
}
"S" => {
modifier |= IBusModifierType_IBUS_SHIFT_MASK;
}
_ => {
bail!("Unknown modifier in keymap: {}", key);
}
}
}
Ok((modifier, p(keys[keys.len() - 1])))
} else {
Ok((0, p(key)))
}
}
pub(crate) fn new() -> anyhow::Result<Self> {
// TODO use IBus.Hotkey
// 入力モードの切り替え
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_j],
IBusModifierType_IBUS_SHIFT_MASK | IBusModifierType_IBUS_CONTROL_MASK,
"set_input_mode_hiragana",
);
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_Henkan, IBUS_KEY_Hangul],
0,
"set_input_mode_hiragana",
);
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_Muhenkan, IBUS_KEY_Hangul_Hanja],
0,
"set_input_mode_alnum",
);
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_colon],
IBusModifierType_IBUS_SHIFT_MASK | IBusModifierType_IBUS_CONTROL_MASK,
"set_input_mode_alnum",
);
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_l],
IBusModifierType_IBUS_SHIFT_MASK | IBusModifierType_IBUS_CONTROL_MASK,
"set_input_mode_fullwidth_alnum",
);
builder.insert(
&[
KeyState::Composition,
KeyState::PreComposition,
KeyState::Conversion,
],
&[IBUS_KEY_k],
IBusModifierType_IBUS_SHIFT_MASK | IBusModifierType_IBUS_CONTROL_MASK,
"set_input_mode_katakana",
);
let keymap = Keymap::load("default")?;
let mut mapping: HashMap<KeyPattern, String> = HashMap::new();
// basic operations.
builder.insert(
&[KeyState::Composition],
&[IBUS_KEY_space],
0,
"update_candidates",
);
builder.insert(&[KeyState::Conversion], &[IBUS_KEY_space], 0, "cursor_down");
builder.insert(
&[KeyState::Conversion, KeyState::Composition],
&[IBUS_KEY_BackSpace],
0,
"erase_character_before_cursor",
);
builder.insert(
&[KeyState::Conversion, KeyState::Composition],
&[IBUS_KEY_h],
IBusModifierType_IBUS_CONTROL_MASK,
"erase_character_before_cursor",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Return, IBUS_KEY_KP_Enter],
0,
"commit_candidate",
);
builder.insert(
&[KeyState::Composition],
&[IBUS_KEY_Return, IBUS_KEY_KP_Enter],
0,
"commit_preedit",
);
builder.insert(
&[KeyState::Conversion, KeyState::Composition],
&[IBUS_KEY_Escape],
0,
"escape",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Up, IBUS_KEY_KP_Up],
0,
"cursor_up",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Down, IBUS_KEY_KP_Down],
0,
"cursor_down",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Right, IBUS_KEY_KP_Right],
0,
"cursor_right",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Left, IBUS_KEY_KP_Left],
0,
"cursor_left",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Right, IBUS_KEY_KP_Right],
IBusModifierType_IBUS_SHIFT_MASK,
"extend_clause_right",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_Left, IBUS_KEY_KP_Left],
IBusModifierType_IBUS_SHIFT_MASK,
"extend_clause_left",
);
// 後から文字タイプを指定する
builder.insert(
&[KeyState::Composition, KeyState::Conversion],
&[IBUS_KEY_F6],
0,
"convert_to_full_hiragana",
);
builder.insert(
&[KeyState::Composition, KeyState::Conversion],
&[IBUS_KEY_F7],
0,
"convert_to_full_katakana",
);
builder.insert(
&[KeyState::Composition, KeyState::Conversion],
&[IBUS_KEY_F8],
0,
"convert_to_half_katakana",
);
builder.insert(
&[KeyState::Composition, KeyState::Conversion],
&[IBUS_KEY_F9],
0,
"convert_to_full_romaji",
);
builder.insert(
&[KeyState::Composition, KeyState::Conversion],
&[IBUS_KEY_F10],
0,
"convert_to_half_romaji",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_KP_Page_Up, IBUS_KEY_Page_Up],
0,
"page_up",
);
builder.insert(
&[KeyState::Conversion],
&[IBUS_KEY_KP_Page_Down, IBUS_KEY_Page_Down],
0,
"page_down",
);
let mut num = |keyvals: &[u32], n: i32| {
// fn insert(&mut self, key_states: &[KeyState], keyvals: &[u32], modifier: u32, func_name: &str) {
builder.insert(
&[KeyState::Conversion],
keyvals,
0,
format!("press_number_{}", n).as_str(),
)
};
num(&[IBUS_KEY_1, IBUS_KEY_KP_1], 1);
num(&[IBUS_KEY_2, IBUS_KEY_KP_2], 2);
num(&[IBUS_KEY_3, IBUS_KEY_KP_3], 3);
num(&[IBUS_KEY_4, IBUS_KEY_KP_4], 4);
num(&[IBUS_KEY_5, IBUS_KEY_KP_5], 5);
num(&[IBUS_KEY_6, IBUS_KEY_KP_6], 6);
num(&[IBUS_KEY_7, IBUS_KEY_KP_7], 7);
num(&[IBUS_KEY_8, IBUS_KEY_KP_8], 8);
num(&[IBUS_KEY_9, IBUS_KEY_KP_9], 9);
num(&[IBUS_KEY_0, IBUS_KEY_KP_0], 0);
KeyMap {
keymap: builder.keymap,
for kc in keymap.keys {
for key in &kc.key {
let (modifier, keyval) = Self::split_key(key.as_str())?;
if keyval == IBUS_KEY_VoidSymbol {
error!("Unknown key symbol: {} {:?}", key, kc);
continue;
}
info!("Insert: {} {} {} {:?}", modifier, keyval, key, kc);
for state in &kc.states {
mapping.insert(
KeyPattern::new(*state, keyval, modifier),
kc.command.clone(),
);
}
}
}
Ok(KeyMap { keymap: mapping })
}
pub fn get(&self, key_state: &KeyState, keyval: u32, modifier: u32) -> Option<&String> {
@ -293,3 +98,40 @@ impl KeyMap {
.get(&KeyPattern::new(*key_state, keyval, modifier))
}
}
#[cfg(test)]
mod tests {
use ibus_sys::ibus_key::{IBUS_KEY_Right, IBUS_KEY_h};
use super::*;
#[test]
fn test_c_h() -> anyhow::Result<()> {
let (modifier, keyval) = KeyMap::split_key("C-h")?;
assert_eq!(modifier, IBusModifierType_IBUS_CONTROL_MASK);
assert_eq!(keyval, IBUS_KEY_h);
info!("Key: C-h, {}, {}", modifier, keyval);
Ok(())
}
#[test]
fn test_c_s_h() -> anyhow::Result<()> {
let (modifier, keyval) = KeyMap::split_key("C-S-h")?;
assert_eq!(
modifier,
IBusModifierType_IBUS_CONTROL_MASK | IBusModifierType_IBUS_SHIFT_MASK
);
assert_eq!(keyval, IBUS_KEY_h);
info!("Key: C-S-h, {}, {}", modifier, keyval);
Ok(())
}
#[test]
fn test_shift() -> anyhow::Result<()> {
let (modifier, keyval) = KeyMap::split_key("S-Right")?;
assert_eq!(modifier, IBusModifierType_IBUS_SHIFT_MASK);
assert_eq!(keyval, IBUS_KEY_Right);
info!("Key: S-Right, {}, {}", modifier, keyval);
Ok(())
}
}

6
ibus-sys/src/keys.rs Normal file
View File

@ -0,0 +1,6 @@
use crate::glib::{gchar, guint};
extern "C" {
pub fn ibus_keyval_name(keyval: guint) -> *const gchar;
pub fn ibus_keyval_from_name(keyval_name: *const gchar) -> guint;
}

View File

@ -7,6 +7,7 @@ pub mod core;
pub mod engine;
pub mod glib;
pub mod ibus_key;
pub mod keys;
pub mod lookup_table;
pub mod prop_list;
pub mod property;

121
keymap/default.yml Normal file
View File

@ -0,0 +1,121 @@
---
keys:
# 入力モードの切り替え
- states: [Composition, PreComposition, Conversion]
key: [C-S-j]
command: set_input_mode_hiragana
- states: [Composition, PreComposition, Conversion]
key: [Henkan]
command: set_input_mode_hiragana
- states: [Composition, PreComposition, Conversion]
key: [Hangul]
command: set_input_mode_hiragana
- states: [Composition, PreComposition, Conversion]
key: [Muhenkan]
command: set_input_mode_alnum
- states: [Composition, PreComposition, Conversion]
key: [Hangul_Hanja]
command: set_input_mode_alnum
- states: [Composition, PreComposition, Conversion]
key: [C-S-colon]
command: set_input_mode_alnum
- states: [Composition, PreComposition, Conversion]
key: [C-S-l]
command: set_input_mode_fullwidth_alnum
- states: [Composition, PreComposition, Conversion]
key: [C-S-k]
command: set_input_mode_katakana
# 基本的な操作
- states: [Composition]
key: [space]
command: update_candidates
- states: [Conversion]
key: [space]
command: cursor_down
- states: [Conversion, Composition]
key: [BackSpace]
command: erase_character_before_cursor
- states: [Conversion, Composition]
key: [C-h]
command: erase_character_before_cursor
- states: [Conversion]
key: [Return, KP_Enter]
command: commit_candidate
- states: [Composition]
key: [Return, KP_Enter]
command: commit_preedit
- states: [Conversion, Composition]
key: [Escape]
command: escape
- states: [Conversion]
key: [Up, KP_Up]
command: cursor_up
- states: [Conversion]
key: [Down, KP_Down]
command: cursor_down
- states: [Conversion]
key: [Left, KP_Left]
command: cursor_left
- states: [Conversion]
key: [S-Right, S-KP_Right]
command: extend_clause_right
- states: [Conversion]
key: [S-Left, S-KP_Left]
command: extend_clause_left
- states: [Composition, Conversion]
key: [Page_Up, KP_Page_Up]
command : page_up
- states: [Composition, Conversion]
key: [Page_Down, KP_Page_Down]
command : page_down
# あとから文字タイプを指定する
- states: [Composition, Conversion]
key: [F6]
command: convert_to_full_hiragana
- states: [Composition, Conversion]
key: [F7]
command : convert_to_full_katakana
- states: [Composition, Conversion]
key: [F8]
command : convert_to_half_katakana
- states: [Composition, Conversion]
key: [F9]
command : convert_to_full_romaji
- states: [Composition, Conversion]
key: [F10]
command : convert_to_half_romaji
# 数字キーによる選択
- states: [Conversion]
key: [1, KP_1]
command : press_number_1
- states: [Conversion]
key: [2, KP_2]
command : press_number_2
- states: [Conversion]
key: [3, KP_3]
command : press_number_3
- states: [Conversion]
key: [4, KP_4]
command : press_number_4
- states: [Conversion]
key: [5, KP_5]
command : press_number_5
- states: [Conversion]
key: [6, KP_6]
command : press_number_6
- states: [Conversion]
key: [7, KP_7]
command : press_number_7
- states: [Conversion]
key: [8, KP_8]
command : press_number_8
- states: [Conversion]
key: [9, KP_9]
command : press_number_9
- states: [Conversion]
key: [0, KP_0]
command : press_number_0

67
libakaza/src/keymap.rs Normal file
View File

@ -0,0 +1,67 @@
use anyhow::Context;
use log::info;
use serde::{Deserialize, Serialize};
use std::env;
use std::fs::File;
use std::io::BufReader;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Keymap {
pub keys: Vec<KeyConfig>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct KeyConfig {
pub states: Vec<KeyState>,
pub key: Vec<String>,
pub command: String,
}
#[derive(Debug, Hash, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum KeyState {
// 何も入力されていない状態。
PreComposition,
// 変換処理に入る前。ひらがなを入力している段階。
Composition,
// 変換中
Conversion,
}
impl Keymap {
pub fn load(name: &str) -> anyhow::Result<Keymap> {
let pathstr: String = if cfg!(test) || cfg!(feature = "it") {
format!("{}/../keymap/{}.yml", env!("CARGO_MANIFEST_DIR"), name)
} else if let Ok(env) = env::var("AKAZA_KEYMAP_DIR") {
format!("{}/{}.yml", env, name)
} else {
let pathbuf = xdg::BaseDirectories::with_prefix("akaza")
.with_context(|| "Opening xdg directory with 'akaza' prefix")?
.get_config_file(format!("keymap/{}.yml", name));
pathbuf.to_string_lossy().to_string()
};
info!("Load {}", pathstr);
let got: Keymap = serde_yaml::from_reader(BufReader::new(
File::open(&pathstr).with_context(|| pathstr)?,
))?;
Ok(got)
}
}
#[cfg(test)]
mod tests {
use crate::keymap::KeyState::Conversion;
use std::fs::File;
use std::io::BufReader;
use super::*;
#[test]
fn test_keymap() -> anyhow::Result<()> {
let keymap: Keymap =
serde_yaml::from_reader(BufReader::new(File::open("../keymap/default.yml")?))?;
for kc in keymap.keys {
println!("{:?}", kc);
}
Ok(())
}
}

View File

@ -15,3 +15,4 @@ pub mod lm;
pub mod romkan;
pub mod trie;
pub mod user_side_data;
pub mod keymap;