Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ With one more bit of setup, you'll be able to hold <kbd>caps lock</kbd> for <kbd

Open *System Preferences*, navigate to *Keyboard > Modifier Keys*, and set the <kbd>caps lock</kbd> key to <kbd>control</kbd>. [[screenshot]](https://user-images.githubusercontent.com/2988/27111039-7f620442-507b-11e7-9bcf-93d46e14af13.png)

You may find that sometimes your ControlEscape key does not result in an <kbd>escape</kbd>, and you have tap it a few times. By default, the <kbd>escape</kbd> key is emitted if you tap and release the ControlEscape key within 150ms, and your pinky may not be dexterous enough to meet this requirement. In this case, you can adjust the tapTime in your Hammerspoon config (commonly `~/.hammerspoon/init.lua`):

```lua
-- replace this
hs.loadSpoon("ControlEscape"):start() -- Load Hammerspoon bits from https://github.com/jasonrudolph/ControlEscape.spoon

-- with
control_escape = hs.loadSpoon("ControlEscape")
control_escape.tapTime = 200 -- ms, making this number too high can make it hard to activate `control`
control_escape:start() -- Load Hammerspoon bits from https://github.com/jasonrudolph/ControlEscape.spoon
```

Now you're ready to rock. 🤘

## Where can I get this code as a Spoon?
Expand Down
5 changes: 2 additions & 3 deletions init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ obj.license = 'MIT - https://opensource.org/licenses/MIT'

function obj:init()
self.sendEscape = false
self.tapTime = 0.150 -- If `control` is held for this long, don't send `escape`
self.lastModifiers = {}

-- If `control` is held for this long, don't send `escape`
local CANCEL_DELAY_SECONDS = 0.150
self.controlKeyTimer = hs.timer.delayed.new(CANCEL_DELAY_SECONDS, function()
self.controlKeyTimer = hs.timer.delayed.new(self.tapTime, function()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 @quangd42: I tried testing this locally, but it seems to always use the default value of 150 ms, even if I follow the updated instructions in the README.

I have a feeling that this is due to hs.timer.delayed.new(self.tapTime, ...) being called in obj:init() at initialization time, and therefore any changes made to self.tapTime after object initialization have no effect on the timer. That's my best guess at least.

If you're able to get this pull request working as desired and you can describe how to verify the new behavior, I'm happy to take another look.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking at this. To be honest I was not so sure how to verify the delay amount of the controlKeyTimer, as hs.timer does keep an internal state for the delay amount.

So I ended up moving the initialization of the timer to the start method so it's clear what value of CANCEL_DELAY is being passed to the timer, and it only happens once. Hopefully this is straightforward enough to eliminate guess work.

Thanks!

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 Hi @quangd42 - I've checked out the latest updates. They do look like they should work, but I still haven't been able to definitively verify that they work. Before accepting any changes, it's important to me that I'm able to verify the new behavior. Can you describe a process for manually testing this behavior to verify that it's working as you expect?

@quangdn42 quangdn42 May 29, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jasonrudolph - In my latest commit you can find a few commented lines of code that will log elapsed time since the timer starts until either 'escape' is sent or the timer is canceled, using https://www.hammerspoon.org/docs/hs.timer.html#absoluteTime. I pushed the code as comments so that the PR can be merged as is and avoid the extra overhead.

Here are the steps:

  1. Uncomment the logging code.
  2. In init.lua, call start(200) on the module. Open hammerspoon console and reload config.
  3. Hold 'control' key. Expect to see 'timer canceled after {time} ms' in the console, where time is approximately 200.
  4. Press and quickly release 'control' key. Expect to see 'escape sent after {time} ms' in the console, where time is less than 200.

Let me know if that will work.

self.sendEscape = false
end)

Expand Down