chroju.dev/blog

the world as code

Lily58 Pro 最強のキーマップを目指して

自作キーボード Lily58 Pro に関するエントリーシリーズ第3弾、おそらくこれで最後。

自作キーボードの大きな魅力の一つが自由なキーマップを作れる点で、市販のキーボードのキーマクロ機能の比ではなく、本当にすべてのキーを自由にアサインできる。 Dvorak ももちろん可能だし、「俺が考えた最強の配列」を作ることもできる。また Lily58 をはじめ多くのキーボードはフルキーボードを比較してキー数が少なく設定されていることが多いため、レイヤーの概念などを駆使して、1つのキーに複数の機能をアサインすることも必要になってくる。どのキーにどの役割をいくつ与えるか、という組み合わせも考えていくと、途方もないほどに自由度が高い。

QMK Firmware

GitHub - qmk/qmk_firmware: Open-source keyboard firmware for Atmel AVR and Arm USB families
Open-source keyboard firmware for Atmel AVR and Arm USB families - qmk/qmk_firmware
GitHub - qmk/qmk_firmware: Open-source keyboard firmware for Atmel AVR and Arm USB families favicon https://github.com/qmk/qmk_firmware
GitHub - qmk/qmk_firmware: Open-source keyboard firmware for Atmel AVR and Arm USB families

例の Ergodox 含め、多くの自作キーボードがファームウェアとして QMK Firmware という OSS を活用している。自作キーボード制作者は自身のキーボードを作成したら、そのキーマップ設定を追加して Pull Request を送ることで QMK Firmware 本体のレポジトリに merge されるようで、現在買えるような自作キーボードであれば、基本的には上記レポジトリから clone すれば、すぐにデフォルトキーマップを焼き込めるようになっている。もちろん、そこから自分でキーマップを変えていくこともできる。

QMK Firmware では単にキー配置を司るだけではなく、多くの付随的な機能を提供している。

レイヤー

shift キーのように、レイヤー切り替えキーを押下している間のみ、キーアサインが別のものに変換される機能。キー数が少ないキーボードではほぼ必須の機能であり、多くの場合 LOWER と RAISE の2つのレイヤーキーがデフォルトでアサインされている。

Tap Dance

1回押した場合と、すばやく複数回押した場合でキーアサインを切り替える機能。複数種類の括弧を1つのキーでまかなえるようにしたり、欧米圏でウムラウト付きのアルファベットなどを出す際に使われているのをよく見る印象。

Mod-Tap

他のキーと同時に押した場合は Modifier key = shift や control などの修飾キーに、単独で押した場合は別のキーに切り替える機能。確かに修飾キーというのは同時押しが基本なので、同時に押さない場合は別のキーとして扱う、というのはなんだか頭いいな、と思う発想だった。

Modifier Keys

修飾キー + 他のキーを押した状態を、1つのキーにアサインしてしまえる機能。どちらかと言えばキー数に余裕がある場合に、よく使うキーボードショートカットをアサインする、などの利用をするっぽい。

One Shot Keys

通常、修飾キーを使う際は同時押しが必要になるが、一度押すとそのキーが押された状態になり、別のキーを押すと押された状態が解除される、という機能。 Windows の固定キーに近い。

他にもマウス操作をキーにアサインできるとか、 Unicode のアサインも可能なので :thinking_face: を1キーで出せるとかいろいろできる。つまり 単純なキーの配置だけではなく、どの機能を使うと自分の理想的な運指になるか ということを考える必要があり、沼と呼ぶに非常にふさわしい。

なお Lily58 は対応していないが、キーボードにありがちな LED を光らせる設定も QMK Firmware でまかなうことができる。一部機能に関しては rules.mk というファイルに有効化の設定を書かなくてはならないため、ドキュメントを読みながら対応する必要がある。機能を有効化すると、その機能に関するコードがファームウェアに内包されるようになる仕組みと思われる。私の rules.mk はこんな感じ。

