【QMK】変換・無変換キーの使えるUS配列を作る

自作キーボード

JIS vs US

筆者はenterとbackspaceが近いという理由でUS配列が好き。

しかし、JIS配列も捨てがたい。その理由が変換・無変換キーがあること。

最近のWindowsではこの2つのキーをIMEオン・オフ、即ちかな入力と英数入力の切替に利用できる。

これに慣れてしまうと、全角/半角キーやShift+CapsLockには戻れない。

それならUS配列に変換・無変換キーが付いていれば最強では???と思ったので、作ってみました。

レイアウト

以下がQMKのkeymap.cのレイアウト。

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [0] = LAYOUT(
        KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BACKSPACE,
        MO(2), KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SEMICOLON, KC_ENTER,
        KC_LEFT_SHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, KC_SLASH, KC_INT3,
        KC_LEFT_CTRL, MO(3), KC_LEFT_ALT, KC_INT5, KC_SPACE, MO(1), KC_INT4, KC_LEFT_GUI, KC_DELETE, MO(2)
    ),
    [1] = LAYOUT(
        KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINUS, KC_EQUAL,
        KC_TRNS, KC_LEFT_BRACKET, KC_RIGHT_BRACKET, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, KC_LEFT_BRACKET, KC_QUOTE,
        KC_LEFT_SHIFT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_ALT, KC_F7, KC_ESCAPE, KC_TRNS, KC_ESCAPE, KC_HOME, KC_END, KC_TRNS
    ),
    [2] = LAYOUT(
        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_TRNS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, KC_SEMICOLON, KC_ENTER,
        KC_LEFT_SHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, KC_SLASH, KC_BACKSLASH,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_GUI, KC_LEFT_ALT, KC_SPACE, KC_TRNS, KC_ESCAPE, KC_HOME, KC_END, KC_TRNS
    ),
    [3] = LAYOUT(
        KC_CAPS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_P7, KC_P8, KC_P9, KC_EQUAL,
        KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_P4, KC_P5, KC_P6, KC_ENTER,
        KC_LEFT_SHIFT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_P1, KC_P2, KC_P3, KC_TAB,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_GUI, KC_LEFT_ALT, KC_SPACE, KC_TRNS, KC_P0, KC_HOME, KC_END, KC_TRNS
    )
};

USだろうと、JISだろうと、キーボード側は同じキーコードを送るので、レイアウト自体はUSと大差ない。

ただし、US配列にないキーも一部あり、それらはKC_INTnの形式で書くことができる。

KC_INT3:とenterの間のキー
KC_INT4変換
KC_INT5無変換

KCではじまるキーコードをJIS配列の名前に変換するライブラリもあるようだが、送るキーコードは一緒なので、使わなくてもJIS配列のキーマップを書くことは可能。

これでJISキーボードとしては問題なく動作するはずだ。

記号をUSに置換

keymap.cは.cとある通りC言語なので、if文とかも普通に書ける。

なので、いい感じに条件分岐を書けば、特定のキーを押したときだけ違う動作をさせることも可能。

というのを、以下の記事を参考に書きました。うつぼさんありがとうございます。リンクを貼っておくので、そちらの記事もご一読ください。

SHIFT+JP_XXXをUS配列に対応させる – blog-dog

以下は、シフトキーを押していて、かつ、switch case文に一致するキーコードがある場合、別のキーコードに置き換えて送信するというコード。

USではシフト同時押しでも、JISでは単押しのキーがあるため、if文でシフトを解除したりしなかったりしている。

// ------前略----------(全文は記事末尾に記載)
static bool process_jp_symbols_impl(uint16_t keycode, bool pressed) {
    if (!pressed) {
        return true;
    }
    uint8_t shift = get_mods() & (MOD_BIT(KC_LEFT_SHIFT) | MOD_BIT(KC_RIGHT_SHIFT));
    if (!shift) {
// ------中略----------
    }
    else
    {
        //シフト押下時
        bool shift_need = true;
        uint16_t s;
        switch (keycode) {
            case KC_2:    s = KC_LEFT_BRACKET; shift_need = false; break; // @
            case KC_6:    s = KC_EQUAL; shift_need = false; break; // ^
            case KC_7:    s = KC_6; shift_need = true; break; // `
            case KC_8:    s = KC_QUOTE; shift_need = true; break; // *
            case KC_9:    s = KC_8; shift_need = true; break; // (
            case KC_0:    s = KC_9; shift_need = true; break; // )
            case KC_QUOTE: s = KC_2; shift_need = true; break; // "
            case KC_MINUS: s = KC_INT1; shift_need = true; break; // _
            case KC_EQUAL: s = KC_SEMICOLON; shift_need = true; break; // +
            case KC_SEMICOLON: s = KC_QUOTE; shift_need = false; break; // :
            default: return true;
        }

        if(!shift_need) // 単押し
        {
            unregister_mods(shift);  // シフトキーを一時解除
            tap_code16(s);     // 修正後のキーを送信
            register_mods(shift);  // シフトキーを復元
        }
        else{ // シフト同時押し(シフト押下時の場合なので追加でシフトを押す必要はない)
            tap_code16(s);     // 修正後のキーを送信
        }
        
        return false; // 元のキーコードを送信しない
    }
    
}
// ------後略----------

同様にシフト同時押しでないキーも処理を書いている。

変換・無変換キーはwindows、macのどちらでも使えるように書いているコードなので、この記事の趣旨とは関係がないコード。

// ------前略----------
if (!shift) {
        // シフト押してない時
        bool shift_need = true;
        uint16_t s;
        switch (keycode) {
            case KC_EQUAL: s = KC_MINUS; shift_need = true; break; // =
            case KC_QUOTE: s = KC_7; shift_need = true; break; // '
            case KC_LEFT_BRACKET: s = KC_RBRC; shift_need = false; break; // [
            case KC_RIGHT_BRACKET: s = KC_NUHS; shift_need = false; break; // ]

            // 変換・無変換キー
            case KC_INT4: s = KC_LNG1; shift_need = false; break;
            case KC_INT5: s = KC_LNG2; shift_need = false; break;

            default: return true;
        }

        if(s == KC_LNG1 || s == KC_LNG2) // macの英数・かな
        {
            tap_code16(s);
            return true; // 元のキーコードも送信 win macのいずれかでしか認識しないキーコードなので2個送っても問題ない
        }
        else if(shift_need) // シフトキー同時押し
        {
            tap_code16(S(s)); //変換後のキーコードをシフトと同時押し
            return false; // 元のキーコードを送信しない
        }
        else // 単押し
        {
            tap_code16(s);     // 変換後のキーコード送信
            return false;
        }
    }
// ------後略----------

あとは、下の方に以下のコードを書いておけば、キーコードの置換が可能だ。

bool process_jp_symbols(uint16_t keycode, keyrecord_t *record) {
    return process_jp_symbols_impl(keycode, record->event.pressed);
}

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (!process_jp_symbols(keycode, record)) {
        return false;  // 記号置換を行った場合、元のキーコードを処理させない
    }
    return true;  // それ以外は通常のQMK処理を実行
}

この辺はぶっちゃけよくわからない。

参考にした記事そのままなので、元の記事をご参照ください。

SHIFT+JP_XXXをUS配列に対応させる – blog-dog

これで、変換・無変換キーの使えるUS配列が完成。

なお、OSはJIS配列の設定にしておかないと意味不明な置換になるので注意。

あとがき

少し面倒だったが、理想的なキー配列が作れたので、満足。

やはり変換・無変換キーをIMEオン、オフに割り当てると便利。もうこれwindows標準にしろよ……

あと、副次的なメリットだが、配列はUSでも、OSの設定はJISなので、JISキーボードのノートパソコンで使う時にも便利だった。

keymap.c

// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [0] = LAYOUT(
        KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BACKSPACE,
        MO(2), KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SEMICOLON, KC_ENTER,
        KC_LEFT_SHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, KC_SLASH, KC_INT3,
        KC_LEFT_CTRL, MO(3), KC_LEFT_ALT, KC_INT5, KC_SPACE, MO(1), KC_INT4, KC_LEFT_GUI, KC_DELETE, MO(2)
    ),
    [1] = LAYOUT(
        KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINUS, KC_EQUAL,
        KC_TRNS, KC_LEFT_BRACKET, KC_RIGHT_BRACKET, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, KC_LEFT_BRACKET, KC_QUOTE,
        KC_LEFT_SHIFT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_ALT, KC_F7, KC_ESCAPE, KC_TRNS, KC_ESCAPE, KC_HOME, KC_END, KC_TRNS
    ),
    [2] = LAYOUT(
        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_TRNS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, KC_SEMICOLON, KC_ENTER,
        KC_LEFT_SHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, KC_SLASH, KC_BACKSLASH,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_GUI, KC_LEFT_ALT, KC_SPACE, KC_TRNS, KC_ESCAPE, KC_HOME, KC_END, KC_TRNS
    ),
    [3] = LAYOUT(
        KC_CAPS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_P7, KC_P8, KC_P9, KC_EQUAL,
        KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_P4, KC_P5, KC_P6, KC_ENTER,
        KC_LEFT_SHIFT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_P1, KC_P2, KC_P3, KC_TAB,
        KC_LEFT_CTRL, KC_TRNS, KC_LEFT_GUI, KC_LEFT_ALT, KC_SPACE, KC_TRNS, KC_P0, KC_HOME, KC_END, KC_TRNS
    )
};

static bool process_jp_symbols_impl(uint16_t keycode, bool pressed) {
    if (!pressed) {
        return true;
    }
    uint8_t shift = get_mods() & (MOD_BIT(KC_LEFT_SHIFT) | MOD_BIT(KC_RIGHT_SHIFT));
    if (!shift) {
        // シフト押してない時
        bool shift_need = true;
        uint16_t s;
        switch (keycode) {
            case KC_EQUAL: s = KC_MINUS; shift_need = true; break; // =
            case KC_QUOTE: s = KC_7; shift_need = true; break; // '
            case KC_LEFT_BRACKET: s = KC_RBRC; shift_need = false; break; // [
            case KC_RIGHT_BRACKET: s = KC_NUHS; shift_need = false; break; // ]

            // 変換・無変換キー
            case KC_INT4: s = KC_LNG1; shift_need = false; break;
            case KC_INT5: s = KC_LNG2; shift_need = false; break;

            default: return true;
        }

        if(s == KC_LNG1 || s == KC_LNG2) // macの英数・かな
        {
            tap_code16(s);
            return true; // 元のキーコードも送信 win macのいずれかでしか認識しないキーコードなので2個送っても問題ない
        }
        else if(shift_need) // シフトキー同時押し
        {
            tap_code16(S(s)); //変換後のキーコードをシフトと同時押し
            return false; // 元のキーコードを送信しない
        }
        else // 単押し
        {
            tap_code16(s);     // 変換後のキーコード送信
            return false;
        }
    }
    else
    {
        //シフト押下時
        bool shift_need = true;
        uint16_t s;
        switch (keycode) {
            case KC_2:    s = KC_LEFT_BRACKET; shift_need = false; break; // @
            case KC_6:    s = KC_EQUAL; shift_need = false; break; // ^
            case KC_7:    s = KC_6; shift_need = true; break; // `
            case KC_8:    s = KC_QUOTE; shift_need = true; break; // *
            case KC_9:    s = KC_8; shift_need = true; break; // (
            case KC_0:    s = KC_9; shift_need = true; break; // )
            case KC_QUOTE: s = KC_2; shift_need = true; break; // "
            case KC_MINUS: s = KC_INT1; shift_need = true; break; // _
            case KC_EQUAL: s = KC_SEMICOLON; shift_need = true; break; // +
            case KC_SEMICOLON: s = KC_QUOTE; shift_need = false; break; // :
            default: return true;
        }

        if(!shift_need) // 単押し
        {
            unregister_mods(shift);  // シフトキーを一時解除
            tap_code16(s);     // 修正後のキーを送信
            register_mods(shift);  // シフトキーを復元
        }
        else{ // シフト同時押し(シフト押下時の場合なので追加でシフトを押す必要はない)
            tap_code16(s);     // 修正後のキーを送信
        }
        
        return false; // 元のキーコードを送信しない
    }
    
}

bool process_jp_symbols(uint16_t keycode, keyrecord_t *record) {
    return process_jp_symbols_impl(keycode, record->event.pressed);
}

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    if (!process_jp_symbols(keycode, record)) {
        return false;  // 記号置換を行った場合、元のキーコードを処理させない
    }
    return true;  // それ以外は通常のQMK処理を実行
}

タイトルとURLをコピーしました