If you use vim or vim-style keybindings, it’s really convenient setting up left caps-lock to function as escape when tapped, but ctrl when chorded, i.e. held together with other keys.
On Windows, most solutions that do this are fully AutoHotKey-based.
However, as a heavy user of Emacs with evil-mode, but with enough ctrl-c chording for conventional Emacs bindings to confuse any keymapping solution, these solutions often broke.
The slightly more robust solution I settled on uses the built-in Windows remapping functionality to remap caps-lock to control (which is active from boot, and for the whole machine), and then a subset of the AutoHotKey approach only to do the ctrl (which is actually caps lock!) to escape translation.
Built-in Windows remap caps-lock to control
First we swap Caps-Lock and Control.
Either run this as admin in powershell:
$hexified = "00,00,00,00,00,00,00,00,02,00,00,00,1d,00,3a,00,00,00,00,00".Split(',') | % { "0x$_"};
$kbLayout = 'HKLM:\System\CurrentControlSet\Control\Keyboard Layout';
New-ItemProperty -Path $kbLayout -Name "Scancode Map" -PropertyType Binary -Value ([byte[]]$hexified);
… or apply this .reg
file:
REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex:00,00,00,00,00,00,00,00,03,00,00,00,1d,00,3a,00,3a,00,1d,00,00,00,00,00
Reboot to apply. Enjoy caps-lock as control.
AutoHotKey for Esc
Then we map Control (which is physically Caps-Lock) to Esc, but only when tapped by itself.
Ensure that you have AHK installed, with the below script running.
;; based on https://github.com/fenwar/ahk-caps-ctrl-esc/blob/master/AutoHotkey.ahk
;; modified because I already map capslock to ctrl using windows registry
;; here I'm ONLY adding behaviour that LControl tap results in esc
*LControl::
Send {Blind}{LControl down}
return
*LControl up::
Send {Blind}{LControl Up}
;; send Esc only when it was LControl by itself
if A_PRIORKEY = LControl
{
Send {Esc}
}
Return
Right click on the relevant AHK icon and select reload.