# Build Options
#   change to "no" to disable the options, or define them in the Makefile in
#   the appropriate keymap folder that will get included automatically
#
BOOTMAGIC_ENABLE = no       # Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = no        # Mouse keys(+4700)
EXTRAKEY_ENABLE = yes       # Audio control and System control(+450)
CONSOLE_ENABLE = no         # Console for debug(+400)
COMMAND_ENABLE = no         # Commands for debug and configuration
NKRO_ENABLE = no            # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
MIDI_ENABLE = no            # MIDI controls
AUDIO_ENABLE = no           # Audio output on port C6
UNICODE_ENABLE = no         # Unicode
BLUETOOTH_ENABLE = no       # Enable Bluetooth with the Adafruit EZ-Key HID
RGBLIGHT_ENABLE = no        # Enable WS2812 RGB underlight.
SWAP_HANDS_ENABLE = no      # Enable one-hand typing
TAP_DANCE_ENABLE = yes

# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

# If you want to change the display of OLED, you need to change here
SRC +=  ./lib/glcdfont.c \
        ./lib/rgb_state_reader.c \
        ./lib/layer_state_reader.c \
        ./lib/logo_reader.c \
        ./lib/key_count.c \
        ./lib/uptime.c \

Lily58 デフォルトキーマップ

すべて自分で考えて配置するのは大変なので、デフォルトキーマップを元に編集することにした。本記事執筆時点で、デフォルトキーマップは以下の通りになっている。

/* QWERTY
 * ,-----------------------------------------.                    ,-----------------------------------------.
 * | ESC  |   1  |   2  |   3  |   4  |   5  |                    |   6  |   7  |   8  |   9  |   0  |  `   |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * | Tab  |   Q  |   W  |   E  |   R  |   T  |                    |   Y  |   U  |   I  |   O  |   P  |  -   |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |LCTRL |   A  |   S  |   D  |   F  |   G  |-------.    ,-------|   H  |   J  |   K  |   L  |   ;  |  '   |
 * |------+------+------+------+------+------|   [   |    |    ]  |------+------+------+------+------+------|
 * |LShift|   Z  |   X  |   C  |   V  |   B  |-------|    |-------|   N  |   M  |   ,  |   .  |   /  |RShift|
 * `-----------------------------------------/       /     \      \-----------------------------------------'
 *                   | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| RGUI |
 *                   |      |      |      |/       /         \      \ |      |      |      |
 *                   `----------------------------'           '------''--------------------'
 */

 [_QWERTY] = LAYOUT( \
  KC_ESC,   KC_1,   KC_2,    KC_3,    KC_4,    KC_5,                     KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_GRV, \
  KC_TAB,   KC_Q,   KC_W,    KC_E,    KC_R,    KC_T,                     KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_MINS, \
  KC_LCTRL, KC_A,   KC_S,    KC_D,    KC_F,    KC_G,                     KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT, \
  KC_LSFT,  KC_Z,   KC_X,    KC_C,    KC_V,    KC_B, KC_LBRC,  KC_RBRC,  KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,  KC_RSFT, \
                             KC_LALT, KC_LGUI,LOWER, KC_SPC,   KC_ENT,   RAISE,   KC_BSPC, KC_RGUI \
),
/* LOWER
 * ,-----------------------------------------.                    ,-----------------------------------------.
 * |      |      |      |      |      |      |                    |      |      |      |      |      |      |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |  F1  |  F2  |  F3  |  F4  |  F5  |  F6  |                    |  F7  |  F8  |  F9  | F10  | F11  | F12  |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |   `  |   !  |   @  |   #  |   $  |   %  |-------.    ,-------|   ^  |   &  |   *  |   (  |   )  |   -  |
 * |------+------+------+------+------+------|   [   |    |    ]  |------+------+------+------+------+------|
 * |      |      |      |      |      |      |-------|    |-------|      |   _  |   +  |   {  |   }  |   |  |
 * `-----------------------------------------/       /     \      \-----------------------------------------'
 *                   | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| RGUI |
 *                   |      |      |      |/       /         \      \ |      |      |      |
 *                   `----------------------------'           '------''--------------------'
 */
