Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
589 views
in Technique[技术] by (71.8m points)

macos - How to hook/remap an arbitrary keyboard event on OSX?

I would like to map:

  • CAPS-LOCK to Fn
  • Fn to Left-MOUSE
  • LSHIFT+3 to #
  • RSHIFT+3 to something else
  • etc.

I have searched exhaustively for any tool that offers complete freedom for remapping keyboard input, but cannot find one. (Windows has AutoHotkey).

I'm planning to write my own tool that parses a config file.

But how to actually dig in and do this?

Solving this problem will require a thorough understanding of the journey of a keystroke through the operating system, so as to intercept at the appropriate point.

I think I need to eat the event at a low-level, where it is still a virtual key code, then provide my own customised mapping system and inject an appropriate event further up the system.

But where (and how)?

Edit: I am detailing the results of my research in an answer below (which maybe should be migrated back into the question at some point).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm making this community wiki, please feel welcome to improve it.

Sub-Questions I've asked:

I can intercept almost all keydown/keyup events at the bottom of the middle tier. Except for power, and also CAPSLOCK key-UP when it is transitioning from ON to OFF.

Pretty nasty!

Working at the bottom tier level, I can get all key up/down except for the PowerKey.

If it were not for that awkward 75% success rate for CapsLock I would have a good solution. It is vexing that handling a key in such a useful location massively escalates the required complexity.

I have found DDHidLib and am currently looking through it to figure out if it smoothes that problem.

Research

Googling "keyEventWithType CGEventTapCreate" seems like a good idea, as those are essential ingredients for Tapping the event and Re-Emitting it.

Yay! Modify keyDown output -- that code compiles, and with minor tweaking (CGEventMaskBit( kCGEventKeyDown ) | CGEventMaskBit( kCGEventFlagsChanged ),) I can pick up modifier keys also. I get different keycodes for LSHIFT and RSHIFT. Brilliant!

Problems with the above:

  • Tapping kCGEventKeyDown works for some function keys but not others. It looks as though Apple have only overloaded certain keys, and the overloaded ones seem to get caught at a lower level.
  • Power/Eject key doesn't get caught.
  • I don't see any way to disambiguate which device the keystroke is coming from.

How to tap (hook) F7 through F12 and Power/Eject on a MacBook keyboard

http://blog.tklee.org/2008/09/modifiers-across-keyboards.html
-> http://forums.macrumors.com/showthread.php?t=778484
-> https://github.com/maravillas/dual-keyboards

https://github.com/pkamb/PowerKey may provide some insight
-> https://github.com/pkamb/PowerKey/blob/master/PowerKey/PKPowerKeyEventListener.m
-> Apple Keyboard Media Key Event Handling -- Rogue Amoeba
... system wide shortcut for Mac OS X
-> http://snippets.aktagon.com/snippets/361-registering-global-hot-keys-with-cocoa-and-objective-c

Another problem is: LSHIFT-down RSHIFT-down&up LSHIFT-up. The RSHIFT events wouldn't get caught.

Looks like I need to dip down into IOKit

Using IOHIDManager to Get Modifier Key Events
-> https://github.com/candera/khordr/blob/master/src/c/keygrab/hid-scratch.c

kEventRawKeyDown in:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/
Developer/SDKs/MacOSX10.10.sdk/System/Library/
Frameworks//Carbon.framework/Frameworks/HIToolbox.framework/
Headers/CarbonEvents.h

Resources

3-tier:

  • Cocoa/AppKit is a higher-level wrapper
  • Quartz takes the events from IOKit routes them to apps
    Note: NSEvent is built over Quartz Event

  • IOKit -- talks to the hardware

3-tier

Top Tier (Cocoa/AppKit)

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html -- this document is a must-see, and shows the above basic 3-tier architecture pic. However, it appears to only focus on the top tier (Cocoa/AppKit).

http://www.hcs.harvard.edu/~jrus/Site/Cocoa%20Text%20System.html <-- this article shows you 3 OSX config files that operate at an even higher level, letting you script your own mappings. This is documented here.

^ KeyBindingsEditor lets you make the above modifications.

Middle Tier (Quartz)

QuartzEventServicesRef

NSEvent -- specifically Creating Events

A couple of working code examples at this level. However, they all perform the same basic trick of receiving one virtual key code and emitting another. So you could use this technique for example to swap 'a' and 'z'.

Intercept keyboard input in OSX -- leads to Quartz Event Taps

Modify NSEvent to send a different key than the one that was pressed -- Dave De Long provide a working example, also using QET.

https://gist.github.com/wewearglasses/8313195 <-- "Global keyboard hook for OSX" -- another short working demo using QET.

Ukelele lets you choose which Unicode gets associated with a particular key, or Modifier+key. Doesn't allow remapping of modifiers, nor does it disambiguate left from right shift keys etc.

Keyboard input on OSX -- answer points towards addGlobalMonitorForEventsMatchingMask in NSEvent (in AppKit)

Base Tier (IOKit)

IOKitFundamentals <-- Also IOKit ("Interrupt Handling in the I/O Kit... Two types of events trigger an interrupt: ... Asynchronous events, such as keyboard presses")

https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Other_APIs/AH_Other_APIs.html

How can Mac OS X games receive low-level keyboard input events? <-- gamers are interested in this!

http://phrack.org/issues/66/16.html#article -- sometimes the hackers present things most clearly, haven't read through this yet. IOKit again, it seems.


More Links...

How do you implement global keyboard hooks in Mac OS X? <-- links to an article.

OSX: Detect system-wide keyDown events? <-- slightly OT as this is just to do with global monitoring, i.e. read-only.

http://www.codeitive.com/7QHSeVjPWq/where-can-i-find-kcgkeyboardeventkeycode-list-of-key-codes-for-cyrillic-language.html


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...