はじめに
こんにちは、Ctrlです。
今回はCTRL配列を左手から右手に移行した後も、左手専用のキーボードが多数あったため、時々でも使えるよう調整したキーボードレイアウトです。
左手の場合はWorm EatenのPro Microに接続し、右手の場合はte96から切り離したPro Micro付き基盤を経由して、TRRSケーブルで接続することで、左右の切り替えを行うようにしました。
現在はKensingtonのスリムブレード、Worm Eaten、ディスプレイの下に無指向性マイクを設置して作業しています。
片手キーボードの設計思想
-
15キー構成の限界への挑戦
- 物理的なキー数を絞り、部品の費用を削減しながら、基本操作を網羅できる構成が目標
-
ホームポジションから動かない
- 指を移動させずに済む配置により、作業時のストレスや疲労を軽減
-
左右対称/反転運用の両立
- 左手・右手どちらでも同じ操作感を実現し、状況に応じて使い分けが可能
-
用途別のレイヤー設計
- 入力・編集・マウス操作を押しやすさ重視でレイヤーに分離し、必要時に素早く切り替え
-
誤操作の抑制
- 不要なキーを排除し、無意識の誤爆を防ぐためのコンパクトな配置を採用
-
マウスとの一体化
- トラックボール操作やマウスクリックを含めて、片手での完結を前提
-
ショートカットの集中化
- 操作に必要なコマンドはすべてキーボードに割り当て、ドラッグをマウスに頼らない作業が可能
-
姿勢と環境への適応
- 座椅子や布団など、柔軟な作業環境でも使えるよう設計されており、机が不要
-
切り替えの簡易性
- TRRSケーブルやPro Microの差し替えにより、物理的に素早く左右を切り替え可能
-
記憶より感覚
- キー配置を覚えるのではなく、感覚で自然に手が動くことを重視した配列
主な変更点
CTRL配列:2.0以降の主な変更点
### ● 2.0(タイピング便振化とQMK改良)
* タップダンスによる大文字入力を導入(TD_LTR)
* Vimiumとの使用を意識したキー割り当てへと変更
* ショートカット操作(コピー・ペースト・検索など)をキーボードに集積
* QMKファームウェア側で独自キーコード(VOICE, PRNなど)を設定
https://qiita.com/hiyoctrl/items/11ea9d917f2716408944
---
### ● 3.0(トラックボール使用とマウス操作拡張)
* Kensington SlimBlade に合わせたカーソル操作レイヤーを追加
* マウスホイール操作(MS_WHLU / MS_WHLD)をレイヤーで実装
* マウスボタン(BTN1 / BTN2 / BTN3)を片手で操作可能に
* ESC, DEL, TABなどの「戻る系」操作の最適配置を強化
https://qiita.com/hiyoctrl/items/f19cc1c8f90177073fe2
---
### ● 片手1.0(左右反転・兼用対応レイアウト)
* 左手配列をベースに右手用レイアウトへ完全反転
* TRRSケーブルで左右の切り替えに対応した構成へ変更
* レイヤー構造は左右共通、QMKファームウェア内で反転実装
* Vimiumとのショートカット広和を抑えつつ、マウス/ブラウザ操作を統合
* キーボードを右手でも安定して操作できるよう、体動の自由度を向上
https://qiita.com/hiyoctrl/items/788995d2333bbd2f8406
- 右手と左手のレイアウトを反転して使いやすく調整
- Vimiumによる操作のずれの解消と最適化
- Vimiumによるブラウザ操作の一部をキーボードショートカットに統一
- 左手の場合はWorm EatenのPro Microに接続
- 右手の場合はPro Micro付き基盤とTRRSケーブルで接続
- ブラウザの起動などのエイリアスを調整
以上が主な変更点です。スワップハンドの導入も検討しましたが、やはり両手での入力の必要性を感じられなかったため、3年ほどで使い慣れた左手、今のところ1ヶ月程度で使いやすいと感じている右手を、それぞれ片手で使えるという方向性で進めてみました。
現在1週間程度経過しましたが、TRRSケーブルで接続している右手モードの方が、物理的に端子部分が壊れる不安も少ないため、右手モードの状態の方が多いです。
使用しているPCB:物理キーボード
使用している物理キーボードは@takashicompanyさんのWorm Eatenです。
全体のサンプルコード
keymap.c
#include QMK_KEYBOARD_H
enum layer_number {
_BASE0 = 0,_LT1,_LT2,_LT3,_LT4,_LT5,_LT6,_LT7,_LT8,_LT9,_LT10,
};
enum custom_keycodes {
VOICE = SAFE_RANGE,PRN,BRC,CBR,ABK,
};
#define SEND_PAIR(str) if (record->event.pressed) SEND_STRING(str);
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case VOICE: SEND_PAIR(SS_TAP(X_LNG1) SS_RCTL(SS_LSFT("s"))); break;
case PRN: SEND_PAIR("()"); break;
case BRC: SEND_PAIR("[]"); break;
case CBR: SEND_PAIR("<>"); break;
case ABK: SEND_PAIR("{}"); break;
}
return true;
}
enum {
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,
};
#define TD_LTR(kc) ACTION_TAP_DANCE_DOUBLE(KC_##kc, S(KC_##kc))
#define TD_ROW(a, b, c, d, e) \
[a] = TD_LTR(A), [b] = TD_LTR(B), [c] = TD_LTR(C), [d] = TD_LTR(D), [e] = TD_LTR(E)
tap_dance_action_t tap_dance_actions[] = {
[A] = TD_LTR(A), [B] = TD_LTR(B), [C] = TD_LTR(C), [D] = TD_LTR(D), [E] = TD_LTR(E),
[F] = TD_LTR(F), [G] = TD_LTR(G), [H] = TD_LTR(H), [I] = TD_LTR(I), [J] = TD_LTR(J),
[K] = TD_LTR(K), [L] = TD_LTR(L), [M] = TD_LTR(M), [N] = TD_LTR(N), [O] = TD_LTR(O),
[P] = TD_LTR(P), [Q] = TD_LTR(Q), [R] = TD_LTR(R), [S] = TD_LTR(S), [T] = TD_LTR(T),
[U] = TD_LTR(U), [V] = TD_LTR(V), [W] = TD_LTR(W), [X] = TD_LTR(X), [Y] = TD_LTR(Y),
[Z] = TD_LTR(Z),
};
#define BSPC KC_BSPC
#define ENG KC_LNG2
#define JPN KC_LNG1
#define DEL KC_DEL
#define SPC KC_SPC
#define BTN1 KC_BTN1
#define BTN2 KC_BTN2
#define BTN3 KC_BTN3
#define ESC KC_ESC
#define TAB KC_TAB
#define ENT KC_ENT
#define RGHT KC_RGHT
#define LEFT KC_LEFT
#define HOME KC_HOME
#define END KC_END
#define IBUS C(KC_F1)
#define KEYBOARD C(KC_F2)
#define MOUSE C(KC_F3)
#define CHROMIUM C(KC_F4)
#define SOUND C(KC_F6)
#define FIREFOX C(KC_F9)
#define SETTINGS C(KC_F10)
#define THUNAR C(KC_F11)
#define TERMINAL C(KC_F12)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE0] = LAYOUT(
LT(8,ESC), LT(7,KC_DOT), LT(6,KC_COMM), MS_WHLU, VOICE, VOICE, MS_WHLU, LT(6,KC_COMM), LT(7,KC_DOT), LT(8,ESC),
LT(9,BTN3), LEFT, RGHT, LT(5,BTN1), LT(4,TAB), LT(4,TAB), LT(5,BTN1), LEFT, RGHT, LT(9,BTN3),
LT(10,ENT), XXXXXXX, XXXXXXX, MS_WHLD, LT(3,SPC), LT(3,SPC), MS_WHLD, XXXXXXX, XXXXXXX, LT(10,ENT),
XXXXXXX, XXXXXXX, SH_TOGG, LT(1,DEL), LT(2,BSPC), LT(2,BSPC), LT(1,DEL), SH_TOGG, XXXXXXX, XXXXXXX),
[_LT1] = LAYOUT(
TD(L), TD(O), TD(E), TD(Y), TD(W), TD(W), TD(Y), TD(E), TD(O), TD(L),
TD(A), TD(J), TD(C), TD(I), TD(F), TD(F), TD(I), TD(C), TD(J), TD(A),
TD(Q), XXXXXXX, XXXXXXX, TD(U), TD(V), TD(V), TD(U), XXXXXXX, XXXXXXX, TD(Q),
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, JPN, JPN, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT2] = LAYOUT(
TD(X), TD(S), TD(T), TD(R), TD(P), TD(P), TD(R), TD(T), TD(S), TD(X),
TD(K), TD(Z), TD(D), TD(N), TD(H), TD(H), TD(N), TD(D), TD(Z), TD(K),
TD(G), XXXXXXX, XXXXXXX, TD(M), TD(B), TD(B), TD(M), XXXXXXX, XXXXXXX, TD(G),
XXXXXXX, XXXXXXX, XXXXXXX, ENG, XXXXXXX, XXXXXXX, ENG, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT3] = LAYOUT(
XXXXXXX, S(HOME), S(END), S(KC_UP), C(KC_A), C(KC_A), S(KC_UP), S(HOME), S(END), XXXXXXX,
RCS(KC_V), S(LEFT), S(RGHT), C(KC_C), C(KC_X), C(KC_X), C(KC_C), S(LEFT), S(RGHT), RCS(KC_V),
C(KC_V), XXXXXXX, XXXXXXX, S(KC_DOWN), XXXXXXX, XXXXXXX, S(KC_DOWN), XXXXXXX, XXXXXXX, C(KC_V),
XXXXXXX, XXXXXXX, XXXXXXX, S(DEL), A(KC_F4), A(KC_F4), S(DEL), XXXXXXX, XXXXXXX, XXXXXXX),
[_LT4] = LAYOUT(
IBUS, KEYBOARD, MOUSE, CHROMIUM, C(KC_F5), C(KC_F5), CHROMIUM, MOUSE, KEYBOARD, IBUS,
SOUND, C(KC_F7), C(KC_F8), QK_BOOT, XXXXXXX, XXXXXXX, QK_BOOT, C(KC_F8), C(KC_F7), SOUND,
XXXXXXX, XXXXXXX, XXXXXXX, FIREFOX, SETTINGS, SETTINGS, FIREFOX, XXXXXXX, XXXXXXX, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, THUNAR, TERMINAL, TERMINAL, THUNAR, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT5] = LAYOUT(
KC_HASH, KC_COLN, KC_SLSH, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, KC_SLSH, KC_COLN, KC_HASH,
C(KC_T), KC_EXLM, KC_QUES, XXXXXXX, BTN2, BTN2, XXXXXXX, KC_QUES, KC_EXLM, C(KC_T),
KC_UNDS, XXXXXXX, XXXXXXX, C(KC_Y), C(KC_Z), C(KC_Z), C(KC_Y), XXXXXXX, XXXXXXX, KC_UNDS,
XXXXXXX, XXXXXXX, XXXXXXX, RCS(KC_T), C(KC_W), C(KC_W), RCS(KC_T), XXXXXXX, XXXXXXX, XXXXXXX),
[_LT6] = LAYOUT(
XXXXXXX, RCS(KC_C), XXXXXXX, C(HOME), XXXXXXX, XXXXXXX, C(HOME), XXXXXXX, RCS(KC_C), XXXXXXX,
A(KC_S), XXXXXXX, XXXXXXX, A(TAB), XXXXXXX, XXXXXXX, A(TAB), XXXXXXX, XXXXXXX, A(KC_S),
XXXXXXX, XXXXXXX, XXXXXXX, C(END), C(KC_H), C(KC_H), C(END), XXXXXXX, XXXXXXX, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, C(KC_F), C(KC_F), XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT7] = LAYOUT(
XXXXXXX, XXXXXXX, PRN, ABK, XXXXXXX, XXXXXXX, ABK, PRN, XXXXXXX, XXXXXXX,
CBR, XXXXXXX, XXXXXXX, BRC, XXXXXXX, XXXXXXX, BRC, XXXXXXX, XXXXXXX, CBR,
XXXXXXX, XXXXXXX, XXXXXXX, KC_PLUS, KC_MINS, KC_MINS, KC_PLUS, XXXXXXX, XXXXXXX, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, KC_ASTR, KC_EQL, KC_EQL, KC_ASTR, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT8] = LAYOUT(
XXXXXXX, KC_AMPR, KC_PERC, KC_TILD, XXXXXXX, XXXXXXX, KC_TILD, KC_PERC, KC_AMPR, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, KC_AT, XXXXXXX, XXXXXXX, KC_AT, XXXXXXX, XXXXXXX, XXXXXXX,
KC_CAPS, XXXXXXX, XXXXXXX, KC_CIRC, KC_GRV, KC_GRV, KC_CIRC, XXXXXXX, XXXXXXX, KC_CAPS,
XXXXXXX, XXXXXXX, XXXXXXX, KC_DQUO, KC_QUOT, KC_QUOT, KC_DQUO, XXXXXXX, XXXXXXX, XXXXXXX),
[_LT9] = LAYOUT(
XXXXXXX, KC_SCLN, KC_BSLS, KC_F11, KC_F5, KC_F5, KC_F11, KC_BSLS, KC_SCLN, XXXXXXX,
XXXXXXX, KC_PIPE, KC_DLR, KC_F7, KC_F10, KC_F10, KC_F7, KC_DLR, KC_PIPE, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, RCS(TAB), C(TAB), RCS(TAB), C(TAB), XXXXXXX, XXXXXXX, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, RCS(KC_PGUP), RCS(KC_PGDN), RCS(KC_PGUP), RCS(KC_PGDN), XXXXXXX, XXXXXXX, XXXXXXX),
[_LT10] = LAYOUT(
XXXXXXX, KC_4, KC_3, KC_2, KC_1, KC_1, KC_2, KC_3, KC_4, XXXXXXX,
XXXXXXX, KC_8, KC_7, KC_6, KC_5, KC_5, KC_6, KC_7, KC_8, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, KC_0, KC_9, KC_9, KC_0, XXXXXXX, XXXXXXX, XXXXXXX,
XXXXXXX, XXXXXXX, XXXXXXX, A(LEFT), A(RGHT), A(LEFT), A(RGHT), XXXXXXX, XXXXXXX, XXXXXXX),
};
以上が今回の全体のサンプルコードになります。
※この keymap.c は、QMKファームウェアで動作する15キー仕様のカスタムレイアウトをベースにしています。左右両用にするため、各レイヤーに対してキー配置を反転・最適化しています。
Vimiumの設定
unmapAll
# map <c-S> goBack
# map <space> goNext
# map <tab> LinkHints.activateModeToOpenInNewForegroundTab
# map <delete> restoreTab
# map <backspace> removeTab
map <c-home> scrollToTop
map <c-end> scrollToBottom
map <c-s> copyCurrentUrl
# map , previousTab
# map . nextTab
mapkey 0 0
mapkey 1 0
mapkey 2 0
mapkey 3 0
mapkey 4 0
mapkey 5 0
mapkey 6 0
mapkey 7 0
mapkey 8 0
mapkey 9 0
Vimiumについては、コメントアウトした項目が非常に使いやすかったものの、押しやすさが逆に誤爆の原因になったので、一時的に外しました。
Vimiumでしか実現できなかったのが、Googleドキュメント利用時のトップとボトムの移動、見ているページのURLのコピーのみだったため、この2つは残しました。
また、タブの移動が左右反転させると逆になってしまうこともあり、調整がうまくいけば再度元に戻したいとも考えています。
ただし、キーボードショートカットとマウス操作で全て移動できる状態になっているため、スリム化や重複を避けることを考えると現在の状態がベストです。
まとめ
今回は左手を使うかどうかは分からないものの、物理的に左手でしか使えないキーボードがたくさんあることが気になったために作ったキーボードレイアウトと言えます。
基本的には右手での作業の方が快適なため、もしかしたら左手のみのレイアウトに戻すかもしれません。今回の気づきとしては、片手キーボードではあるものの、操作する右手のキーボードはマスターとしてではなく、スレーブとして扱うことで、いつでも安心・安全に使えるようになった点です。
TRRSケーブルによる接続はとても快適であり、端子の部分が壊れたとしても、取り替えるのが難しい場所でないため、交換やメンテナンスにも向いている点も安心感があります。
また、左右反転に対応したものを含めて、右手として使える物理的なキーボードも合計で5つ、te96から切り離したPro Micro付き基盤は合計で4つあるため、全てが同時に壊れることは考えにくいことから、現在現在の環境が失われることはないでしょう。
左手用のキーボードも多数あることを考えると、もしもの時?に備えるとすれば、スリムブレードの買い増しか、左右どちらでも使えるトラックボールの購入を検討するかなとは思っております。トラックボールでおすすめがあったら教えてください。
最後までお読みいただきありがとうございました。
キーボードレイアウトやキーボードショートカットなど、何か面白い取り組みがあったら教えてくれると嬉しいです。
ライティング案件や自作キーボード関連のお仕事などありましたら、お気軽にご相談ください!