[_LOWER] = LAYOUT( \
  _______, _______, _______, _______, _______, _______,                   _______, _______, _______,_______, _______, _______,\
  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,                     KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12, \
  KC_GRV, KC_EXLM, KC_AT,   KC_HASH, KC_DLR,  KC_PERC,                   KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_TILD, \
  _______, _______, _______, _______, _______, _______, _______, _______, XXXXXXX, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE, \
                             _______, _______, _______, _______, _______,  _______, _______, _______\
),
/* RAISE
 * ,-----------------------------------------.                    ,-----------------------------------------.
 * |      |      |      |      |      |      |                    |      |      |      |      |      |      |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |   `  |   1  |   2  |   3  |   4  |   5  |                    |   6  |   7  |   8  |   9  |   0  |      |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |  F1  |  F2  |  F3  |  F4  |  F5  |  F6  |-------.    ,-------|      | Left | Down |  Up  |Right |      |
 * |------+------+------+------+------+------|   [   |    |    ]  |------+------+------+------+------+------|
 * |  F7  |  F8  |  F9  | F10  | F11  | F12  |-------|    |-------|   +  |   -  |   =  |   [  |   ]  |   \  |
 * `-----------------------------------------/       /     \      \-----------------------------------------'
 *                   | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| RGUI |
 *                   |      |      |      |/       /         \      \ |      |      |      |
 *                   `----------------------------'           '------''--------------------'
 */

[_RAISE] = LAYOUT( \
  _______, _______, _______, _______, _______, _______,                     _______, _______, _______, _______, _______, _______, \
  KC_GRV,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,                        KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    _______, \
  KC_F1,  KC_F2,    KC_F3,   KC_F4,   KC_F5,   KC_F6,                       XXXXXXX, KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT, XXXXXXX, \
  KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,   _______, _______,  KC_PLUS, KC_MINS, KC_EQL,  KC_LBRC, KC_RBRC, KC_BSLS, \
                             _______, _______, _______,  _______, _______,  _______, _______, _______ \

これに対する自分の意見は以下。

  • 右GUI、右Shiftは不要
  • Backspace が右親指の位置にあるが、最初は慣れないと思うので右上にも配置したい
  • LOWER レイヤーにすべての記号がアサインされているが、 Lily58 は数字キーも存在するのでいくつかは重複アサインとなっており、不要
  • ファンクションキーは数字キーと同じ列にそれぞれ配置するとわかりやすそう(F11とF12は諦める)
  • 矢印キーは Vim の感覚から hjkl にあると覚えやすそう

これを踏まえてキーマップを作成した。

現時点で最強のキーマップ

自作キーボードは親指を駆使させ、小指を使わなくさせる傾向にあることが多い。 Ergodox の親指用キーの多さなどは顕著である。多くの人は小指より親指のほうが動かしやすいと思うので理に適った話ではあるが、しかし使い慣れたキー配置からの移行コストもあるわけで、一旦折衷的なキーマップとした。 まずはデフォルトのレイヤー。

/* QWERTY
 * ,------------------------------------------.                    ,-----------------------------------------.
 * | ESC   |   1  |   2  |   3  |   4  |   5  |                    |   6  |   7  |   8  |   9  |   0  |BackSP|
 * |-------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * | Tab   |   Q  |   W  |   E  |   R  |   T  |                    |   Y  |   U  |   I  |   O  |   P  |  -   |
 * |-------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |CTL/GUI|   A  |   S  |   D  |   F  |   G  |-------.    ,-------|   H  |   J  |   K  |   L  |   ;  |  '   |
 * |-------+------+------+------+------+------|   [   |    |    ]  |------+------+------+------+------+------|
 * |LShift |   Z  |   X  |   C  |   V  |   B  |-------|    |-------|   N  |   M  |   ,  |   .  |   /  |CTL+Sp|
 * `------------------------------------------/       /     \      \-----------------------------------------'
 *                    | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| ESC  |
 *                    |      |      |IMEon |/       /         \      \ |IMEoff|      |      |
 *                    `----------------------------'           '------''--------------------'
 */

 [_QWERTY] = LAYOUT( \
  KC_ESC,           KC_1,   KC_2,    KC_3,    KC_4,    KC_5,                                KC_6,           KC_7,    KC_8,    KC_9,    KC_0,    KC_BSPC,      \
  KC_TAB,           KC_Q,   KC_W,    KC_E,    KC_R,    KC_T,                                KC_Y,           KC_U,    KC_I,    KC_O,    KC_P,    KC_BSLS,      \
  TD(TD_CTL_GUI),   KC_A,   KC_S,    KC_D,    KC_F,    KC_G,                                KC_H,           KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,      \
  KC_LSFT,          KC_Z,   KC_X,    KC_C,    KC_V,    KC_B,            KC_LBRC,   KC_RBRC, KC_N,           KC_M,    KC_COMM, KC_DOT,  KC_SLSH, LCTL(KC_SPC), \
                                     KC_LALT, KC_LGUI, LT(1, KC_LANG1), KC_SPC,    KC_ENT,  LT(2,KC_LANG2), KC_BSPC, KC_ESC  \
),

Backspace と ESC は右親指で押すことを目指すが、現時点では一般的な位置にも配置している。最左列、下から2番目のキーは Tap-Dance により、1回押すと ctrl、2回押すと GUI とした。これは macOS でキーボードショートカットが GUI(⌘)に割り当たっている一方、 GNU Readline の各種ショートカットが ctrl と位置がバラけるのが嫌だったため。これも移行措置として、通常の位置にも GUI を置いている。

なお、 Tap-Dance はどのキーを何回押したときにどう動作させるか、という定義を書く必要があり、キーマップ以外に以下のようなコードを追加している。

enum {
  TD_CTL_GUI = 0
};

qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_CTL_GUI] = ACTION_TAP_DANCE_DOUBLE(KC_LCTRL, KC_LGUI)
};

