QMKで矢印キーの加速装置を作った

# 概要
押している間、各矢印キーを高速で連打するカスタムキーを作成しました。

# 動作
矢印キーを押している間に以下で定義するSPD_UPキーコードを配置したキーを押すと、矢印キーが高速で連打されます。
また、SPD_UPキーコードを押している間に矢印キーを押しても同様の動きをします。
SPD_UPキーを押している間加速し、離すと減速します。再加速も可能です。
複数の矢印キーを押した際に直感的に正しい動きにならないこともありますが、仕様としてご理解ください。
ex)上矢印を押しながらSPD_UPを押し、そのまま下矢印を押しっぱなしにするとカーソルは高速で下に移動する。
しかし、下矢印を先に離すとカーソルは下方向に移動し続ける。
実用的にはほぼ無関係な動作と思われます。
複数押しをしても、後に入力された方向の入力のみが走るため、斜め移動のような動作は不可能です。
また、ブラウザ等のスクロール加速目的には使用できません（キーを高速連打することで実現しているため）。

# コード（しつこい解説付き）
```keymap.c
// 書く場所が分からなければ #include が終わった後あたりに記載してください------------

// SPD_UPキー用変数
// 加速装置のon/off判定
static bool spd_up = false;
// 矢印キーがいくつ押されているか(動作の調整用)
static int arrow_count = 0;
// どの矢印キーが押されているかの判定をする列挙型の変数定義を宣言
enum arrow_held {
    NONE = 0,
    UP, // 以降順番に1から数字が割り当てられます
    RIGHT,
    LEFT,
    DOWN
};
// 上記の変数
enum arrow_held arrow_held = NONE;

// カスタムキーコードの宣言
enum custom_keycodes {
    SPD_UP = SAFE_RANGE // キーボード毎に専用のSAFE_RANGEが設定されている場合があります
};
// 私はkeyballで設定したので、SAFE_RANGEの代わりにKEYBALL_SAFE_RANGEを使用しました
// 各キーボードのビルドガイド等を参照してください
// 特に記載がない場合は上記の通りで大丈夫だと思います。
//----------------------------------------------------------------------

//----------------------------------------------------------------------
// レイヤー定義がこの辺りになります
// 先ほど定義したキーコード"SPD_UP"をキーボードの好きなところに配置してください
//----------------------------------------------------------------------

// 書く場所が分からなければレイヤー定義の下に記載してください---------------------

// 作成したキーボードの種類によってはkeymap.cに既にprocess_record_user関数が記載されています。
/*
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (record->event.pressed) {
        switch(keycode) {
            //何かしらのコード
        }
    } else {
        switch(keycode){
            // 何かしらのコード
        }
    }
}
*/
// 上記のようなコードになっている場合は、何となく中に埋め込んだりせずにコードを追いながら修正してください。
// どちらかといえば下記をベースに既存のコードを書き直す方が良いと思います。
// 二重定義はエラーが発生しますので気をつけてください。

// キーを押すか離す際に呼ばれる関数です
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    // 押したキーコード毎に場合わけします
    switch(keycode){
        // 先ほど定義したカスタムキーコードです
        case SPD_UP:
            if (record->event.pressed) {
                // SPD_UPを押した際に、spd_upをtrueにします
                spd_up = true;
            } else {
                // SPD_UPを離した際に、spd_upをfalseにします
                spd_up = false;
                switch(arrow_held) {
                    // 最後に押した矢印キーを確認して、入力を再登録します
                    // これを行わないと、SPD_UPを放した際に矢印の入力が止まってしまいます
                    case RIGHT:
                        register_code(KC_RIGHT);
                        break;
                    case LEFT:
                        register_code(KC_LEFT);
                        break;
                    case UP:
                        register_code(KC_UP);
                        break;
                    case DOWN:
                        register_code(KC_DOWN);
                        break;
                    case NONE:
                        break;
                }
            }
            break;
        // 各方向キーを押した時に処理を行います
        case KC_RIGHT:
            if (record->event.pressed){
                // 右矢印が押された時にここが走ります
                // 押された矢印の向きを記憶します
                arrow_held = RIGHT;
                // 押されている矢印キーの数を増やします
                arrow_count++;
                // 通常のキー処理を行います
                // ここであればregister_code(KC_RIGHT)が行われている（ハズ）
                return true;
            } else {
                //押されている矢印キーの数を減らします
                arrow_count--;
                // 右矢印のキーの入力登録を解除します
                unregister_code(KC_RIGHT);
                if (arrow_count == 0){
                    // 矢印キーが一つも押されていなければ矢印キーの入力判定の変数をNONEにします
                    // 矢印キーの数を判定して解除しないと、複数押しからキーを放した時に加速ができません
                    arrow_held = NONE;
                }
            }
            break;
        case KC_LEFT:
            if (record->event.pressed){
                arrow_held = LEFT;
                arrow_count++;
                return true;
            } else {
                arrow_count--;
                unregister_code(KC_LEFT);
                if (arrow_count == 0){
                    arrow_held = NONE;
                }
            }
            break;
        case KC_UP:
            if (record->event.pressed){
                arrow_held = UP;
                arrow_count++;
                    return true;
            } else {
                arrow_count--;
                unregister_code(KC_UP);
                if (arrow_count == 0){
                    arrow_held = NONE;
                }
            }
            break;
        case KC_DOWN:
            if (record->event.pressed){
                arrow_held = DOWN;
                arrow_count++;
                    return true;
            } else {
                arrow_count--;
                unregister_code(KC_DOWN);
                if (arrow_count == 0){
                    arrow_held = NONE;
                }
            }
            break;

    }
    return true;
}

// QMKがキーの押下をチェックする時に呼び出される関数
void matrix_scan_user(void) {

    if (spd_up) {
        // SPD_UPが押されているときにここが走る
        switch(arrow_held) {
            // 右矢印が最後に押された時、右矢印を2回押して20msのディレイをかける
            // ディレイをかけないと連打されすぎた分の入力が処理されてしまい、
            // キーを離したときに矢印の入力が止まってくれない
            case RIGHT:
                SEND_STRING(SS_TAP(X_RIGHT)SS_TAP(X_RIGHT)SS_DELAY(20));
            break;
            case LEFT:
                SEND_STRING(SS_TAP(X_LEFT)SS_TAP(X_LEFT)SS_DELAY(20));
            break;
            case UP:
                SEND_STRING(SS_TAP(X_UP)SS_TAP(X_UP)SS_DELAY(20));
            break;
            case DOWN:
                SEND_STRING(SS_TAP(X_DOWN)SS_TAP(X_DOWN)SS_DELAY(20));
            break;
            // どの矢印も押されていない場合は何もしない
            case NONE:
            break;
        }
    }

};
```

# 備考
単押しで4回押される程度にしたいのですが、現状だと6回押しになっています。
ディレイとTAPの回数で調整できそうですが、あまり少なくしてしまうと加速？って感じになってしまうので調整してください。

# 参考
[QMKでマクロをリピートできるようにしてみた](https://gist.github.com/ypsilon-takai/c4a249087ea19eabd1deb3f9f273de52)