MacOS HammerSpoon - 2 Button Script

Hey, I quickly translated the AHK Script from Hadronox / Rezel to a MacOS HammerSpoon Script.

local toggle1 = false
local toggle2 = false

hs.hotkey.bind({}, "1", function()
  toggle1 = not toggle1
  if toggle1 then
    hs.timer.doWhile(function() return toggle1 end, function()
      hs.eventtap.keyStroke({"cmd"}, "1")
      hs.timer.usleep(150 * 1000)
    end)
  end
end)

hs.hotkey.bind({}, "2", function()
  toggle2 = not toggle2
  if toggle2 then
    hs.timer.doWhile(function() return toggle2 end, function()
      hs.eventtap.keyStroke({"cmd"}, "2")
      hs.timer.usleep(150 * 1000)
    end)
  end
end)


1 Like

@Kaehfin this is exactly what I was looking for today. I installed Hammerspoon and did the Hello World so I know it’s working. I tried saving this in the .hammerspoom/Spoons path but couldn’t get it to load.

I put it in the init.lua and reloaded and see:

2023-02-21 07:39:07: – Loading extension: hotkey
2023-02-21 07:39:07: 07:39:07 hotkey: Enabled hotkey 1
2023-02-21 07:39:07: hotkey: Enabled hotkey 2
2023-02-21 07:39:07: – Done.

I tried testing this in a text editor and pressing 1 or cmd 1 doesn’t seem to spam the 1 key. Am I doing something wrong?

It works! Any idea how can I make it press faster? I’m not sure what 150 * 1000 mean, thanks!

Edited: I found, lowering first value it makes it faster ex.: 75 * 1000 will double speed

I had some issues with that script, it’s got some things that are limiting how fast it can press.
So, with the help of ChatGPT, I improved it and now you can pick which keybinds you want to use, which buttons it’s going to press, how many of the keybinds you want, and finally, how long the delay are in between.
There’s also a master keybind that’s set to cmd + 1, that lets you use the keyboard normally when it’s set to off, and when it’s on, it’ll start the key spamming when you push your keybinds.

Now here’s for the juicy part, here’s the code:

local toggle = {}
local keyRepeatTimer = {}
local keyPressInterval = 0.1  -- Set the delay here (0.1 seconds = 100 ms)

-- Arrays for keybinds and keys to press
local keybinds = {"1", "2"}  -- You can add more keybinds here
local keysToPress = {"å", "ä"}  -- Corresponding keys to press

-- Function to stop all key repetition timers
local function stopAllKeyRepeats()
  for i, _ in ipairs(toggle) do
    if keyRepeatTimer[i] and keyRepeatTimer[i]:running() then
      keyRepeatTimer[i]:stop()
    end
    toggle[i] = false
  end
end

k = hs.hotkey.modal.new('cmd', '1')
function k:entered() hs.alert'Entered mode' end

-- Stop all keypresses when exiting the modal
function k:exited()  
  hs.alert'Exited mode'
  stopAllKeyRepeats()
end

k:bind('cmd', '1', function() 
  k:exit() 
end)

-- Function to create a key event (keydown and keyup)
local function pressKey(key)
  local keyDown = hs.eventtap.event.newKeyEvent({}, key, true)
  local keyUp = hs.eventtap.event.newKeyEvent({}, key, false)
  keyDown:post()
  keyUp:post()
end

-- Function to start key repetition for a given toggle and key
local function startKeyRepeat(i)
  -- Stop any existing timer for this keybind
  if keyRepeatTimer[i] and keyRepeatTimer[i]:running() then
    keyRepeatTimer[i]:stop()
  end

  -- Create a single timer for key repetition
  keyRepeatTimer[i] = hs.timer.doEvery(keyPressInterval, function()
    if toggle[i] then
      pressKey(keysToPress[i])
    else
      keyRepeatTimer[i]:stop()  -- Stop the timer if toggled off
    end
  end)
end

-- Dynamically create keybinds and their corresponding actions
for i, keybind in ipairs(keybinds) do
  toggle[i] = false  -- Initialize toggle for each keybind

  k:bind('', keybind, function()
    if toggle[i] then
      stopAllKeyRepeats()  -- Stop all if any key is active
    else
      stopAllKeyRepeats()  -- Stop any currently running key presses
      toggle[i] = true  -- Start the repetition for this keybind
      startKeyRepeat(i)
    end
  end)
end