什么是 home row?
Home row 是键盘上中间的那一排按键。以常见的 QWERTY 键盘为例,home row 就是 ASDFGHJKL; 这一排按键。在 F 和 J 键上还有定位用的横杠,方便盲打时手指快速移动到相应的位置。
什么是 mods?
Mods 全称是 Modifiers,指的是键盘上的修饰键,如 ⌃ Ctrl、⎇ Alt、⇧ Shift、⊞ Win、⌘ Command 等。
什么是 home row mods?
通常来说,Mods 按键都位于键盘的左下角和右下角,处在 home row 上的手不得不移动到边上才能够到这些按键。而 home row mods 则是借助于 QMK 的 Mod-tap 功能,来让 home row 按键作为两个按键来使用,点按(tap)时则是键帽上印着的按键,按住(hold)时则是映射过去的 Mods 按键。
常见的 home row mods 映射
这里以 QWERTY 键盘为例,介绍一些常见的 home row mods 映射。
首先约定一些符号:
Full Modifier Name | Abbreviation | Symbol |
---|---|---|
Shift | S | ⇧ |
Control | C | ⎈ |
Alt/Option | A | ⎇ |
GUI/Win/Command | G | ◆ |
No modifier/mod-tap | _ | _ |
接下来就是常见的映射:
- SCGA: ⇧⎈◆⎇__⎇◆⎈⇧
- GASC: ◆⎇⇧⎈__⎈⇧⎇◆
- GACS: ◆⎇⎈⇧__⇧⎈⎇◆
- CAGS: ⎈⎇◆⇧__⇧⎇⎈◆
以 GASC 为例,这是在 QWETRY home row 上的映射关系:
Home row | A | S | D | F | G | H | J | K | L | ; |
---|---|---|---|---|---|---|---|---|---|---|
Tap | A | S | D | F | G | H | J | K | L | ; |
Hold | ◆ | ⎇ | ⇧ | ⎈ | _ | _ | ⎈ | ⇧ | ⎇ | ◆ |
上述映射的优缺点
- SCGA: 这是直接将 Mods 按键在键盘上的顺序,映射到 home row 的结果。忽略了 Mods 按键的使用频率,比如将 ⎇ 和 ◆ 放在了最方便按到的食指和中指的位置,但是很明显这比 ⇧ 和 ⎈ 使用频率要低得多。
- GASC: 这种布局将 ⎈ 和 ⇧ 放在了食指和中指的位置,将使用频率更高的按键放在更灵活的手指上,更加合理和舒适。而且将 ⇧ 放在中指上,食指就可以很方便地去按其他按键从而输入大写字母。缺点就是 ◆ 在尾指位置上,在 macOS 上就没那么方便按。
- GACS: 这种布局则是将使用频率最高的修饰键放在最灵活的手指下,使用频率最低的放在最弱的手指处。同时它避免了 GASC 布局无法单手 Ctrl+Letter 的尴尬局面,比如 Ctrl+C。
- CAGS: 这种布局则是在 macOS 上更加合理和方便。
另外这些 home row key 就不能长按了,因为长按时会被 Mods 按键覆盖掉。这在玩游戏时会造成不便,所以建议定义一个 game mode layer 来禁用 home row mods。
组合修饰键
还有一种用法就是在 lower row 上,即 home row 下一排的按键,将一些常用组合修饰键映射上去。比如 Ctrl+Shift 映射到 C 上。
这里举一个例子:
Lower row | Z | X | C | V | B | N | M | , | . |
---|---|---|---|---|---|---|---|---|---|
Tap | Z | X | C | V | B | N | M | , | . |
Hold | ◆+⎇ | ⎇+⎈ | ⎈+⇧ | V | B | N | ⇧+⎈ | ⎈+⎇ | ⎇+◆ |
如果想了解更多,可以阅读 A guide to home row mods,里面有更多详细说明和 tips。不过涉及了很多 QMK 相关的内容,如果不打算使用 QMK 的话,这部分可以略过。
Kanata 实现
我选择了 kanata 而不是 A guide to home row mods 里提及的 KMonad 作为实现 home row mods 的工具。不过 kanata 是受 KMonad 启发而用 Rust 写的,功能上差不多。
Kanata 仓库里就有两份 home row mods 的示例配置,分别是 home-row-mod-basic.kbd 和 home-row-mod-advanced.kbd。
我之前也写过一篇使用 kanata 将 CapsLock 映射成 Ctrl 和 Esc 的文章,可以作为 kanata 的简单介绍。
也可以使用 kanata online simulator 来测试配置是否正确。
下面是我目前使用的 kanata 配置:
1(defsrc
2 esc 1 2 3 4 5 6 7 8 9 0 - = bspc
3 tab q w e r t y u i o p [ ] \
4 caps a s d f g h j k l ; ' ret
5 lsft z x c v b n m , . / rsft
6 lctl lmet lalt spc ralt rctl
7)
8
9(deflayer qwerty
10 @eca 1 2 3 4 5 6 7 8 9 0 - = bspc
11 tab @q` w e r t y u i o p [ ] \
12 @cap a s d f g h j k l ; ' ret
13 lsft z x c v b n m , . / rsft
14 @cap lmet lalt spc ralt rctl
15)
16
17(defalias
18 cap (tap-hold-press 200 200 esc lctl)
19 ;; 1 tap : "Escape"
20 ;; 2 tap : "Caps Lock"
21 eca (tap-dance 200 (esc caps))
22 ;; 1 tap : "q"
23 ;; 2 tap : "`"
24 q` (tap-dance 200 (q grave))
25)
我打算采用 GASC 布局。至于无法单手 Ctrl+Letter 的问题,我可以通过 CapsLock+
defalias
首先需要定义一些 tap-hold
别名来描述 Dual role keys,这里可以参考 kanata 示例配置里的 home-row-mod-advanced.kbd:
1(defalias
2 ;; home row mods -- GASC
3 met_a (tap-hold-release-keys $tt $ht (multi a @tap) lmet $left-hand-keys)
4 alt_s (tap-hold-release-keys $tt $ht (multi s @tap) lalt $left-hand-keys)
5 sft_d (tap-hold-release-keys $tt $ht (multi d @tap) lsft $left-hand-keys)
6 ctl_f (tap-hold-release-keys $tt $ht (multi f @tap) lctl $left-hand-keys)
7
8 ctl_j (tap-hold-release-keys $tt $ht (multi j @tap) rctl $right-hand-keys)
9 sft_k (tap-hold-release-keys $tt $ht (multi k @tap) rsft $right-hand-keys)
10 alt_l (tap-hold-release-keys $tt $ht (multi l @tap) lalt $right-hand-keys)
11 met_; (tap-hold-release-keys $tt $ht (multi ; @tap) rmet $right-hand-keys)
12)
在上面的 alias 里用到的 action 是 tap-hold-release-keys
,它的作用是当第 5 个参数中的按键在当前动作发生时被按下,提前触发一次点按,减少同手滚动输入的误差。
比如你想连按 k 和 l 来输入 kl
,使用 tap-hold-release-keys
就可以减少输入成 Shiftl 的可能性。
然后再定义一些变量:
1(defvar
2 tap-timeout 50
3 hold-timeout 200
4 tt $tap-timeout
5 ht $hold-timeout
6
7 left-hand-keys (
8 q w e r t
9 a s d f g
10 z x c v b
11 )
12 right-hand-keys (
13 y u i o p
14 h j k l ;
15 n m , . /
16 )
17)
其中 left-hand-keys
和 right-hand-keys
变量分别是左手和右手的按键,用作 tap-hold-release-keys
的第 5 个参数。
至于 tap-timeout
和 hold-timeout
则可以根据自己的按键习惯来进行设置。
1(defalias
2 tap (multi
3 @nomods
4 (on-idle-fakekey to-base tap 20)
5 )
6)
tap
则是一个虚拟按键,用于在 home row 按键点按触发时临时切换至 base layer,以防止误触。具体定义如下:
1(deffakekeys
2 to-base (layer-switch qwerty)
3)
deflayer
接着定义 qwerty
层:
1(deflayer qwerty
2 esc 1 2 3 4 5 6 7 8 9 0 - = bspc
3 tab q w e r t y u i o p [ ] @bsl
4 @cap @met_a @alt_s @sft_d @ctl_f g h @ctl_j @sft_k @alt_l @met_; ' ret
5 lsft z x c v b n m , . / rsft
6 lctl lmet lalt spc ralt rctl
7)
以及 nomods-layer
:
1(deflayer nomods-layer
2 esc 1 2 3 4 5 6 7 8 9 0 - = bspc
3 tab q w e r t y u i o p [ ] @bsl
4 @cap a s d f g h j k l ; ' ret
5 lsft z x c v b n m , . / rsft
6 lctl lmet lalt spc ralt rctl
7)
我还定义了一个 layers
层,用于切换到其他层,并把它映射到 \ 上:
1(deflayermap (layers)
2 1 @base
3 2 @nomods
4 3 lrld
5)
6
7(defalias
8 ;; toggle layer aliases
9 lay (layer-toggle layers)
10 bsl (tap-hold-release $tt $ht \ @lay)
11 ;; change the base layer between base and nomods-layer
12 base (layer-switch qwerty)
13 nomods (layer-switch nomods-layer)
14)
通过按住 \ 键,可以切换到 layers
层,然后用 1、2、3 键切换到不同的层。
完整配置
至此,我的配置就完成了。完整的配置可以看这里。