୧ʕ •ᴥ•ʔ୨ Silent Lake

Home Row Mods

Published: | Updated: | 1000 words | 5 mins

什么是 home row?

Home row 是键盘上中间的那一排按键。以常见的 QWERTY 键盘为例,home row 就是 ASDFGHJKL; 这一排按键。在 FJ 键上还有定位用的横杠,方便盲打时手指快速移动到相应的位置。

什么是 mods?

Mods 全称是 Modifiers,指的是键盘上的修饰键,如 ⌃ Ctrl⎇ Alt⇧ Shift⊞ Win⌘ Command 等。

什么是 home row mods?

通常来说,Mods 按键都位于键盘的左下角和右下角,处在 home row 上的手不得不移动到边上才能够到这些按键。而 home row mods 则是借助于 QMKMod-tap 功能,来让 home row 按键作为两个按键来使用,点按(tap)时则是键帽上印着的按键,按住(hold)时则是映射过去的 Mods 按键。

常见的 home row mods 映射

这里以 QWERTY 键盘为例,介绍一些常见的 home row mods 映射。

首先约定一些符号:

Full Modifier NameAbbreviationSymbol
ShiftS
ControlC
Alt/OptionA
GUI/Win/CommandG
No modifier/mod-tap__

接下来就是常见的映射:

  • SCGA: __
  • GASC: __
  • GACS: __
  • CAGS: __

GASC 为例,这是在 QWETRY home row 上的映射关系:

Home rowASDFGHJKL;
TapASDFGHJKL;
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 rowZXCVBNM,.
TapZXCVBNM,.
Hold+++VBN+++

Note

如果想了解更多,可以阅读 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.kbdhome-row-mod-advanced.kbd

我之前也写过一篇使用 kanata 将 CapsLock 映射成 CtrlEsc文章,可以作为 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+Letter 来代替,因为我的 CapsLock 已经是 dual-role key 了。

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 个参数中的按键在当前动作发生时被按下,提前触发一次点按,减少同手滚动输入的误差。

比如你想连按 kl 来输入 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-keysright-hand-keys 变量分别是左手和右手的按键,用作 tap-hold-release-keys 的第 5 个参数。

至于 tap-timeouthold-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 层,然后用 123 键切换到不同的层。

完整配置

至此,我的配置就完成了。完整的配置可以看这里

#keyboard   #kanata   #tips  

Reply to this post by email ↪