LOWER、RAISEの各キーは Mod-Tap により、単独で押した場合に KC_LANG1KC_LANG2 というキーをそれぞれ割り当てている。ドキュメントに記載はないのだが、これらは macOS のかな/英数に相当するらしく、 IME の切り替えが Toggle ではなく一発で出来るようになる。 US キーボードを使っていると IME 切り替えは ctrl + space などで Toggle させるしかなかったため、このアサインが出来るのは嬉しかった。

参考 : 変わり種ErgoDox紹介 + IME話 - Qiita

最右最下は正直余ってしまったので、 ctrl + space を割り当てた。 Windows での IME 切り替えに現在使っているが、 Windows でも OS 側の設定で KC_LANG 各キーを IME 切り替えに割り当てられるらしいので、後日対応の予定。

LOWER

/* LOWER
 * ,-----------------------------------------.                    ,-----------------------------------------.
 * |      |      |      |      |      |      |                    |      |      |      |      |      |      |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |      |      |      |      |   ~  |      |                    |      |      |      |  _   |  +   |      |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |      |      |      |      |   `  |      |-------.    ,-------|      |      |      |  -   |  =   |      |
 * |------+------+------+------+------+------|   (   |    |    )  |------+------+------+------+------+------|
 * |      |      |      |      |      |      |-------|    |-------|      |      |      |      |      |      |
 * `-----------------------------------------/       /     \      \-----------------------------------------'
 *                   | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| RGUI |
 *                   |      |      |      |/       /         \      \ |      |      |      |
 *                   `----------------------------'           '------''--------------------'
 */

[_LOWER] = LAYOUT( \
  _______, _______, _______, _______, _______, _______,                     _______, _______, _______, _______, _______, _______, \
  _______, _______, _______, _______, KC_TILD, _______,                     _______, KC_UNDS, KC_PLUS, _______, _______, _______, \
  _______, _______, _______, _______, KC_GRV,  _______,                     _______, KC_MINS, KC_EQL,  _______, _______, _______, \
  _______, _______, _______, _______, _______, _______,  KC_LPRN, KC_RPRN,  _______, _______, _______, _______, _______, _______, \
                             _______, _______, _______,  _______, _______,  _______, _______, _______ \
),

