QMK Firmwareによる日本語入力

Posted by Shugo Maeda on January 23, 2018 · 6 mins read

NaClの前田です。

最近Viterbiという格子配列で左右分離型のキーボードを作ったので、職場で主に利用しています。

キーボード

このキーボードではレツプリなどの他の自作キーボードでもよく使われているQMK Firmwareというオープンソースのファームウェアを利用しているので、自由にカスタマイズすることができます。

この記事では、筆者が利用しているT-Codeという入力語入力方式をキーボード上に実装します。

T-Codeとは

T-Codeは左手20キーと右手20キーを組み合わせて、2ストロークで漢字を含む日本語の文字を直接入力する方式です。

色々とメリットがあるのですが、キーボードのファームウェアに実装する上では、かな漢字変換方式に比べて実装が単純なのでプログラムサイズを小さくできるということが大きなメリットになります。

T-Codeモードのトグル

keyboards/viterbi/keymaps/shugoのようにキーボードのディレクトリに下にキーマップのディレクトリを作ってカスタマイズしていきます。

keymap.cがメインのファイルです。

まず、T-Codeモードをオン/オフするためのキーコード TCODE_TOGGLE を追加します。
キー配列の定義で使っている KC_KEYMAP() の中では KC_ というプリフィックスが追加されるので、KC_TCTG という別名を用意しておきます。

enum custom_keycodes {
  QWERTY = SAFE_RANGE,
  LOWER,
  RAISE,
  ADJUST,
  TCODE_TOGGLE,
  DYNAMIC_MACRO_RANGE,
};

#define KC_TCTG TCODE_TOGGLE

Raise + Space に上記のキーコードを割り当てます。

  [_RAISE] = KC_KEYMAP(
  //,----+----+----+----+----+----+----.    ,----+----+----+----+----+----+----.
     DRS ,TILD, F1 , F2 , F3 , F4 , F5 ,      F6 , F7 , F8 , F9 ,F10 ,F11 ,F12 ,
  //|----+----+----+----+----+----+----|    |----+----+----+----+----+----+----|
     DRS1,    , 1  , 2  , 3  , 4  , 5  ,      6  , 7  , 8  , 9  , 0  ,LCBR,RCBR,
  //|----+----+----+----+----+----+----|    |----+----+----+----+----+----+----|
     DMP1,    , F1 , F2 , F3 , F4 , F5 ,      F6 ,MINS,PLUS,LBRC,RBRC,    ,    ,
  //|----+----+----+----+----+----+----|    |----+----+----+----+----+----+----|
     DRS2,    , F7 , F8 , F9 ,F10 ,F11 ,     F12 ,NUHS,NUBS,    ,    ,    ,    ,
  //|----+----+----+----+----+----+----|    |----+----+----+----+----+----+----|
     DMP2,    ,    ,    ,    ,DBG ,TCTG,         ,    ,MNXT,VOLD,VOLU,MPLY,MUTE
  //`----+----+----+----+----+----+----'    `----+----+----+----+----+----+----'
  ),

キーイベントは process_record_user() で拾えるので、上記のキーが押された時にフラグを立てます。
tcode_prev_key_index は一つ前に押されたキーを覚えておくための変数ですが、T-Codeがオンになった際に -1 にしておきます。

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  ...
  switch (keycode) {
    case TCODE_TOGGLE:
      if (record->event.pressed) {
        tcode_enabled = !tcode_enabled;
        if (tcode_enabled) {
	  tcode_prev_key_index = -1;
        }
      }
      return false;
      break;

T-Codeモード入力

process_record_user() の先頭で process_tcode() を呼び出します。
process_tcode() がT-Code入力の実装のメイン部分で、T-Codeでキーが処理されたら false を返してキーイベント処理を終了します。

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  if (!process_tcode(keycode, record)) {
    return false;
  }

process_tcode() では、T-Codeがオフの場合やT-Codeで使うキーではない場合、Ctrlなどが押されている場合は、 true を返して通常のキーイベント処理に戻ります。
また、キーが押されていない場合は、 false を返してイベントを捨てます。

static inline bool process_tcode(uint16_t keycode, keyrecord_t *record) {
  if (!tcode_enabled) {
    return true;
  }
  if (!is_tcode_key(record) || get_mods() != 0) {
    tcode_prev_key_index = -1;
    return true;
  }
  if (!record->event.pressed) {
    return false;
  }
  int i = tcode_key_index(record);
  dprintf("T-Code: row = %d, col = %d, i = %d\n",
	  record->event.key.row,
	  record->event.key.col,
	  i);
  if (tcode_prev_key_index == -1) {
    tcode_prev_key_index = i;
  }
  else {
    int j = tcode_prev_key_index + i * 40;
    dprintf("T-Code: tcode_prev_key_index = %d, i = %d, j = %d\n",
	    tcode_prev_key_index, i, j);
    if (0 <= j && j < 40 * 40) {
      uint16_t ch = pgm_read_word(&tcode_table[j]);
      tap_unicode(ch);
    }
    else {
      tap_unicode(0x3013);
    }
    tcode_prev_key_index = -1;
  }
  return false;
}

詳細は省きますが、入力されたキーから tcode_table というテーブルを引いて、 tap_unicode() でUnicodeの文字を入力します。

ポイントは、

  • tcode_table の定義に PROGMEM を付けて、プログラム領域に配置する。
  • 上記のため、 pgm_read_word() で要素にアクセスする必要がある(最初このあたりを知らなくてはまった)。
  • Viterbiでは左側のキーボードは左から0オリジンで列番号が振られ、右側のキーボードでは右側から0オリジンで列番号が振られている。また、行番号は左側が上から0〜4、右側が上から5〜9が振られている。

といったところです。

Unicode入力

QMK FirmwareではUnicodeがサポートされています。

OS毎に方式を切り替える必要がありますが、例えばLinuxだと以下のようにモードを UC_LNX に設定します。

void matrix_init_user(void) {
  ...
  set_unicode_input_mode(UC_LNX);
}

ドキュメントにはUNICODE_ENABLEで 0xFFFF までの値を入力できるとありますが、 UC() というマクロでMSBを立てて他のキーコードと区別するようになっているので、MSBが立っている値を入力するとMSBが落ちて入力されてしまいます。

UNICODEMAP_ENABLEでは 0xFFFFFFFF まで入力できるようですが、uint32_tを使うとテーブルサイズが2倍になってしまうので、以下のように直接入力する関数を用意しました。

void unicode_input_start(void);
void unicode_input_finish(void);
void register_hex(uint16_t hex);

static void tap_unicode(uint16_t ch) {
  unicode_input_start();
  register_hex(ch);
  unicode_input_finish();
}

デバッグ出力

上記のようなコードを書いているとデバッグ出力がほしくなりますが、rules.mkで
CONSOLE_ENABLE = no とすると、 dprintf() などでデバッグ出力が可能です。
デバッグ出力はhid_listenというプログラムで確認することができます。

debug_enable という変数を true にして、 DEBUG というキーコードを送るとデバッグ出力が行われるようになります。

void matrix_init_user(void) {
  ...
  debug_enable = true;
  ...
}

まとめ

T-Codeという漢字直接入力方式を採用することで、キーボードのファームウェア上に漢字を含む日本語入力を実装することができました。

ただし、LinuxではibusかGTKが、WindowsではWinComposeが必要なので、Linuxサーバや他人のWindowsマシンで気軽にT-Code入力、というわけには行きませんでした。残念。

なお、この記事はfcitx-mozcで入力されました。