ErgoDoxEZにて足を踏みならす代わりに指を踏みならそう(執筆途中)

タップダンスとは、ドラムを叩くことを禁止された黒人たちが、ドラムの代わりに足を踏みならし、音を奏でたことでできあがったモダンダンスの一つ(Wikipediaから)。

今回は、キーボードの話だ。
キーボードを踏みつけてしまっては壊れてしまう。
そのため、指でキーボード上をタップダンスで操ろうという試みだ。

# Tap Dance
1回の押下と2回連続押下で、キーが異なるという機能になる。
名前は似ているが、`ACTION_FUNCTION_TAP`とは違うようだ。

## 例えば
セミコロンキーを1回押下すれば、当然セミコロンが入力できる。
そして、セミコロンキーを2回連続で押下すれば、(コロンを設定しているならば)コロンが入力できる。
3回連続押下では、(設定しているならば)LEDが光る。
などなど・・・分かりにくい説明に思えるが、1つのキーで複数のキー入力の使い分けが可能と言うこと。

## [ACTION_FUNCTION_TAP](https://beta.docs.qmk.fm/features/feature_tap_dance "qmkのドキュメント")
では、[ACTION_FUNCTION_TAP](https://github.com/qmk/qmk_firmware/blob/master/tmk_core/common/action_code.h#L325)とは何か。
残念ながらよく分からない。[reddit](https://www.reddit.com/r/olkb/comments/5nh9xe/qmk_and_light_switch_toggle_switch/ "QMK and light switch / toggle switch")で説明している？
取りあえず、拙い説明を続ける。

唯一分かるのは、この機能を代替する場合骨が折れるだろうと言うこと。
簡単にするのが今回のタップダンスだというわけだ。


### ACTION関数
ヘッダファイルに`ACTION_FUNCTION_TAP`の記述があった。

```ErgoDoxEZ:action_code.h
enum action_kind_id {
    /* Key Actions */
    ・・・,
    ACT_FUNCTION        = 0b1111
};

typedef union {
    uint16_t code;
    struct action_kind {
        uint16_t param  :12;
        uint8_t  id     :4;
    } kind;
    struct action_key {
        uint8_t  code   :8;
　　　　・
　　　　・
　　　　・
    }
}

/* action utility */
#define ACTION(kind, param)             ((kind)<<12 | (param))


/* Function */
enum function_opts {
    FUNC_TAP = 0x8,     /* indciates function is tappable */
};

#define ACTION_FUNCTION_TAP(id)    ACTION(ACT_FUNCTION, FUNC_TAP<<8 | (id))
```

ACTIONは、[Function](https://qiita.com/chesscommands/items/ce2883ad0a0c6c27c8de#funcn "自前の解説")の一種？



## SPC
これ何？

## 簡易的な使い方(タップダンス披露開始)
いつも通り`rules.mk`に記載することで使えるようになる。

```ErgoDoxEZ:rules.mk
TAP_DANCE_ENABLE=yes
```

キーの割り当て方法は`TD(x)`のように行う。もちろんxは事前準備に仕込んだ数字(文字)になる。
~~なぜか`ACTION_FUNCTION_TAP(x)`ではなく、~~ **[TD(x)](https://github.com/qmk/qmk_firmware/blob/ee700b2e831067bdb7584425569b61bc6329247b/quantum/process_keycode/process_tap_dance.h#L36)**だ。
エイリアスに似た定義をするからなのだろう・・・きっと。


## 大雑把な使い方`ACTION_TAP_DANCE_DOUBLE`
1回のタップと~~それ以外の連続タップ~~ 2回連続タップで挙動が異なる。

関数名：﻿﻿ACTION_TAP_DANCE_DOUBLE
引数：2つ

利用例：ACTION_TAP_DANCE_DOUBLE(kc1, kc2)

ただし、これをそのままキーに割り当てることは出来ない。
また、事前準備として、別ファイルの用意が必要だ。

```ErgoDoxEZ:rules.mk
TAP_DANCE_ENABLE = yes
```

このファイルを用意しなければ、コンパイルエラーが発生する。
今までそんなことはなかったため、ヘッダファイルか何かを読み込むような何かがあるのだろう。
そして、このファイルを追加する以上hexファイルの容量が増える。
気をつけること。

他のコーディング方法も今までのより難易度は高くなっている(ほんのちょっとだけど)。

```ErgoDoxEZ:keymap.c
enum TapDanceDeclarations{
  TD_BSPACE_K = 0
};

//Tap Dance Definitions
qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_BSPACE_K]  = ACTION_TAP_DANCE_DOUBLE(KC_BSPACE, KC_K)
};

[BASEPlate] = LAYOUT_ergodox(  // layer 0 : default
       // left hand
		TD(TD_BSPACE_K),	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,			/* 7桁	*/
		KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,			/* 7桁	*/
		KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,			/* 6桁	*/
		KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,					/* 7桁	*/
		KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,			/* 5桁	*/
　　　・
　　　・
　　　・
```

TDキーは、左上(通常のキーボードであればEscキーやチルダに位置する場所)に設定した。

`tap_dance_actions`は、何となく分かって頂けると思うのだが・・・これは、0番目の配列から順番にタップダンスの挙動を格納する配列名だ。
今回は例と同じ`ACTION_TAP_DANCE_DOUBLE`を使った(なぜにヘッダファイルに記述している関数を使うのか)。

例では無名enumを使っていたが、分かりにくいので今回名前を付けた。

上記の挙動は、tap_dance_actions配列の`[TD_BSPACE_K]`などによって変わる。
今回は、まさにタップダンスと言うことで、1回目の押下はバックスペースキーとして働き、2回連続押下はKキーとして働く。
3回連続の場合は、Kキー押下後にバックスペースキーという感じになる。

## 複雑だが大雑把な説明で1回と2回連続タップの挙動確認
例によって例を使う。

```
void dance_cln_finished (qk_tap_dance_state_t *state, void *user_data) {
  if (state->count == 1) {
    register_code (KC_RSFT);
    register_code (KC_SCLN);
  } else {
    register_code (KC_SCLN);
  }
}

void dance_cln_reset (qk_tap_dance_state_t *state, void *user_data) {
  if (state->count == 1) {
    unregister_code (KC_RSFT);
    unregister_code (KC_SCLN);
  } else {
    unregister_code (KC_SCLN);
  }
}

//All tap dance functions would go here. Only showing this one.
qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_BSPACE_K]  = ACTION_TAP_DANCE_DOUBLE(KC_BSPACE, KC_K),
  [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, dance_cln_finished, dance_cln_reset)
};

[BASEPlate] = LAYOUT_ergodox(  // layer 0 : default
       // left hand
		TD(CT_CLN),	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,			/* 7桁	*/
　　　　・
　　　　・
　　　　・
```

※順番大事
C言語はあまり頭が良くないため、`dance_cln_finished`や`dance_cln_reset`を先に記述してからtap_dance_actions配列のなかでdance_cln_finished関数やdance_cln_reset関数を呼び出す必要がある。
tap_dance_actions配列を先に書いてから関数をその後ろに記述した場合、エラーになる。

上記の内容は、1回押下で`:`キーになり、2回連続押下で`;`キーになる。

第1引数は、指定の機能を呼び出すのだが、今回は`NULL`として何もしないようにしている。
第2引数は、dance_cln_finished関数を呼び出す。
第3引数は、dance_cln_reset関数を呼び出す。

キーを押すことで、上記の引数の内容が第1引数から第3引数まで順番に呼び出される。

`register_code`は、キー押下を表す。
`unregister_code`は、キー押上を表す。

なぜに例の癖して第1引数をNullにしているのか分からない。どうやって使えば良いのか・・・。
普通のKC_Kを渡した場合、当然エラーになる。

## タップで文字がダンスする
まさに、タップダンス^^

```
void dance_egg (qk_tap_dance_state_t *state, void *user_data) {
  if (state->count >= 100) {
    SEND_STRING ("Safety dance!");
    reset_tap_dance (state);
  }
}

qk_tap_dance_action_t tap_dance_actions[] = {
 [CT_EGG] = ACTION_TAP_DANCE_FN (dance_egg)
};
```

100回以上連続押下をすることで、`Safety dance`の文字列がテキストエディタに入力される。
もちろん、連続タップの時間が設定されているため、私は連続100回押下無理でした。
そのため3回にして文字をダンスさせた。

>  if (state->count >= 3) {

4回連続押下でも文字を躍らせることが出来た。
何に使うのか分からないが、メールを打つときの定型文には良いかもしれない。

## LEDを使って光りを踊らせることも可能
例によって説明しているのだが、省略する。
過去の投稿でLED点灯は満足している。
正直に言えば、LEDは興味が薄れてしまった。
なにせ、LED付きのHelixごみキーボードでやる気をそがれてしまった。
無念だ。

## 4種類のタップダンス
かなり複雑な設定により、1回押下・押し続ける・2回連続押下・2回連続押下後押し続けることの4種類を設定することが可能らしい。
取りあえず、例通りに進めるが、見ただけで複雑なため、1回の作業では理解できないだろう。
そのため、今後加筆していくはず・・・きっと。
究めれば、2回連続押下の1回押下と言う訳の分からないことや3回連続押下や3回連続押下後の押し続けも対応可能らしい。
(自分でも何を言っているのか分かっていない)

例では、何やら複数のファイルを使い、mkファイルにも書き込みをしている。
しかし、めんどくさいので、使わない。
そして、コメント部分は極力削除して、プログラム部分のみ流用している。

```c:keymap.c
enum TapDanceDeclarations{
  XX_CTL,
  SOME_OTHER_DANCE
};

typedef struct {
  bool is_press_action;
  int state;
} tap;

enum TapDanceQuadFunction {
  SINGLE_TAP = 1,
  SINGLE_HOLD = 2,
  DOUBLE_TAP = 3,
  DOUBLE_HOLD = 4,
  DOUBLE_SINGLE_TAP = 5, //send two single taps
  TRIPLE_TAP = 6,
  TRIPLE_HOLD = 7
};

int cur_dance (qk_tap_dance_state_t *state) {
  if (state->count == 1) {
    if (state->interrupted || !state->pressed)  return SINGLE_TAP;
    else return SINGLE_HOLD;
  }
  else if (state->count == 2) {
    if (state->interrupted) return DOUBLE_SINGLE_TAP;
    else if (state->pressed) return DOUBLE_HOLD;
    else return DOUBLE_TAP;
  }
  if (state->count == 3) {
    if (state->interrupted || !state->pressed)  return TRIPLE_TAP;
    else return TRIPLE_HOLD;
  }
  else return 8; //magic number. At some point this method will expand to work for more presses
}

//instanalize an instance of 'tap' for the 'x' tap dance.
static tap xtap_state = {
  .is_press_action = true,
  .state = 0
};

void x_finished (qk_tap_dance_state_t *state, void *user_data) {
  xtap_state.state = cur_dance(state);
  switch (xtap_state.state) {
    case SINGLE_TAP: register_code(KC_X); break;
    case SINGLE_HOLD: register_code(KC_LCTRL); break;
    case DOUBLE_TAP: register_code(KC_ESC); break;
    case DOUBLE_HOLD: register_code(KC_LALT); break;
    case DOUBLE_SINGLE_TAP: register_code(KC_X); unregister_code(KC_X); register_code(KC_X);
  }
}

void x_reset (qk_tap_dance_state_t *state, void *user_data) {
  switch (xtap_state.state) {
    case SINGLE_TAP: unregister_code(KC_X); break;
    case SINGLE_HOLD: unregister_code(KC_LCTRL); break;
    case DOUBLE_TAP: unregister_code(KC_ESC); break;
    case DOUBLE_HOLD: unregister_code(KC_LALT);
    case DOUBLE_SINGLE_TAP: unregister_code(KC_X);
  }
  xtap_state.state = 0;
}

qk_tap_dance_action_t tap_dance_actions[] = {
  [TD_BSPACE_K]  = ACTION_TAP_DANCE_DOUBLE(KC_BSPACE, KC_K),
  [XX_CTL]       = ACTION_TAP_DANCE_FN_ADVANCED(NULL,x_finished, x_reset)
};
```

これだけで何をやっているのか分からず混乱する。
enumは、説明不要だよね。
`typedef struct〜なんちゃら`は、構造体なので、変数の詰め込み版だと思えば良いだけ。たいして難しいものではない・・・と思うことにしよう。
その下で、`static tap xtap_state〜なんちゃら`と言う記述があり、そこで構造体に値を設定している(何のために設定するのか不明)。
きっと`xtap_state`の`.state`にて、押下回数を記録させているんだと思う。

それ以降は関数になる。
ここまで来たら理解できない処理ばかり・・・あぁ。

これらの関数を呼び出しているのが、tap_dance_actions配列にある`ACTION_TAP_DANCE_FN_ADVANCED`なのだが・・・。
また、3つの引数を渡しているが、1つ目はNULLと言う・・・。

第2引数でキーを押し続け、第3引数でキーを離す処理が絶対的な組合せなのだろう。

そのキー押下に関してだけでも理解したい。
1回押下：Xキー押下のこと。
1回押し続ける：コントロールキー押下のこと。
2回押下：Escキー押下のこと。
2回押下で2回目は押し続ける：Altキー押下のこと。
ダブルシングルタップ：　不明　理解の範疇を超えている。どのようにすれば再現するのかもよく分からない。キー押下のことでもない。挙動を目視できない。3回連続押下でも何も起きない。
(結局理解できなかった)

### [1回押下(](https://github.com/qmk/qmk_firmware/blob/700aa8fe1c5775ba26358c343c122997225fafcc/tmk_core/common/keycode.h#L232)・[2回押下\[](https://github.com/qmk/qmk_firmware/blob/700aa8fe1c5775ba26358c343c122997225fafcc/tmk_core/common/keycode.h#L68)・[3回押下{](https://github.com/qmk/qmk_firmware/blob/dad79cafcd9b3401b68fb29aaa311e999c4c4e6e/docs/keycodes_basic.md#punctuation)
シフトキー押下との組みあわせキーコードが使えない。
結構めんどくさいが、取りあえず出来ることは出来る。

無駄な部分はほぼ省いた状態で記述する(上記で説明済みなので)。

<details><summary>省略無し</summary><div>

ヘッダファイルの読み込み不要だった。
ついでに、閉じ括弧も同じように実装している。
このとき、rprn_tap_state関数を新しく追加している(用心のために)。

```c:keymap.c
enum TapDanceDeclarations{
// キーコード宣言
  LPRN_LBRC_LCBR, // 開き括弧用
  RPRN_RBRC_RCBR, // 閉じ括弧用

  SOME_OTHER_DANCE
};

enum TapDanceQuadFunction {
// タップ回数宣言
	SINGLE_TAP = 1,
	SINGLE_HOLD = 2,
	DOUBLE_TAP = 3,
	DOUBLE_HOLD = 4,
	DOUBLE_SINGLE_TAP = 5, //send two single taps
	TRIPLE_TAP = 6,
	TRIPLE_HOLD = 7
};

typedef struct {
// タップの保存変数宣言(正確には構造体)
	bool is_press_action;
	int state;
} tap;

static tap lprn_tap_state = {
// タップの保存変数(開き括弧用)
  .is_press_action = true,
  .state = 0
};

static tap rprn_tap_state = {
// タップの保存変数(閉じ括弧用)
  .is_press_action = true,
  .state = 0
};

int cur_dance (qk_tap_dance_state_t *state) {
// タップに関する挙動を処理する部分だろう。
	if (state->count == 1) {
		if (state->interrupted || !state->pressed)  return SINGLE_TAP;
		else return SINGLE_HOLD;
	}
	else if (state->count == 2) {
		if (state->interrupted) return DOUBLE_SINGLE_TAP;
		else if (state->pressed) return DOUBLE_HOLD;
		else return DOUBLE_TAP;
	}
	if (state->count == 3) {
		if (state->interrupted || !state->pressed)  return TRIPLE_TAP;
		else return TRIPLE_HOLD;
	}
	else return 8; //magic number. At some point this method will expand to work for more presses
}

void LPRN_finished (qk_tap_dance_state_t *state, void *user_data) {
// キー押下(開き括弧)
  lprn_tap_state.state = cur_dance(state);
  switch (lprn_tap_state.state) {
    case SINGLE_TAP: register_code(KC_LSFT); register_code(KC_9); 
                                             break;
    case DOUBLE_TAP: register_code(KC_LBRACKET); break;
    case TRIPLE_TAP: register_code(KC_LSFT); register_code(KC_LBRACKET); 
                                             break;
  }
}

void LPRN_reset (qk_tap_dance_state_t *state, void *user_data) {
// キー押上(開き括弧)
  switch (lprn_tap_state.state) {
    case SINGLE_TAP: unregister_code(KC_9); unregister_code(KC_LSFT); 
                                             break;
    case DOUBLE_TAP: unregister_code(KC_LBRACKET); break;
    case TRIPLE_TAP: unregister_code(KC_LBRACKET); unregister_code(KC_LSFT); 
                                             break;
  }
  lprn_tap_state.state = 0;
}


void RPRN_finished (qk_tap_dance_state_t *state, void *user_data) {
// キー押下(閉じ括弧)
  rprn_tap_state.state = cur_dance(state);
  switch (rprn_tap_state.state) {
    case SINGLE_TAP: register_code(KC_LSFT); register_code(KC_0); 
                                             break;
    case DOUBLE_TAP: register_code(KC_RBRACKET); break;
    case TRIPLE_TAP: register_code(KC_LSFT); register_code(KC_RBRACKET); 
                                             break;
  }
}

void RPRN_reset (qk_tap_dance_state_t *state, void *user_data) {
// キー押上(閉じ括弧)
  switch (rprn_tap_state.state) {
    case SINGLE_TAP: unregister_code(KC_0); unregister_code(KC_LSFT); 
                                             break;
    case DOUBLE_TAP: unregister_code(KC_RBRACKET); break;
    case TRIPLE_TAP: unregister_code(KC_RBRACKET); unregister_code(KC_LSFT); 
                                             break;
  }
  rprn_tap_state.state = 0;
}



[BASEPlate] = LAYOUT_ergodox(  // layer 0 : default
       // left hand
		TD(CT_CLN),	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,	KC_NO,
//　　　　・
//　　　　・
//　　　　・
)
};


//Tap Dance Definitions
qk_tap_dance_action_t tap_dance_actions[] = {
	[LPRN_LBRC_LCBR]  = ACTION_TAP_DANCE_FN_ADVANCED(NULL, LPRN_finished, LPRN_reset),
	[RPRN_RBRC_RCBR]  = ACTION_TAP_DANCE_FN_ADVANCED(NULL, RPRN_finished, RPRN_reset)

};


```

</div></details>

調べたからと言って、快適に使いこなせるかどうかは別問題だが・・・。
なにせ、キーマッピング時の配置に悩みに悩むからな・・・(多用し、混乱し、戻し・・・さらに、汎用キーボードにまで戻すと言う・・・)。

```c:keymap.c
enum TapDanceDeclarations{
  XX_CTL,
  LPRN_LBRC_LCBR,
  SOME_OTHER_DANCE
};

static tap lprn_tap_state = {
  .is_press_action = true,
  .state = 0
};

void LPRN_finished (qk_tap_dance_state_t *state, void *user_data) {
  lprn_tap_state.state = cur_dance(state);
  switch (lprn_tap_state.state) {
//    case SINGLE_TAP: register_code(KC_LPRN); break;
//    case SINGLE_TAP: register_code('('); break;
//    case SINGLE_TAP: register_code( SS_LSFT("(") ); break;
//    case SINGLE_TAP: register_code( SS_DOWN(X_LSHIFTR) ); break;
    case SINGLE_TAP: register_code(KC_LSFT); register_code(KC_9); 
                                             break;
    case DOUBLE_TAP: register_code(KC_LBRACKET); break;
//    case TRIPLE_TAP: register_code(KC_LCBR); break;
    case TRIPLE_TAP: register_code(KC_LSFT); register_code(KC_LBRACKET); 
                                             break;
  }
}

void LPRN_reset (qk_tap_dance_state_t *state, void *user_data) {
  switch (lprn_tap_state.state) {
    case SINGLE_TAP: unregister_code(KC_9); unregister_code(KC_LSFT); 
                                             break;
    case DOUBLE_TAP: unregister_code(KC_LBRACKET); break;
    case TRIPLE_TAP: unregister_code(KC_LBRACKET); unregister_code(KC_LSFT); 
                                             break;
  }
  xtap_state.state = 0;
}
```

問題点は、コメントアウト(//行)で、`KC_LPRN`は、9キーとShiftキーの組合せで実現している。
それを今回使用するつもりだったが、コンパイルエラーが発生し、なぜか使えない。

仕方ないため、Shiftキーを押し続け、その後9キーを押すことで括弧入力を実現した。
面倒だぞ。
当然ながら`LPRN_reset`関数で、それを押し上げる処理を記述しなければならない。
ぬぅぅめんどくさい。

しかし、実現できた。
良かった。

4回連続押下は、無効になり、何も入力されない。
そんな処理を記述していないから当たり前なのだが・・・。

実際に使ってみて、あまり使い勝手が良いように感じられなかった。
今回お試し利用だからかな・・・。

















# 使い方不明
他にも使えるはずの関数が軒を連ねているのだが、解説がないため使い方が分からずじまいになっている。
もし、これらが使えるのであれば、もっとキーボードの利用方法の幅は広がるだろう。


ACTION_TAP_DANCE_DOUBLE関数が使えるならば、`ACTION_TAP_DANCE_DUAL_ROLE`や`ACTION_TAP_DANCE_FN`も使えるだろう。

preprocess_tap_dance関数は、マクロが使えるような引数名に見える。

[process_tap_dance.h](https://github.com/qmk/qmk_firmware/blob/ee700b2e831067bdb7584425569b61bc6329247b/quantum/process_keycode/process_tap_dance.h)を読み解けるかどうかが鍵かな。


## [process_tap_dance.c](https://github.com/qmk/qmk_firmware/blob/ee700b2e831067bdb7584425569b61bc6329247b/quantum/process_keycode/process_tap_dance.c)
このソースファイルも存在がよく分からない。

使い方の説明がないのは何故だろうか。



# 最後に
幸せなら足ならそう。
幸せなら手を叩こう。
幸せなら指ならそう。
幸せならキーボードで奏でよう。
幸せなら態度で示そうよ。
今日も元気にキーマッパー。

以上だ。