-
-
Save arbelt/b91e1f38a0880afb316dd5b5732759f1 to your computer and use it in GitHub Desktop.
ctrl_table = { | |
sends_escape = true, | |
last_mods = {} | |
} | |
control_key_timer = hs.timer.delayed.new(0.15, function() | |
ctrl_table["send_escape"] = false | |
-- log.i("timer fired") | |
-- control_key_timer:stop() | |
end | |
) | |
last_mods = {} | |
control_handler = function(evt) | |
local new_mods = evt:getFlags() | |
if last_mods["ctrl"] == new_mods["ctrl"] then | |
return false | |
end | |
if not last_mods["ctrl"] then | |
-- log.i("control pressed") | |
last_mods = new_mods | |
ctrl_table["send_escape"] = true | |
-- log.i("starting timer") | |
control_key_timer:start() | |
else | |
-- log.i("contrtol released") | |
-- log.i(ctrl_table["send_escape"]) | |
if ctrl_table["send_escape"] then | |
-- log.i("send escape key...") | |
hs.eventtap.keyStroke({}, "ESCAPE") | |
end | |
last_mods = new_mods | |
control_key_timer:stop() | |
end | |
return false | |
end | |
control_tap = hs.eventtap.new({12}, control_handler) | |
control_tap:start() | |
Also, it doesn't appear to be breaking the logic since the priming on line 2
isn't necessary, but it's not named the same as the rest of the file. Line 2
's sends_escape = true,
has an "s" that the other references don't. It should be:
send_escape = true,
I'm finding this solution doesn't work with my tmux prefix chord unfortunately. I'll hack on it a bit and report back. If anyone else is in the same boat, let me know, and let me know what you've tried.
To get ready to play with it, I did some cleanup and simplification refactoring (original logic is still in tact, but with fewer variables). Feel free to have a look if you're using it as a jumping off point as well: https://gist.github.com/rjhilgefort/07ce5cdd3832083d7e94113d54372b1c
I have expanded on this script to avoid sending Escape when other keys are tapped while Control is held down. I’ve been running this for a few weeks now. Works great.
https://github.com/jasoncodes/dotfiles/blob/master/hammerspoon/control_escape.lua
Thank you arbelt and jasoncodes! This saved me from a lot of pain!
Instead of using hs.eventtap.keyStroke
to emit an event, returning from the control_handler
function with a table of events to post (KeyDown then KeyUp) makes the escape perform instantly on KeyUp. hs.eventtap.keyStroke
feels slow for some reason, taking a fraction of a second before the actual stroke gets sent, drove me nuts.
control_handler = function(evt)
local new_mods = evt:getFlags()
if last_mods["ctrl"] == new_mods["ctrl"] then
return false
end
if not last_mods["ctrl"] then
last_mods = new_mods
send_escape = true
control_key_timer:start()
else
last_mods = new_mods
control_key_timer:stop()
if send_escape then
return true, {
hs.eventtap.event.newKeyEvent({}, 'escape', true),
hs.eventtap.event.newKeyEvent({}, 'escape', false),
}
end
end
return false
end
Seems much faster!
@wezzynl Thank you for that comment. I also felt it a little unbearable sometimes to wait a fraction of a second for it to work. Also for some reason the escape key event would not be sent to emacs until I tried your solution.
In case anyone else comes upon this and finds the above code confusing or, like me, keeps getting a bug where you end up having to hit escape multiple times, I rewrote the above and added comments, clearer variable names, and fixed that bug. Gist located here: https://gist.github.com/zcmarine/f65182fe26b029900792fa0b59f09d7f
Definitely wouldn't have gotten that done without the above as an initial starting point though, so thanks a ton to arbelt and jasoncodes!
Hi everyone,
Is there a way to not have this run when a specific app is in focus? (for example: remote desktop, vmware, teamviewer etc.)
Many thanks!
@safecancel you can grab the current active application and use that to determine if the logic should run.
Thanks everyone. I've been using Karabiner-Elements only to remap caps lock to escape on tap and control on hold. With your solutions, I can use Hammerspoon instead (after changing caps lock to control in System Preferences) and drop Karabiner as a dependency.
This is a great and very helpful gist. I used it to get my application to more reliably trigger the ESCAPE key. Since sending it with the hs.eventtap.sendKey
does not seem to reliably do so (especially in applications like Alfred, 1Password, and such.
But the tip by @wezzynl in their comment above got me thinking and it actually does work much faster and more reliable.
@arbelt I think it would be more clear if you change line
39
to be more explicit about the event flag you're targeting (it was the first thing I went digging for when reading through the code).Without logging out
hs.eventtap.event.types
, it's unclear what event code12
is.