LOWER は記号キー用とした。通常配置で盛り込みきれていない記号キーだけに絞っている。

当初はハイフンを9に割り当てるなど、一般的配列と近い位置に配置していたが、わざわざ遠くへ押しに行く必要もないと考え直し、左右とも人差し指近くに集中させた。

RAISE

/* RAISE
 * ,-----------------------------------------.                    ,-----------------------------------------.
 * |      |      |      |      |      |      |                    |      |      |      |      |      | DEL  |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * | F11  |  F1  |  F2  |  F3  |  F4  |  F5  |                    |  F6  |  F7  |  F8  |  F9  | F10  | F12  |
 * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
 * |      |      |      |      |      |      |-------.    ,-------| LEFT | DOWN |  UP  |RIGHT |      |      |
 * |------+------+------+------+------+------|   [   |    |    ]  |------+------+------+------+------+------|
 * |      |      |      |      |      |      |-------|    |-------| BRID | BRIU | VOLD | VOLU | MUTE |      |
 * `-----------------------------------------/       /     \      \-----------------------------------------'
 *                   | LAlt | LGUI |LOWER | /Space  /       \Enter \  |RAISE |BackSP| CAD  |
 *                   |      |      |      |/       /         \      \ |      |      |      |
 *                   `----------------------------'           '------''--------------------'
 */
[_RAISE] = LAYOUT( \
  _______, _______, _______, _______, _______, _______,                   _______, _______, _______, _______,  _______, KC_DEL,  \
  KC_F11,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,                     KC_F6,   KC_F7,   KC_F8,   KC_F9,    KC_F10,  KC_F12,  \
  _______, _______, _______, _______, _______, _______,                   KC_LEFT, KC_DOWN, KC_UP,   KC_RIGHT, _______, _______, \
  _______, _______, _______, _______, _______, _______, _______, _______, KC_BRID, KC_BRIU, KC_VOLD, KC_VOLU,  KC_MUTE, _______, \
                             _______, _______, _______, _______, _______, _______, _______, LCTL(LALT(KC_DEL)) \
),

RAISE は機能的なキー担当。先述の方針通りにファンクションキーと矢印キーを並べている。

また Macbook に搭載されている輝度や音量調節のキーをよく使っていたので、それらも割り当てた。 Windows でも動いてくれるとうれしいのだが、いまのところ動かないので要調整。

右親指あたりにある「CAD」というキーは ctrl + alt + delete の同時押しに相当している。 Windows 最悪のあのキーバインドが簡単に打てて QOL がだいぶ上がった。右上の Delete は Windows で使うことがあるかもしれない、ということで念の為配置している。今のところ必要になったことはない。

Conclusion

この配置になるまで5回ぐらいはキーマップの変更を経ているが、ようやく安定したかなというところ。自作キーボードは組み立て終わってからが沼だとはよく言ったものだなと思う。

なるべく親指の役割を増やす、ホームポジションから手を動かさず済むようにする、というのは実際にやってみると確かに負荷が少ない。もともと自分は「腕を動かすのが嫌」という理由でトラックパッドとトラックボールを愛用してもいるので、キーボード上で指の移動距離を減らす、という考え方にもハマりこんでしまった。

そしてその結果、自分でも面白い変化だと思うが、「数字列」が遠いと思うようになってきた。指を動かす範囲は上下左右1キー以内に現状ほぼ収まってきたので、たまに数字列だけ2キー分指を伸ばさなくてはならないのがどうにも気持ち悪い。

この投稿をInstagramで見る

corne chocolate 買っちった

@ chrojuがシェアした投稿 -

つまりこういうことである。 Lily58 から数字列をザックリ削ったような形状の Corne Chocolate を買ってしまった。キーマップを見てもらってもわかる通り、58キーだとちょろちょろ余ることもわかったし、42キーでも案外イケそうな気はしている。願わくば、これで End Game に至るといいのだが。