New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bind capslock to hotkey #3512
Comments
Yes and no... as a toggle, yes, we can detect the caps-lock and optionally enable a set of hotkeys. But we can't prevent it from being used by the OS to actually engage the caps lock, so no release event is detected by us. Hitting the caps-lock again will issue another flag change event so we can un-enable the hotkeys, but it has to be as a toggle, not as a true modifier like key. For example: local eventtap = require("hs.eventtap")
local event = eventtap.event
-- get initial capslock state
local clFlagState = (eventtap.checkKeyboardModifiers(true)._raw & event.rawFlagMasks.alphaShift) ~= 0
local capsLockOnFn = function()
-- enable your hot keys here
print("caps lock down")
end
local capsLockOffFn = function()
-- disable your hot keys here
print("caps lock not down")
end
local eventtapFn = function(e)
-- this is so we can use it to set initial state when started up
if type(e) == "boolean" then
if e then
capsLockOnFn()
else
capsLockOffFn()
end
else
local rf = e:rawFlags()
local state = (rf & event.rawFlagMasks.alphaShift) ~= 0
if state and not clFlagState then
clFlagState = true
capsLockOnFn()
-- in theory, the following should "throw away" this event, but as you'll see
-- if you try this code, it doesn't affect the caps-lock event -- the system has
-- already done its job and set the flag internally and lit the caps-lock led
--
-- in my code, I'd just remove it -- leaving it in only prevents other eventtaps
-- (possibly in other apps) from detecting this flag change if they registered
-- after we did
return true
elseif not state and clFlagState then
clFlagState = false
capsLockOffFn()
end
end
end
-- set initial state
eventtapFn(clFlagState)
tap = eventtap.new({ event.types.flagsChanged }, eventtapFn):start() edited to remove some unnecessary code to flags -- the inequality check already sets true/false |
Thinking about it a little more, it sounds like you're doing something similar to my viKeys.lua, which uses the Using the above as a template, I would do something like this -- it has the advantage that if you hold the shift/option/cmd keys while the toggle is in effect that you get the shift-arrow/option-arrow/cmd-arrow behavior and auto-repeats as well... -- this is minimally tested, but initial tests show that it seems to work fine, as long as you
-- remember that caps-lock is a toggle and it's only changing JKLI and no other keys
-- (though disabling those would just require the key-handler returning true instead of
-- false in the else clause)
local eventtap = require("hs.eventtap")
local event = eventtap.event
-- get initial capslock state
local clFlagState = (eventtap.checkKeyboardModifiers(true)._raw & event.rawFlagMasks.alphaShift) ~= 0
-- key handling function
local keyHandler = function(e)
-- local watchFor = { h = "left", j = "down", k = "up", l = "right" } -- vi style
-- I remember using this on an Apple ][, but forget the program, so no fancy style name :-)
local watchFor = { j = "left", k = "down", i = "up", l = "right" }
local actualKey = e:getCharacters(true)
local replacement = watchFor[actualKey:lower()]
-- if replacement has a value, then one of our keys was pressed
if replacement then
-- duplicate the event, keeping all traditional modifiers, but with our (arrow) key instead
local isDown = e:getType() == event.types.keyDown
local flags = {}
for k, v in pairs(e:getFlags()) do
if v then
table.insert(flags, k)
end
end
local replacementEvent = event.newKeyEvent(flags, replacement, isDown)
if isDown then
-- if a key is held down, it actually sends a second down event, but with
-- the autorepeat property set, so duplicate that as well if this is a
-- key-down event:
replacementEvent:setProperty(event.properties.keyboardEventAutorepeat, e:getProperty(event.properties.keyboardEventAutorepeat))
end
-- throw out the original event and replace it with ours
return true, { replacementEvent }
else
-- otherwise, do nothing to the event, just pass it along
return false
end
end
-- set up, but don't start, keyup/keydown eventtap
local keyListener = eventtap.new({ event.types.keyDown, event.types.keyUp }, keyHandler)
-- this is the eventtap function that checks for the caps lock key change
local eventtapFn = function(e)
-- this is so we can use it to set initial state when started up
local state
if type(e) == "boolean" then
state = e
clFlagState = not clFlagState
else
state = (e:rawFlags() & event.rawFlagMasks.alphaShift) ~= 0
end
if state and not clFlagState then
clFlagState = true
keyListener:start()
elseif not state and clFlagState then
clFlagState = false
keyListener:stop()
end
end
-- set initial state
eventtapFn(clFlagState)
-- start watching flags
tap = eventtap.new({ event.types.flagsChanged }, eventtapFn):start() |
@asmagill Thank you very much for your detailed response! My plans were to hotkey capslock (while held down) with other keys to get shift-arrow/option-arrow/cmd-arrow behavior like you said, however instead of pressing down the option/shift/cmd keys, it would be S,D,F keys. So for example:
And ofcourse i can create any combination of s,d,f being pressed down simultaneously for a total of 2^3 = 8 different options (or even use other keys to increase it exponentially) From some reading around i see that people use karabiner-elements to map the capslock key to a "hyper" key (from my understanding its a combination of keys). |
May macOS' key remapping help you? It doesn't turn capslock into a new modifier key but I believe you could do this within Hammerspoon. I remapped caps lock to F13 to toggle my terminal window by creating the file <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.local.KeyRemapping</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/hidutil</string>
<string>property</string>
<string>--set</string>
<string>{"UserKeyMapping":[
{
"HIDKeyboardModifierMappingSrc": 0x700000039,
"HIDKeyboardModifierMappingDst": 0x700000068
}
]}</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist> Run For reference: Apple HIDUTIL. You can generate a property list with any remappings here. Pros:
Cons:
EDIT: When thinking about it, it's much easier to put this into function key_remapping()
-- remap capslock to F13:
status = os.execute("hidutil property --set '{\"UserKeyMapping\":[{\"HIDKeyboardModifierMappingSrc\": 0x700000039, \"HIDKeyboardModifierMappingDst\": 0x700000068}]}'")
if not status then
hs.dialog.blockAlert("Key remapping failed", "Check with:\nhidutil property --get UserKeyMapping")
end
end
key_remapping() |
Hi, I'm trying to set up a hotkey where CAPSLOCK-<NUMBER_KEY> executes a command, i.e. I'd like to use capslock as a modifier key. Would your code here be appropriate for such use? Ideally, I'd like to also be able to reset the capslock key back to it's previous state after pressing the hotkey so e.g. if capslock is off then after hitting CAPSLOCK-1 the capslock key should reset back to off. Hope that makes sense. |
Hi @piechologist, I recently started using Universal Control to operate two Macs and encountered a challenging issue with keyboard events not transmitting properly between the machines with karabiner-elements. I've been struggling with this for quite a while. Fortunately, I came across your solution in this issue, and it perfectly solved my problem! I followed your method to remap the capslock key within Hammerspoon, and it worked flawlessly. It's a relief to have this issue resolved, thanks to your guidance. I just wanted to express my sincere gratitude for your help. Your solution not only helped me immensely but will undoubtedly assist others facing similar challenges. Thank you again for sharing your knowledge and expertise! |
I want to bind capslock along with J,K,L,I keys to trigger arrow keys respectively.
From my understanding, this might not be possible according to this issue
Is this still the case with hammerspoon?
The text was updated successfully, but these errors were encountered: