ライフハック

プログラマブルキーボードのQMKキーマップをサラしてみる

私が使用しているキーボードは、いわゆるプログラマブルキーボードという部類で、どこのボタン(キースイッチ)に何のキーを割りあてるか設定できます。

今回はプログラマブルキーボード、オススメという記事になります。

プログラマブルキーボード使用歴

私はプログラマブルキーボードをはじめて使ったのはErgodox EZが発売されたころです。正確には覚えていませんが、2016年ぐらいだったと思います。なので、プログラマブルキーボード使用歴は、かれこれ6年ぐらいでしょうか。

その間、使用キーボードはErgodox EZから、craw44という自作キーボードに変わりましたが、どちらも分離タイプのキーボードなので、キー配置はそんなに変わっていません。今のキー配置で、もう6年近くやっていることになります。もうこのキー配置じゃないとタイピングする気が起きなくなっています。

私のメインPCはノートパソコンですが、ノートパソコンのキーボードは極力使いたくありません。ノートパソコンのキーを使うのは、カフェや自宅のベッドで軽くPCを触るぐらいです。自宅やオフィス、コワーキングスペースなどがっつり仕事するときはキーボードを持ち歩いています。

プログラマブルキーボードがオススメな理由

プログラマブルキーボードは、本当にオススメです。オススメな理由としては

  • 疲労軽減、ストレス軽減

の一言につきます。

通常のキーボードですと、使用頻度の高いEnterキーやCtrl/Cmdキーが小指でタイプするようになっています。なので手が不自然に動きますし、一番強度の低い小指が一番負荷がかかることになります。

一方、プログラマブルキーボードだと一番強度ある親指に、EnterキーはCtrl/Cmdキーを割り当てられるので、一般のキーボードと比べて疲労度が全然違います。特に日本人は、日本語の変換でEnterキーを多用するので、この恩恵は大きいです。

また、プログラマブルキーボードだとレイヤーが使えるので、Vimでなくても、全てのアプリケーションで、カーソル移動をVimのh,j,k,lでできます。一般のキーボードだと、矢印キーがホームポジションから遠く離れた打ちづらい位置にあるので、カーソル移動の度にホームポジションから指をはなさないといけません。

これも非常に疲れますし、何より度々ホームポジションから指をはなさいといけないので、ストレスが非常にたまります。

キーマップをサラしてみる

キーマップは各自が使いやすいと思うものを好きにカスタムすればいいのですが、他人のを見るのも何かしら参考になることがあったりします。

以下、私のQMKのキーマップをさらしてみます。craw44用、キーの数は44個が前提のキーマップです。

keymap.c

#include QMK_KEYBOARD_H
#include <stdio.h>

enum layer_number {
    _BASE = 0,
    _NUMBER,
    _SUPPORT,
};

// Win & Mac Lang
enum custom_keycodes {
    _JP_LANG,
    _EN_LANG,
    _ESC,
};


// layer toggle
#define KC_L_SPC LT(_NUMBER, KC_SPC)
#define KC_R_ENT LT(_SUPPORT, KC_ENT)

// Lang
#define KC_JP_LANG _JP_LANG
#define KC_EN_LANG _EN_LANG

// Custome ESC, send with _EN_LANG in order to change lang in Vim
#define KC_ESC_EN_LANG _ESC

// hold key
#define KC_C_TAB LCTL_T(KC_TAB)      // ctrl
#define KC_C_BS LCTL_T(KC_BSPC)      // ctrl
#define KC_A_DEL ALT_T(KC_DEL)       // alt

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [_BASE] = LAYOUT( \
    //,--------+--------+---------+--------+---------+--------.   ,--------+---------+--------+---------+--------+--------.
        _ESC   , KC_Q   , KC_W    , KC_E   , KC_R    , KC_T   ,     KC_Y   , KC_U    , KC_I   , KC_O    , KC_P   , KC_LBRC,
    //|--------+--------+---------+--------+---------+--------|   |--------+---------+--------+---------+--------+--------|
        KC_C_TAB, 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_N   , KC_M    , KC_COMM, KC_DOT  , KC_SLSH, KC_RSFT,
    //`--------+--------+---------+--------+---------+--------/   \--------+---------+--------+---------+--------+--------'
                          KC_LGUI, _EN_LANG, KC_L_SPC, KC_A_DEL,   KC_C_BS, KC_R_ENT, _JP_LANG, KC_RGUI
    //                 `----------+--------+---------+--------'   `--------+---------+--------+---------'
    ),

    [_NUMBER] = LAYOUT( \
    //,--------+--------+--------+--------+--------+--------.   ,--------+--------+--------+--------+--------+--------.
        KC_F12 , KC_F1  , KC_F2  , KC_F3  , KC_F4  , KC_F5  ,     KC_F6  , KC_F7  , KC_F8  , KC_F9  , KC_F10 , KC_F11 ,
    //|--------+--------+--------+--------+--------+--------|   |--------+--------+--------+--------+--------+--------|
        _______, KC_1   , KC_2   , KC_3   , KC_4   , KC_5   ,     KC_6   , KC_7   , KC_8   , KC_9   , KC_0  , KC_MINS,
    //|--------+--------+--------+--------+--------+--------|   |--------+--------+--------+--------+--------+--------|
        KC_LSFT, _______, _______, _______, _______, KC_HOME,     KC_END , _______, KC_COMM, KC_DOT , KC_SLSH, KC_RSFT ,
    //`--------+--------+--------+--------+--------+--------/   \--------+--------+--------+--------+--------+--------'
                          RESET  , _______, _______, _______,     _______, KC_RCTL, _______, _______
    //                  `--------+--------+--------+--------'   `--------+--------+--------+--------'
    ),

    [_SUPPORT] = LAYOUT( \
    //,--------+--------+--------+--------+--------+--------.   ,--------+--------+--------+--------+--------+--------.
        _______, _______, _______, _______, KC_RCBR, KC_RBRC,    KC_BSLS , KC_PIPE, _______, _______, _______, KC_EQL ,
    //|--------+--------+--------+--------+--------+--------|   |--------+--------+--------+--------+--------+--------|
        _______, _______, _______, _______, KC_PGUP, KC_PSCR,     KC_LEFT, KC_DOWN,  KC_UP , KC_RGHT, _______, _______,
    //|--------+--------+--------+--------+--------+--------|   |--------+--------+--------+--------+--------+--------|
        KC_LSFT, _______, _______, _______, KC_PGDN, KC_HOME,     KC_END , _______, _______, _______, KC_JYEN, KC_RO  ,
    //`--------+--------+--------+--------+--------+--------/   \--------+--------+--------+--------+--------+--------'
                          _______, _______, _______, _______,     _______, _______, _______, _______
    //                  `--------+--------+--------+--------'   `--------+--------+--------+--------'
    ),
};

