Hammerspoon: CAPS LOCK woes

Created on 1 May 2020  Â·  4Comments  Â·  Source: Hammerspoon/hammerspoon

Karabiner Elements has been causing a lot of crashes on my Macs running 10.15.4 lately. Sadly I've had to remove it.

Is there any way, using _only_ Hammerspoon, to set up CAPS LOCK to act as CTRL when pressed+held down, but pass ESC or some other key when simply "tapped" ?

I've looked at https://kalis.me/setup-hyper-key-hammerspoon-macos/ and https://medium.com/@sixlive/a-more-useful-caps-lock-15d6e0641e0b but both require Karabiner.

Most helpful comment

Not where I can play with this right now but re your issue with the screen saver, check out hs.caffeinate... it has a watcher that can run a function as the system enters/leaves the screen saver which could probably toggle the caps remap when entering and then back (if it was on before) when leaving the screen saver.

All 4 comments

I figured out how to do this using hs.eventtap.event.types.flagsChanged to trap standalone modifier keypresses (without other keys) combined with hs.timer.delayed to fire after 2s and branch depending on whether the modifier was still held down. But that event tap was very costly CPU-wise. Just tapping my ⌘ or ⌥ keys repeatedly would spike CPU up to 18-21%.

In the end I went for a simpler hotkey bind <ctrl+shift+L> and made use of the hs.hid module:

function toggleCaps()
  if hs.hid.capslock.get() then
    hs.hid.capslock.set(false)
    hs.alert.closeAll(0)
    hs.alert.show('↓ caps off',1)
  else
    hs.hid.capslock.set(true)
    hs.alert.closeAll(0)
    hs.alert.show('↑ CAPS ON',1)
  end
  return
end

hs.hotkey.bind({"ctrl", "shift"}, "L", function()
  toggleCaps()
end)

It's working well...

One caveat: there's a sticky situation you can get into if your screen happens to lock while you've got CAPS enabled, and can thus no longer type your password or deactivate the CAPSLOCK since those events are blocked while the login window is active.

To work around this, I created this emergency bash script that I can execute via ssh from my phone or a nearby computer. The script makes use of Apple's hidutil command to map TAB → CAPSLOCK temporarily.

#!/usr/bin/env bash

echo "resetting UserKeyMapping back to default"
hidutil property --set '{"UserKeyMapping":[]}' &>/dev/null

read -r -p "map TAB[0x2b] → CAPSLOCK[0x39] (y/N)? " ANSWER
case ${ANSWER,,} in
  (y) hidutil property --set '{"UserKeyMapping": [{
        "HIDKeyboardModifierMappingSrc":0x70000002b, 
        "HIDKeyboardModifierMappingDst":0x700000039
      }]}' &>/dev/null;
      echo "remapped TAB → CAPSLOCK";;
  (*) exit;;
esac

Not where I can play with this right now but re your issue with the screen saver, check out hs.caffeinate... it has a watcher that can run a function as the system enters/leaves the screen saver which could probably toggle the caps remap when entering and then back (if it was on before) when leaving the screen saver.

Thanks @asmagill 🙌 — great tip! I cobbled together a new version that uses hs.caffeinate to trigger a capslock deactivation when the screen locks.

One gotcha that took a bit of time to work around was: toggling CAPS LOCK simultaneously with the screen lock would be seen as a keystroke by the system, and cause the screen to immediately unlock.

My solution was to wrap the hs.hid.capslock.set() in a 5-second timer function instead, causing it to fire _after_ the 5-second grace period from System Prefs:

image

Not the exact code I'm using, but this snippet shows the basic premise:

function evt_callback(e)
  if (e == evt_watcher.screensDidLock) then
    if hs.hid.capslock.get() then
      local caps_timer = hs.timer.delayed.new(5, function()
        hs.hid.capslock.set(false)
      end):start()
    end
    return
  end
end

evt_watcher.new(evt_callback):start()

Pretty excellent investigation! I’m going to close out the issue because I don’t think there’s any work to be done on Hammerspoon here. Please re-open if you disagree :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zhenwenc picture zhenwenc  Â·  4Comments

Pancia picture Pancia  Â·  4Comments

Madd0g picture Madd0g  Â·  4Comments

lazandrei19 picture lazandrei19  Â·  4Comments

specter78 picture specter78  Â·  3Comments