<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dot.git/config/hammerspoon/dotlib.lua, branch master</title>
<subtitle>Shell, editor and terminal configuration, and the tools that go with them
</subtitle>
<id>https://git.soarez.net/sz/dot.git/atom?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/dot.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dot.git/'/>
<updated>2026-10-01T15:05:25+00:00</updated>
<entry>
<title>hammerspoon: Caps Lock sends F20, and stays that way</title>
<updated>2026-10-01T15:05:25+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-01T15:05:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dot.git/commit/?id=6c7eb1b380cec100663c7c11d0252f9e6b46d34a'/>
<id>urn:sha1:6c7eb1b380cec100663c7c11d0252f9e6b46d34a</id>
<content type='text'>
hidutil remaps inside the IOKit HID driver, below the window server, so the
key stops being Caps Lock before anything above the driver sees it: no toggle
to suppress, and F20 is a real key to other apps, to System Settings
shortcuts, to a password field. An eventtap cannot do this -- Caps Lock is not
reachable from there, which is why hs.hid exists as its own IOKit module.

What hidutil will not do is cover a keyboard attached after it ran, so dotlib
re-applies on USB attach, on wake, and on a Caps Lock event. That last one is
the general answer: a mapped keyboard cannot produce a Caps Lock event, so an
event arriving at all is proof of a keyboard the remap missed, and the fault
triggers its own repair. The watchers are there to get the common cases before
a key is pressed, since the repairing press is spent repairing.

No launchd job and no link/ part. The remap lives and dies with Hammerspoon,
which already owns the keyboard on these machines, and a plist would only have
bought independence from it for the second or two before it starts -- a user
agent does not run at the login window either.

Nothing is bound to f20 here; bind it like any other key. A machine gives the
key back with capslock_off() from its local.lua, which loads last: shadowing
cannot undo a side effect that has already run. hs.ipc is required so
capslock_status() answers from a shell.
</content>
</entry>
<entry>
<title>hammerspoon: shared helpers move to dotlib.lua, which local.lua can require</title>
<updated>2026-09-21T15:30:47+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-09-21T15:30:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dot.git/commit/?id=94a8f1ff31a804a4dfa46ce287e30bf4691ae637'/>
<id>urn:sha1:94a8f1ff31a804a4dfa46ce287e30bf4691ae637</id>
<content type='text'>
A machine's local.lua is a separate chunk, so init.lua's locals were invisible
to it and anything it wanted -- hotkey(), the mod tables, type_text -- had to
be copied. They live in dotlib.lua now, linked beside init.lua, and both files
reach them the same way: require("dotlib").

init.lua keeps its own vocabulary by aliasing what it uses, so the 500 lines
below are untouched. It prepends its own directory to package.path first:
Hammerspoon already puts the config directory there (Contents/Resources/
setup.lua), but taking it from this file's path holds however the config was
found. A machine that pulls without re-linking has no dotlib.lua beside
init.lua, which would lose every hotkey silently -- that case notifies and
names the fix.

Checked with a stubbed hs: the module's hotkey, focus, execute, type_text and
type_current_date all behave, and local-example.lua binds its four keys
through it.
</content>
</entry>
</feed>
