Closed latenitefilms closed 4 years ago
Any ideas what I'm doing wrong @asmagill ?
The event "queue" that hs.eventtap.event
uses is not the global one, but rather one unique to Hammerspoon (I remember trying the global one at one point and it broke some things people were used to so didn't really pursue it further, though when I was working on an eventtap replacement (which might be worth reconsidering for 2.0) I did make this a choice you could set when creating the event).
Because of this, the "shift" key isn't "pressed" globally (i.e. events originating outside of Hammerspoon do not see this flag as having been set)
As an example, try the following in the console:
t = true ; d = hs.hotkey.bind({"cmd","alt"}, "S", nil, function() hs.eventtap.event.newKeyEvent(hs.keycodes.map.shift, t):post() ; t = not t ; end)
Now, press and release Cmd-Alt-S. Type something into the console (without the shift key pressed) and you'll see it in lower case, BUT try this in the console:
hs.eventtap.event.newKeyEvent(hs.keycodes.map.a, true):post() ; hs.eventtap.event.newKeyEvent(hs.keycodes.map.a, false):post()
And you'll see an upper case A
appear in the console's input field.
To verify it's because of our hotkey, press and relase Cmd-Alt-S again, and then repeat the sample posts... this time, the input field receives a lower case a
.
Ah, I see - thanks for the detailed explanation! Is there any workaround for this?
Is your eventtap replacement on GitHub?
@cmsj & @asmagill - This issue has cropped up again, so if you have any ideas let me know!
@asmagill - I haven't been able to find your hs.eventtap
replacement anywhere - is it on GitHub?
Also, I just randomly thought, I wonder if I can make use of hs.eventtap.event:post([app])
, and just specify to the frontmost application as a workaround - but alas, it doesn't work as expected.
Whilst you're online @asmagill - I'm still pretty keen to somehow tap into the global event queue, so that I can use buttons on external devices (like the Streamdeck) as modifier keys - so if you have any tips or tricks, let me know. Thanks!
Hope you're keeping healthy, happy and sane in these crazy times!
For detecting events (extensions/eventtap/internal.m
), we're already as "low" as we can get without using root permissions in a helper app of some sort (see https://developer.apple.com/documentation/coregraphics/1454426-cgeventtapcreate)
For posting events (extensions/eventtap/event.m)
, what I refer to above concerns CGEventSourceCreate
(see https://developer.apple.com/documentation/coregraphics/1408776-cgeventsourcecreate) and I was experimenting with using kCGEventSourceStateCombinedSessionState
as the argument.
As to the code I was working on, I think I was referring to https://github.com/asmagill/hammerspoon_asm/tree/2b2818c86799c32695de35298b68cf58e3dd5c0c/event, which I moved to a local archive folder for things I've more or less abandoned, but this was the last version in my repos history. I think I'd already given up on kCGEventSourceStateCombinedSessionState
by this time so feel free to try your own changes or browsing further back in the repo's history...
Oh, interesting - thanks heaps @asmagill !
Looking at this code, it looks like you did previous experiment with using kCGEventSourceStateCombinedSessionState
in the main Hammerspoon release?
I'll try uncommenting it out at some point, and see what explodes.
Sadly, changing CGEventSourceCreate(kCGEventSourceStatePrivate);
to CGEventSourceCreate(kCGEventSourceStateCombinedSessionState)
didn't seem to have any impact.
I also tried changing CGEventPost(kCGSessionEventTap, event);
to CGEventPost(kCGHIDEventTap, event);
, but again, it had no impact.
I know it's possible, because the Loupedeck control surface (for example) allows you to use buttons on their panel as modifier keys, without any admin privileges or special helper apps.
I did just come across this code here, which allows you to toggle CAPS LOCK - I wonder if a similar technique can be used for other modifiers?
var ioConnect: io_connect_t = .init(0)
let ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass))
IOServiceOpen(ioService, mach_task_self_, UInt32(kIOHIDParamConnectType), &ioConnect)
var modifierLockState = false
IOHIDGetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), &modifierLockState)
modifierLockState.toggle()
IOHIDSetModifierLockState(ioConnect, Int32(kIOHIDCapsLockState), modifierLockState)
IOServiceClose(ioConnect)
The other thing I was thinking was maybe I could use hs.eventtap
to intercept all events and "inject" a modifier key if needed? For example, this seems to work:
addShiftToEverything = hs.eventtap.new({hs.eventtap.event.types.keyDown}, function(e)
local flags = e:getFlags()
print(string.format("flags: %s", hs.inspect(flags)))
flags.shift = true
e:setFlags(flags)
return false, e
end):start()
...but it's probably a bit risky, as it has the potential to slow down keyboard input.
Any other thoughts or ideas @asmagill or @cmsj ?
@asmagill - Until I come up with a better solution, I'm currently "injecting" the modifier keys using hs.eventtap
, which actually seems to work pretty well.
Here's the code I'm using:
However, injecting into gestures doesn't seem to work. Any hints or tricks?
Sorry, no. Gestures seem to be a little bit of a black art, at least from the CoreGraphics side of things, which is what most of our eventtap module uses. I've yet to come across a good example for dissecting them or synthetically creating them. Of course that was a couple of years ago that I last looked...
Using eventtap and looking at the rawflags value, you can distinguish between left and right for cmd, option, and shift but that's the best I have at the moment for extending the number of modifier keys and combinations we can detect. (I forget the exact values you need to look for... check out the virtual toolbar example either in the module or in my personal config -- it specifically looks for a right option key for toggling the toolbar visibility and a left one for allowing you to move the toolbar rather than click on it)
FYI: I also tried this, but no dice:
Funnily enough, I stumbled across this post, which suggested using AppleScript, and it actually works great!
hs.applescript([[tell application "System Events" to shift key down]])
Although it does work... there is a slight delay in it being triggered. It's definitely not as responsive as holding down the SHIFT key for example. I think this would still be better if we could solve it in Obj-C land.
@cmsj - Do you have any ideas?
Note to self - this is interesting and useful:
https://stackoverflow.com/questions/27141645/osx-runloop-options-when-creating-event-taps
FYI - The BetterTouchTool creator came back to me with these helpful hints:
CGEnableEventStateCombining
looks really interesting:
Enables or disables the merging of actual key and mouse state with the application-specified state in a synthetic event.
Using AppleScript seems to get around my specific issue, so closing this for now.
If I trigger the below code in the console:
...I expect it to hold down the SHIFT key until I trigger:
...however it doesn't seem to do anything?
I've also tried
hs.eventtap.event.newKeyEvent({}, hs.keycodes.map.shift, true):post()
.Any ideas?