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文とかも普通に書ける。
なので、いい感じに条件分岐を書けば、特定のキーを押したときだけ違う動作をさせることも可能。
というのを、以下の記事を参考に書きました。うつぼさんありがとうございます。リンクを貼っておくので、そちらの記事もご一読ください。
以下は、シフトキーを押していて、かつ、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処理を実行
}
この辺はぶっちゃけよくわからない。
参考にした記事そのままなので、元の記事をご参照ください。
これで、変換・無変換キーの使える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処理を実行
}