Hammerspoon / hammerspoon

Staggeringly powerful macOS desktop automation with Lua
http://www.hammerspoon.org
MIT License
12.04k stars 584 forks source link

Having trouble sending arrow key events #2282

Open axsuul opened 4 years ago

axsuul commented 4 years ago

My script enables it so that when I press CAPSLOCK+, it converts and sends +. This works well for all key characters except for the arrow keys where the key event doesn't appear to be triggering. For example, pressing:

Is there something different I need to do in order to trigger arrow keys?

Here's the script (please excuse the other stuff but I thought I'd include everything in case there's something I'm not doing correctly). The part that's relevant is the "Passthrough" section. Note: this also relies on Karabiner Elements which sets it up so that pressing CAPSLOCK sends the virtual key F18.

-- A global variable for the Hyper Mode
k = hs.hotkey.modal.new({}, "F17")
hyper = {'cmd', 'ctrl', 'shift', 'alt'}

-- Enter Hyper Mode when F18 (Hyper/Capslock) is pressed
pressedF18 = function()
  k.triggered = false
  k.modifier = false
  k:enter()

  trigger_modifier = function()
    k.modifier = true
  end

  -- Only trigger as modifier if held longer than thisj
  hs.timer.doAfter(0.25, trigger_modifier)
  -- hs.alert.show('in')
end

-- https://github.com/Hammerspoon/hammerspoon/issues/1011#issuecomment-261114434
-- Necessary to define a new function to get faster key strokes for repeating
fastKeyStroke = function(modifiers, key)
  local event = require("hs.eventtap").event
  event.newKeyEvent(modifiers, string.lower(key), true):post()
  event.newKeyEvent(modifiers, string.lower(key), false):post()

  k.triggered = true
end

-- Passthrough
passthrough_definitions = {
  -- 1Password
  {hyper, '1'},

  -- Alfred
  {hyper, 'v'},

  -- Fantastical
  {hyper, 'c'},

  -- macOS Sidebar
  {hyper, '\\'},

  -- Mate Translate
  {hyper, 't'},

  -- Moom
  {hyper, 'up'},
  {hyper, 'right'},
  {hyper, 'down'},
  {hyper, 'left'},
  {hyper, '='},
  {hyper, 'tab'},
  {hyper, '9'},
  {hyper, '0'},
}

for i, definition in pairs(passthrough_definitions) do
  -- Note that Lua copies by reference so assigning variable doesn't work
  -- modifier is definition[1]
  -- key is definition[2]

  passthrough = function()
    print('Triggering passthrough: hyper + ' .. definition[2])

    fastKeyStroke(definition[1], definition[2])
    -- k.triggered = true
  end

  -- Uncomment line below when binding new
  k:bind({}, definition[2], passthrough, nil, nil)
end

-- Applications
apps = {
  {'a', 'Anki'},
  {'w', 'Bear'},
  {';', 'Discord'},
  {'e', 'Evernote'},
  {'f', 'Finder'},
  {'r', 'Google Chrome'},
  {'/', 'Intercom'},
  {'n', 'iTerm2'},
  {'i', 'Polymail'},
  {'u', 'Slack'},
  {'b', 'Firefox'},
  {'p', 'Spotify'},
  {'m', 'Sublime Text'},
  {'g', 'Telegram'},
  {'o', 'Todoist'},
  {'y', 'WhatsApp'},
}

for i, app in ipairs(apps) do
  launchApplication = function()
    hs.application.launchOrFocus(app[2])
    k.triggered = true
  end

  k:bind({}, app[1], launchApplication, nil, nil)
end

-- function onAppLaunched(appName, eventType, app)
--   if eventType == hs.application.watcher.launched then
--     if appName == 'Firefox' then
--       browser = 'Firefox'
--       safariPid = app:pid()

--       print("Firefox launched, setting browser to Firefox with PID " .. safariPid)
--     end
--   elseif eventType == hs.application.watcher.terminated then
--     if app:pid() == safariPid then
--       browser = 'Google Chrome'
--       safariPid = nil

--       print("Application with PID " .. safariPid .. " matching Safari terminated, setting browser to Google Chrome")
--     end
--   end
-- end

-- Open specific browser depending on what's open
-- appWatcher = hs.application.watcher.new(onAppLaunched)
-- appWatcher:start()

-- Arrow keys
hs.fnutils.each({
  { modifiers={}, key='h', direction='Left' },
  { modifiers={'shift'}, key='h', direction='Left' },
  { modifiers={'cmd'}, key='h', direction='Left' },
  { modifiers={'shift', 'cmd'}, key='h', direction='Left' },
  { modifiers={}, key='j', direction='Down' },
  { modifiers={'shift'}, key='j', direction='Down' },
  { modifiers={'cmd'}, key='j', direction='Down' },
  { modifiers={'shift', 'cmd'}, key='j', direction='Down' },
  { modifiers={}, key='k', direction='Up' },
  { modifiers={'shift'}, key='k', direction='Up' },
  { modifiers={'cmd'}, key='k', direction='Up' },
  { modifiers={'shift', 'cmd'}, key='k', direction='Up' },
  { modifiers={}, key='l', direction='Right' },
  { modifiers={'shift'}, key='l', direction='Right' },
  { modifiers={'cmd'}, key='l', direction='Right' },
  { modifiers={'shift', 'cmd'}, key='l', direction='Right' }
}, function(config)
  k:bind(config.modifiers, config.key,
    function() fastKeyStroke(config.modifiers, config.direction) end,
    nil,
    function() fastKeyStroke(config.modifiers, config.direction) end
  )
end)

-- k:bind({'shift'}, 'k', function() fastKeyStroke({'shift'}, 'Up') end, fastKeyStroke({'shift'}, 'Up'), nil)
-- Leave Hyper Mode when F18 (Hyper/Capslock) is pressed,
--   send ESCAPE if no other keys are pressed.
releasedF18 = function()
  k:exit()

  if not k.triggered then
    -- If hotkey held longer than this amount of time
    -- let it remain as modifier and don't send ESCAPE
    if not k.modifier then
      hs.eventtap.keyStroke({}, 'ESCAPE')
    else
      print("Modifier detected")
    end
  end
end

-- Bind the Hyper key
hs.hotkey.bind({}, 'F18', pressedF18, releasedF18)

-- Bind so that modifiers can be pressed first before Hyper
-- key for combinations
hs.hotkey.bind({'shift'}, 'F18', pressedF18, releasedF18)
hs.hotkey.bind({'cmd'}, 'F18', pressedF18, releasedF18)

-- Reload config when any lua file in config directory changes
function reloadConfig(files)
    doReload = false
    for _,file in pairs(files) do
        if file:sub(-4) == '.lua' then
            doReload = true
        end
    end
    if doReload then
        hs.reload()
        print('reloaded')
    end
end
local myWatcher = hs.pathwatcher.new(os.getenv('HOME') .. '/.hammerspoon/', reloadConfig):start()

Thanks for the great tool!

NightMachinery commented 9 months ago

I also have this problem. Did you find a solution?

NightMachinery commented 9 months ago

I found a fix, see: Mission control related shortcuts do not work on macOS Mojave · Issue #1946 · Hammerspoon/hammerspoon