Home row mods - a new approach
I have revamped the way I have implemented homerow mods (HRM). The base functionality is still triggered as a secondary function, but out of a macro (via ifSecondary
), and I added a timer that disables the mods during fast typing streaks.
Unfortunately, this requires a separate macro for each home row key, plus a few more. On the other hand, it again shows the power of UHK macros
On the positive side, I no longer get spurious activations during fast typing.
To keep it modular, I have moved the home row keys onto a separate keymap (HRM
) which is used to overlay my regular keymap.
The base layer contains regular key mappings without mod functionality.
The mod layer maps home-row-mod macros onto each of the home row keys.
My home row mappings are:
a
- left alt
s
- left gui
d
- left shift
f
- left control
g
- left gui
h
- right gui
j
- right control
k
- right shift
l
- right gui
;:
- left alt
\|
(ISO) - right alt
/?
- right alt
Note that I made sure both Alt and AltGr are available on both hands.
Each of the homerow keys now has a macro like this:
hrm-LS-d:
if ($hrm_active > 0) ifSecondary final holdKey leftShift
setVar hrm_tick 0
holdKey d
Adapt holdKey leftShift
and holdKey d
for each of the keys.
There are two new concepts here:
- The secondary function is only checked if
$hrm_active
is set. You can turn home row mod functionality on and off by toggling that variable (0 = hrm off, 1 = hrm on). - A tick counter is reset each time the primary function is triggered (normal typing).
In addition, a timer macro in the background keeps incrementing the tick counter. Depending on how many ticks have passed, it will change the hrm_active state. Basically, when the tick is reset by a primary key activation, hrms are turned off for a certain interval (configurable, I use 40ms). As long as more primary keys are typed during that time, the interval gets extended. Once there are no primary keys for more than the interval, hrms are turned on again. This will protect the hrms from spontaneous incorrect activation during typing streaks.
hrm-timer:
setVar hrm_timer_active 1
setVar hrm_active_state $hrm_active
setLedTxt 800 '+T+'
// if tick is < tick_active, hrm should be inactive
// if tick is >= tick_active, hrm should be active
// primary key activations will reset tick => fast typing streak keeps hrm inactive
loop:
setVar hrm_tick ($hrm_tick + 1)
if ($hrm_active > 0) {
if ($hrm_tick < $hrm_tick_active) setVar hrm_active 0
}
else {
if ($hrm_tick >= $hrm_tick_active) setVar hrm_active 1
}
// the next section is just about showing state changes on LED display
if ($hrm_active_state == 0) {
if ($hrm_active > 0) {
setLedTxt 0 'H++'
setVar hrm_active_state 1
}
}
else {
if ($hrm_active == 0) {
setLedTxt 0 'H--'
setVar hrm_active_state 0
}
}
// --- ^^^ ---
delayUntil $hrm_tick_delay
if ($hrm_timer_active > 0) goTo loop
setLedTxt 800 '-T-'
Finally, some macros to initialise the system.
hrm-on:
setVar hrm_timer_active 0
setVar hrm_active 1
overlayLayer base HRM mod
setLedTxt 666 'H++'
hrm-off:
setVar hrm_timer_active 0
setVar hrm_active 0
overlayLayer base HRM base
setLedTxt 666 'H--'
hrm-auto:
setVar hrm_active 1
setVar hrm_timer_active 0
setVar hrm_tick 0
setVar hrm_tick_delay 10
setVar hrm_tick_active 4
overlayLayer base HRM mod
setLedTxt 666 'H+-'
if ($hrm_timer_active == 0) fork hrm-timer
Bind hrm-on, hrm-off, and hrm-auto onto some keys. I put them on some keys on my regular fn layer:
If you do not run any of these three activation macros, your normal keymap will work as before.
hrm-on turns home row mods constantly on. They will work just like the normal secondary function of UHK. No background timer process. The LED display will briefly show H++
as they turn on.
hrm-off turns home row mods off. No background timer process. The LED display will briefly show H--
as they turn off.
hrm-auto switches to “auto mode”. The timer process will be active, and home row mods will turn on and off automatically depending on your typing. The LED display will briefly show H+-
, and +T+
when the timer process is started. Afterwards, the display will toggle between H++
and H--
whenever the home row mods are activated or deactivated. During fast typing, you should see brief spurs of H--
(it’s really very short), immediately switching back to H++
whenever you are not typing.
Timing configuration
First, the regular secondary mode configuration of UHK applies. This is what I use; I set it in $onInit
:
set keystrokeDelay 10
set secondaryRole.defaultStrategy advanced
set secondaryRole.advanced.timeout 250
set secondaryRole.advanced.timeoutAction secondary
set secondaryRole.advanced.triggerByRelease 1
set secondaryRole.advanced.safetyMargin +5
There are two variables in hrm-auto that are meant to be adjusted:
setVar hrm_tick_delay 10
setVar hrm_tick_active 4
hrm_tick_delay
is the resolution of the tick timer in ms. Every $hrm_tick_delay ms the timer will increment the tick.
hrm_tick_active
is the tick count after which hrm are activated again (in auto mode).
So the total interval that hrms get disabled during fast typing is about $hrm_tick_delay * $hrm_tick_active
. It’s not totally precise because of macro execution speed etc. but it seems to be good enough.
For me, a tick delay of 10 and a tick active of 4 work well, resulting in about 40 ms of deactivation interval. I type approximately 80-90 wpm with bursts up to 110 wpm on good days.
I’ve used this configuration in auto mode for a while now, and I am definitely happier than without.
Possible future extensions:
- Currently, only primary keystrokes on hrm keys trigger the deactivation interval. This could be extended to all alphanumeric keys, but it would need one macro for each key. I think that’s more a topic for an actual firmware implementation.
- Unique deactivation intervals per hrm key are feasible (although I don’t think that is needed). In the key’s hrm macro, you’d need to check for the value of
$hrm_tick
instead of checking$hrm_active
. Additional adaptions are then also needed for “continuous on” mode.
Interested in feedback. Does this work for you? Is it better?