🤖
メカニカルキーボード【Keychron K8 Pro】の設定
初めての記事です。
仕事で使ってるPCがUS配列のWindowsでIME ON/OFFがalt + `
などする必要があり、常々面倒だなっと思っていた。PowerToysのkeyboard managerでどうにかできると思うが、入れることができない環境なのであきらめていた。
そんな時にWindowsがLang1/2に対応したことを今更ながら知った。
これでUS配列でも無変換でIME OFF、変換でIME ONっぽいことができる。
US配列なので変換/無変換はないのでcapslockなど不要なキーを犠牲にするか、comboやTap Danceを使うことになる。
今回はTap Danceを使うことにする。
なのでKeychron K8 Proのキーマップを作成してみる。
ついでqmkのビルド環境の準備もまとめてみる。
前提
- git、vagrantが使えること。
dockerfileも用意されてるみたいなのでdockerでも同じことができるかも。
vagrant上でビルドするのでwindowsでもwslでもlinuxでもmacでも問題ないと思う。 - qmk_toolboxをインストールしていること。
ビルドとフラッシュの動作確認
defaultをビルドして正常に成功するか確認する。
ビルドできた場合、hostのqmk_firmware/.buildにビルド結果が出力される。
- keychronのソースコードを取得する。host
git clone --recurse-submodules git@github.com:Keychron/qmk_firmware.git cd qmk_firmware git checkout bluetooth_playground
- vagrantの起動とssh接続host
vagrant up vagrant ssh
- defaultのキーマップをビルドvagrant
cd vagrant make keychron/k8_pro/ansi/white:default
- キーボードに書き込む
- qmk toolboxを起動する。
- キーボード側面のスイッチを「OFF」にする。
- Keychron K8 ProをUSBケーブルでPCに接続する。
- スペースキー下左側にある「reset」ボタンを押しながらキーボード側面のスイッチを「cable」にする。
- qmk toolboxの「Open」を押下してqmk_firmware/.buildの中にあるbinファイルを選択する。
- 「Flash」を押下して書き込む。
「File downloaded successfully」と表示されれば書き込み成功。
キーマップの編集
ここまででqmkのビルドとフラッシュができるようになったのでキーマップをkeymap.cその他諸々を編集していく。
-
defaultのキーマップをコピーしてカスタム用のフォルダを作成する。
hostcd qmk_firmware/keyboards/keychron/k8_pro/ansi/white/keymaps/ cp default myKeyMap
-
作成したmyKeyMapに下記ファイルを格納する。
rules.mk
rules.mkTAP_DANCE_ENABLE = yes SRC += myTapDance.c
myKeyMap.h
myKeyMap.h#pragma once #include QMK_KEYBOARD_H #include "myTapDance.h" enum layers{ MAC_BASE, MAC_FN, WIN_BASE, WIN_FN };
keymap.c
keymap.c#include "myKeyMap.h" const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [MAC_BASE] = LAYOUT_tkl_ansi( KC_ESC, KC_BRID, KC_BRIU, KC_MCTL, KC_LPAD, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_SNAP, KC_SIRI, BL_STEP, KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_LCTL, KC_LOPTN, KC_LCMMD, KC_SPC, KC_RCMMD, KC_ROPTN, MO(MAC_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT), [MAC_FN] = LAYOUT_tkl_ansi( _______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______, BL_TOGG, _______, BT_HST1, BT_HST2, BT_HST3, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, BL_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, BAT_LVL, NK_TOGG, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______), [WIN_BASE] = LAYOUT_tkl_ansi( KC_ESC, 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_PSCR, KC_CTANA, BL_STEP, KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_INS, KC_HOME, KC_PGUP, KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_DEL, KC_END, KC_PGDN, KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, TD_LSHIFTLANG1, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, TD_RSHIFTLANG2, KC_UP, KC_LCTL, KC_LGUI, KC_LALT, TD_SPCL1, KC_RALT, KC_RGUI, MO(WIN_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT), [WIN_FN] = LAYOUT_tkl_ansi( _______, KC_BRID, KC_BRIU, KC_TASK, KC_FILE, BL_DOWN, BL_UP, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______, BT_HST1, BT_HST2, BT_HST3, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, TD_HMPGUP,KC_UP, TD_EDPGDN,_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, TD_LKKK, TD_RKKK, _______, KC_LEFT, KC_DOWN, KC_RGHT, _______, _______, _______, _______, _______, _______, _______, _______, BAT_LVL, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______) };
myTapDance.h
myTapDance.h#pragma once #include "myKeyMap.h" enum { SINGLE_TAP = 1, SINGLE_HOLD, DOUBLE_TAP, TRIPLE_TAP, QUADRUPLE_TAP, }; typedef struct { bool is_press_action; int state; } tap; enum { HMPGUP, // home, PgUp EDPGDN, // end, PgDn LSHIFTLANG1, // L shift, LANG1 RSHIFTLANG2, // R shift, LANG2 LKAKKO, // (,[,{,< RKAKKO, // ),],},> SPCL1, // SPACE, L1 }; #define TD_HMPGUP TD(HMPGUP) #define TD_EDPGDN TD(EDPGDN) #define TD_LSHIFTLANG1 TD(LSHIFTLANG1) #define TD_RSHIFTLANG2 TD(RSHIFTLANG2) #define TD_LKKK TD(LKAKKO) #define TD_RKKK TD(RKAKKO) #define TD_SPCL1 TD(SPCL1) int cur_dance (tap_dance_state_t *); void x_finished_1 (tap_dance_state_t *, void *); void x_reset_1 (tap_dance_state_t *, void *); void x_finished_2 (tap_dance_state_t *, void *); void x_reset_2 (tap_dance_state_t *, void *); void x_finished_3 (tap_dance_state_t *, void *); void x_reset_3 (tap_dance_state_t *, void *); void x_finished_4 (tap_dance_state_t *, void *); void x_reset_4 (tap_dance_state_t *, void *); void x_finished_5 (tap_dance_state_t *, void *); void x_reset_5 (tap_dance_state_t *, void *);
myTapDance.c
myTapDance.c#include "myTapDance.h" tap_dance_action_t tap_dance_actions[] = { [HMPGUP] = ACTION_TAP_DANCE_DOUBLE(KC_HOME, KC_PGUP), [EDPGDN] = ACTION_TAP_DANCE_DOUBLE(KC_END, KC_PGDN), [LKAKKO] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished_1, x_reset_1), [RKAKKO] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished_2, x_reset_2), [LSHIFTLANG1] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished_3, x_reset_3), [RSHIFTLANG2] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished_4, x_reset_4), [SPCL1] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished_5, x_reset_5), }; int cur_dance (tap_dance_state_t *state) { if (state->count == 1) { if (!state->pressed){ return SINGLE_TAP; } else{ return SINGLE_HOLD; } } else if (state->count == 2) { return DOUBLE_TAP; } else if (state->count == 3) { return TRIPLE_TAP; } else if (state->count == 4) { return QUADRUPLE_TAP; } else { return 6; //magic number. } }; //instanalize an instance of 'tap' for the 'x' tap dance. static tap xtap_state = { .is_press_action = true, .state = 0 }; // SPCL1用 static tap xtap_state_SPCL1 = { .is_press_action = true, .state = 0 }; // tap:(、ダブルタップ:{、トリプルタップ:[、トリプルタップで< void x_finished_1 (tap_dance_state_t *state, void *user_data) { xtap_state.state = cur_dance(state); switch (xtap_state.state) { case SINGLE_TAP: // タップで( register_code16(KC_LPRN); break; case DOUBLE_TAP: // ダブルタップで{ register_code16(KC_LCBR); break; case TRIPLE_TAP: // トリプルタップで[ register_code16(KC_LBRC); break; case QUADRUPLE_TAP: // トリプルタップで< register_code16(KC_LABK); break; default: break; } }; void x_reset_1 (tap_dance_state_t *state, void *user_data) { switch (xtap_state.state) { case SINGLE_TAP: // タップで( unregister_code16(KC_LPRN); break; case DOUBLE_TAP: // ダブルタップで{ unregister_code16(KC_LCBR); break; case TRIPLE_TAP: // トリプルタップで[ unregister_code16(KC_LBRC); break; case QUADRUPLE_TAP: // トリプルタップで< unregister_code16(KC_LABK); break; default: break; } xtap_state.state = 0; }; // tap:)、ダブルタップ:}、トリプルタップ:]、トリプルタップで> void x_finished_2 (tap_dance_state_t *state, void *user_data) { xtap_state.state = cur_dance(state); switch (xtap_state.state) { case SINGLE_TAP: // タップで) register_code16(KC_RPRN); break; case DOUBLE_TAP: // ダブルタップで} register_code16(KC_RCBR); break; case TRIPLE_TAP: // トリプルタップで] register_code16(KC_RBRC); break; case QUADRUPLE_TAP: // トリプルタップで> register_code16(KC_RABK); break; default: break; } }; void x_reset_2 (tap_dance_state_t *state, void *user_data) { switch (xtap_state.state) { case SINGLE_TAP: // タップで) unregister_code16(KC_RPRN); break; case DOUBLE_TAP: // ダブルタップで} unregister_code16(KC_RCBR); break; case TRIPLE_TAP: // トリプルタップで] unregister_code16(KC_RBRC); break; case QUADRUPLE_TAP: // トリプルタップで> unregister_code16(KC_RABK); break; default: break; } xtap_state.state = 0; }; // tap:L shuft、ダブルタップ:LANG1、ホールドでL shiift void x_finished_3 (tap_dance_state_t *state, void *user_data) { xtap_state.state = cur_dance(state); switch (xtap_state.state) { case SINGLE_TAP: // タップでL shiift register_code16(KC_LSFT); break; case DOUBLE_TAP: // ダブルタップでLANG1 register_code16(KC_LNG1); break; case SINGLE_HOLD: // ホールドでL shift register_code16(KC_LSFT); break; default: break; } }; void x_reset_3 (tap_dance_state_t *state, void *user_data) { switch (xtap_state.state) { case SINGLE_TAP: // タップでL shift unregister_code16(KC_LSFT); break; case DOUBLE_TAP: // ダブルタップでLANG1 unregister_code16(KC_LNG1); break; case SINGLE_HOLD: // ホールドでL shift unregister_code16(KC_LSFT); break; default: break; } xtap_state.state = 0; }; // tap:R shuft、ダブルタップ:LANG2、ホールドでR shiift void x_finished_4 (tap_dance_state_t *state, void *user_data) { xtap_state.state = cur_dance(state); switch (xtap_state.state) { case SINGLE_TAP: // タップでR shiift register_code16(KC_RSFT); break; case DOUBLE_TAP: // ダブルタップでLANG2 register_code16(KC_LNG2); break; case SINGLE_HOLD: // ホールドでR shift register_code16(KC_RSFT); break; default: break; } }; void x_reset_4 (tap_dance_state_t *state, void *user_data) { switch (xtap_state.state) { case SINGLE_TAP: // タップでR shift unregister_code16(KC_RSFT); break; case DOUBLE_TAP: // ダブルタップでLANG2 unregister_code16(KC_LNG2); break; case SINGLE_HOLD: // ホールドでR shift unregister_code16(KC_RSFT); break; default: break; } xtap_state.state = 0; }; // tap:SPACE、ホールドでL1 void x_finished_5 (tap_dance_state_t *state, void *user_data) { xtap_state_SPCL1.state = cur_dance(state); switch (xtap_state_SPCL1.state) { case SINGLE_TAP: // タップでSPACE register_code16(KC_SPC); break; case SINGLE_HOLD: // ホールドでL1 layer_on(WIN_FN); break; default: break; } }; void x_reset_5 (tap_dance_state_t *state, void *user_data) { switch (xtap_state_SPCL1.state) { case SINGLE_TAP: // タップでSPACE unregister_code16(KC_SPC); break; case SINGLE_HOLD: // ホールドでL1 layer_off(WIN_FN); break; default: break; } xtap_state_SPCL1.state = 0; };
-
myKeyMapをビルドする。
vagrantmake keychron/k8_pro/ansi/white:myKeyMap
説明
mac用レイヤー2つは基本的に触っていない(使わないので放置)。
win_baseの動作は下記。
- 基本的には普通のキーボード通り。
- 左シフト2回連打でIME ON
- 右シフト2回連打でIME OFF
- スペース長押しでレイヤー移動
win_fn
- 方向キー各種
- かっこ各種
詳細は下記の通り。
win_baseレイヤー
操作 | 動作 | |
---|---|---|
左シフトを1回押す | → | 普通のシフト |
左シフトを2回押す | → | Lang1(変換) |
左シフトを長押し | → | シフトの長押し |
右シフトを1回押す | → | 普通のシフト |
右シフトを2回押す | → | Lang2(無変換) |
右シフトを長押し | → | シフトの長押し |
スペースを1回押す | → | スペース |
スペースを長押し | → | win_fnレイヤーに移行 |
win_fnレイヤー(スペースを押しながら)
操作 | 動作 | |
---|---|---|
J | → | ← |
K | → | ↓ |
L | → | → |
I | → | ↑ |
Uを1回押す | → | home |
Uを2回押す | → | pageUp |
Oを1回押す | → | end |
Oを2回押す | → | pageDown |
Fを1回押す | → | ( |
Fを2回押す | → | [ |
Fを3回押す | → | { |
Fを4回押す | → | < |
Gを1回押す | → | ) |
Gを2回押す | → | ] |
Gを3回押す | → | } |
Gを4回押す | → | > |
Discussion