#ifdef OLED_ENABLE

void render_layer_state(void) {
    switch (get_highest_layer(layer_state)) {
        case _BASE:
            oled_write_ln_P(PSTR("Layer: BASE"), false);
            break;
        case _NUMBER:
            oled_write_ln_P(PSTR("Layer: NUMBER"), false);
            break;
        case _SUPPORT:
            oled_write_ln_P(PSTR("Layer: SUPPORT"), false);
            break;
        default:
            oled_write_ln_P(PSTR("Layer: Undefined"), false);
    }
}

void render_logo(void) {
    static const char PROGMEM logo[] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0};
    oled_write_P(logo, false);
}

char keylog_str[24]  = {};
char keylogs_str[21] = {};
int  keylogs_str_idx = 0;

const char code_to_name[60] = {' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'R', 'E', 'B', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ';', '\'', ' ', ',', '.', '/', ' ', ' ', ' '};

void set_keylog(uint16_t keycode, keyrecord_t *record) {
    char name = ' ';
    if (keycode < 60) {
        name = code_to_name[keycode];
    }

    // update keylog
    snprintf(keylog_str, sizeof(keylog_str), "%dx%d, k%2d : %c", record->event.key.row, record->event.key.col, keycode, name);

    // update keylogs
    if (keylogs_str_idx == sizeof(keylogs_str) - 1) {
        keylogs_str_idx = 0;
        for (int i = 0; i < sizeof(keylogs_str) - 1; i++) {
            keylogs_str[i] = ' ';
        }
    }

    keylogs_str[keylogs_str_idx] = name;
    keylogs_str_idx++;
}

const char *read_keylog(void) { return keylog_str; }
const char *read_keylogs(void) { return keylogs_str; }

void oled_task_user(void) {
    if (is_keyboard_master()) {
        render_layer_state();
        oled_write_ln(read_keylog(), false);
        oled_write_ln(read_keylogs(), false);
    } else {
        render_logo();
    }
}

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (record->event.pressed) {
        set_keylog(keycode, record);
    }
    switch (keycode) {
        case _EN_LANG:
            if (record->event.pressed) {
                register_code(KC_MHEN);
                register_code(KC_LANG2);
            } else {
                unregister_code(KC_MHEN);
                unregister_code(KC_LANG2);
            }
            break;
        case _JP_LANG:
            if (record->event.pressed) {
                register_code(KC_HENK);
                register_code(KC_LANG1);
            } else {
                unregister_code(KC_HENK);
                unregister_code(KC_LANG1);
            }
            break;
        case _ESC:
            if (record->event.pressed) {
                register_code(KC_ESC);
                register_code(KC_MHEN);
                register_code(KC_LANG2);
            } else {
                unregister_code(KC_ESC);
                unregister_code(KC_MHEN);
                unregister_code(KC_LANG2);
            }
            break;
    }
    return true;
}

oled_rotation_t oled_init_user(oled_rotation_t rotation) {
    if (!is_keyboard_master()) return OLED_ROTATION_180;
    return rotation;
}

#endif

キー配置の方針としては

  • Win/Mac両対応
  • 日本語/英語の入力切り替えは、無変換/変換キーを使用する。Toggle切り替えはしない。

というものがあります。

私は元々、Windowsユーザーだったので、Win/Macの両対応です。とはいえ、Windowsでも、日本語入力切り替えは半角/全角のトグルでなく、無変換/変換キーでやっていました。なので、半角/全角キーの割り当てはありあません。

また特殊なキーとして、ESC押下時は英語入力切り替えキーも押すようにしています。これは、Vimを時々使うことがあるので、その対応です。Vimモード切替時は日本語入力を残していてもしようがないので。この設定は、Vim単体でもできるのですが、それを知ったときはすでにキーボードの設定していたため、ずっと残しています。

レイヤーは3つ使用しています。

44キーしかないので、通常レイヤーは文字だけで埋まってしまいます。なので数字とファンクションキー用のレイヤーがあります。最初は数字に2つのキー同時押しって面倒だと思ったのですが、慣れればそれほどでもありません。むしろ、今では数字入力のために上段キーまで指を持っていくのが苦痛に感じます。

また矢印キーを、hjklのキーで打ちたいので、カーソル移動メインのレイヤーも作ってあります。

まとめ

以上、プログラマブルキーボード、オススメというのと私のキーマップをサラしてみた記事でした。

少しでも参考になれば幸いです。