16bitキーコードで LT(layer, kc) したかった
LT(layer, kc) とは
qmk_firmware のキー設定の記法で、ホールドしている間レイヤを有効化し、タップで指定したキーコードを送信します。
LT(layer, kc) - momentarily activates layer when held, and sends kc when tapped. Only supports layers 0-15.
例えば LT(1, KC_SPC) と記述したキーは、タップ(短押し)だとスペースを入力、ホールド(長押し)をしている間はレイヤ1を有効にします。
LT(layer, kc) の制約
マクロ定義を見るとわかりますが、kc には 8bit のキーコードしか使えないという制約があります。
#define LT(layer, kc) (QK_LAYER_TAP | (((layer)&0xF) << 8) | ((kc)&0xFF)
例えばキーマップに LT(3, S(KC_BSLS)) と書いても LT(3, KC_BSLS) として処理されるので、タップしたときに | (Shift + \) ではなく \ が入力されてしまいます。この制約を回避したかったのでやってみました。
方法1: Tap Danceを使ってTap-Hold
rules.mk で TAP_DANCE_ENABLE = yes として Tap Dance を有効化し、Example 3: Send : on Tap, ; on Hold のサンプルコードを使って、ACTION_TAP_DANCE_TAP_HOLD(tap_kc16, hold_kc16) の形でアクションを定義すると tap_kc16 を {tap,register,unregister}_code16(tap_kc16) で送信してくれるのでタップしたときのキーコードの制約は回避できます。
一方で hold_kc16 の方も {regsiter,unregister}_code16(hold_kc16) で送信されるのですが、ホールド中にレイヤを有効にしようと hold_kc16 に MO(n) を指定しても何も起こりません。
enum {
TD_PIPE_MO3,
};
tap_dance_action_t tap_dance_actions[] = {
[TD_PIPE_MO3] = ACTION_TAP_DANCE_TAP_HOLD(S(KC_BSLS), MO(3)),
};
サンプルコードを一部変更して QK_MOMENTARY <= MO(n) <= QK_MOMENTARY_MAX の分岐を追加し、layer_on() , layer_off() することで、ホールド中にレイヤを有効にするという期待動作になります。これで解決。
void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) {
tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
if (state->pressed) {
if (state->count == 1
#ifndef PERMISSIVE_HOLD
&& !state->interrupted
#endif
) {
if (QK_MOMENTARY <= tap_hold->hold && tap_hold->hold <= QK_MOMENTARY_MAX) {
layer_on(QK_MOMENTARY_GET_LAYER(tap_hold->hold));
} else {
register_code16(tap_hold->hold);
}
tap_hold->held = tap_hold->hold;
} else {
register_code16(tap_hold->tap);
tap_hold->held = tap_hold->tap;
}
}
}
void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) {
tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
if (tap_hold->held) {
if (QK_MOMENTARY <= tap_hold->held && tap_hold->held <= QK_MOMENTARY_MAX) {
layer_off(QK_MOMENTARY_GET_LAYER(tap_hold->held));
} else {
unregister_code16(tap_hold->held);
}
tap_hold->held = 0;
}
}
方法2: 未使用キーコードを流用しタップ動作を上書き
TAP_DANCE_ENABLE = yes とするとファームサイズがわりと増えてしまうので、別の方法はないかと調べていたところ、灯台下暗しで Mod-Tap のドキュメントにタップの動作を置き換えるサンプルコードがありました。
LT(layer, kc) の kc には8bitキーコードの範囲で使ってないものを書いておき、タップ動作を上書きすることで、実質 kc に16bitキーコードも指定できたということになります。例えば
enum custom_user_keycodes {
// Reuse unused basic keycodes
MY_PIPE = KC_NUBS,
};
と定義して keymaps 配列に LT(3, MY_PIPE) と書いた場合、それを process_record_user() で
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case LT(3, MY_PIPE):
if (record->tap.count) {
static uint16_t kc;
if (record->event.pressed) {
kc = S(KC_BSLS);
register_code16(kc);
return false;
} else if (kc) {
unregister_code16(kc);
kc = 0;
return false;
}
}
break;
default:
break;
}
return true;
}
としてやると、当該キーをタップしたときに | (Shift + \) が入力されるようになります。
なお、Mod-Tapのドキュメント中のサンプルコードでは tap_code16(kc) を使っているのですが、それだと「タターン」とタップ+ホールドしてもキーリピートが効かなかったので register_code16(kc) と unregister_code16(kc) を呼ぶようにしています。
Discussion