Global keyboard hook
-
- Enthusiast
- Posts: 346
- Joined: Thu Jul 02, 2009 5:42 am
Global keyboard hook
Is it possible to create a global keyboard hook for a mac application?
I know it works in Windows if you use a DLL, but not sure about the Mac.
I know it works in Windows if you use a DLL, but not sure about the Mac.
Re: Global keyboard hook
What do you intend to do ?
-
- Enthusiast
- Posts: 346
- Joined: Thu Jul 02, 2009 5:42 am
Re: Global keyboard hook
Hi Wilbert,
I have a program that is invisible and takes screen shots at a certain period.
I need to activate (Bring the screen up) the program using a key sequence, I have it working in Windows
with a global keyboard hook (dll), but not sure how to do it on the Mac.
I have a program that is invisible and takes screen shots at a certain period.
I need to activate (Bring the screen up) the program using a key sequence, I have it working in Windows
with a global keyboard hook (dll), but not sure how to do it on the Mac.
Re: Global keyboard hook
I think you will need the API function CGEventTapCreate to make it work on both x86 and x64.
Unfortunately I don't know exactly how to make it work.
Unfortunately I don't know exactly how to make it work.
Re: Global keyboard hook
The simplest solution is to use the HotKey API functions of Carbon. Unfortunately there doesn't exist a comparably simple and efficient Cocoa API solution. Most Cocoa examples simply wrap the Carbon API functions. Therefore I have done the same in the following example which I tested successfully on Mac 10.6.8 (Snow Leopard) and 10.8.2 (Mountain Lion) with Cocoa x86 and x64 and subsystem Carbon in both ASCII and Unicode mode.spacebuddy wrote:Is it possible to create a global keyboard hook for a mac application?
The example code starts an invisible window and waits for the hot key combination Shift+Control+Space. When detected it makes the window visible. You may define several different hot keys which are handled in the HotKeyHandler() callback. The 4 byte signature of a hot key is user-defined and may be changed at will.
Code: Select all
EnableExplicit
#kEventClassKeyboard = $6B657962 ; 'keyb'
#kEventHotKeyPressed = 5
#kEventParamDirectObject = $2D2D2D2D ; '----'
#typeEventHotKeyID = $686B6964 ; 'hkid'
#controlKeyBit = 12
#controlKey = 1 << #controlKeyBit
#shiftKeyBit = 9
#shiftKey = 1 << #shiftKeyBit
ImportC ""
GetApplicationEventTarget()
RegisterEventHotKey(HotKeyCode.L, HotKeyModifiers.L, HotKeyID.Q, EventTargetRef.I, OptionBits.L, *EventHotKeyRef)
UnregisterEventHotKey(EventHotKeyRef.I)
EndImport
Structure EventTypeSpec
EventClass.L
EventKind.L
EndStructure
Structure EventHotKeyID
Signature.L
ID.L
EndStructure
ProcedureC HotKeyHandler(NextHandlerRef.I, EventRef.I, *UserData)
Protected HotKey.Q
If GetEventParameter_(EventRef, #kEventParamDirectObject, #typeEventHotKeyID, 0, SizeOf(EventHotKeyID), 0, @HotKey) = 0
If PeekL(HotKey) = $68746B31 ; HotKey\Signature = 'htk1' ?
If PeekL(HotKey + 4) = 1 ; HotKey\ID = 1 ?
HideWindow(0, #False)
EndIf
EndIf
EndIf
EndProcedure
Define HotKey.EventHotKeyID
Define HotKeyRef.I
Dim EventTypes.EventTypeSpec(0)
EventTypes(0)\EventClass = #kEventClassKeyboard
EventTypes(0)\EventKind = #kEventHotKeyPressed
HotKey\Signature = $68746B31 ; 'htk1'
HotKey\ID = 1
OpenWindow(0, 270, 100, 220, 70, "Activated by HotKey", #PB_Window_SystemMenu | #PB_Window_Invisible)
; ----- Install EventHandler to react to pressing of hot key(s)
If InstallEventHandler_(GetApplicationEventTarget(), @HotKeyHandler(), 1, @EventTypes(), 0, 0) = 0
; ----- Register hot key (Control+Shift+Space)
RegisterEventHotKey(49, #controlKey | #shiftKey, @HotKey, GetApplicationEventTarget(), 0, @HotKeyRef)
EndIf
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
; ----- You do not need to unregister a hot key when your application
; terminates; the system takes care of that for you. You can use this
; function if the user changes a hot key for something in your
; application - you would unregister the previous key and register your
; new key.
UnregisterEventHotKey(HotKeyRef)
-
- Enthusiast
- Posts: 346
- Joined: Thu Jul 02, 2009 5:42 am
Re: Global keyboard hook
Thanks Shardik, it works perfectly
Re: Global keyboard hook
I found JNativeHook java library http://code.google.com/p/jnativehook/
Is it really set global hook under MacOSX?
Is it really set global hook under MacOSX?
Re: Global keyboard hook
Thank you for your interesting link! I downloaded the sourcecode of JNativeHook and took a look into /JNativeHook/src/native/osx/NativeThread.c. They used the API functions CGEventMaskBit() to setup the event mask to listen for and CGEventTapCreate() (just like wilbert had proposed).nikoniko wrote:I found JNativeHook java library http://code.google.com/p/jnativehook/
Yes, JNativeHook sets up a global hook, just like my PureBasic example which is much simpler by using higher level Carbon API functions to reduce the lines of code you have to use...nikoniko wrote:Is it really set global hook under MacOSX?
- Andre
- PureBasic Team
- Posts: 2056
- Joined: Fri Apr 25, 2003 6:14 pm
- Location: Germany (Saxony, Deutscheinsiedel)
- Contact:
Re: Global keyboard hook
@shardik: your example works well on MacOS 10.5.8 too, thank you!
Re: Global keyboard hook
Your example is simple but doesn't track all keystrokes (ok, most of them).Shardik wrote:Yes, JNativeHook sets up a global hook, just like my PureBasic example which is much simpler by using higher level Carbon API functions to reduce the lines of code you have to use...
It is useful for advanced tool to capture ctrl, shift, etc keys.
Re: Global keyboard hook
Seems it isn't hard for macosx guru, but not for me yet.
Add 2 links (use this topic as bookmark)
Log keydowns and mouse buttons
http://forums.appleinsider.com/t/7872/k ... r-mac-os-x
Receiving, Filtering, and Modifying Mouse Events
http://www.osxbook.com/book/bonus/chapter2/altermouse/
And key events
http://www.osxbook.com/book/bonus/chapter2/alterkeys/
Add 2 links (use this topic as bookmark)
Log keydowns and mouse buttons
http://forums.appleinsider.com/t/7872/k ... r-mac-os-x
Receiving, Filtering, and Modifying Mouse Events
http://www.osxbook.com/book/bonus/chapter2/altermouse/
And key events
http://www.osxbook.com/book/bonus/chapter2/alterkeys/
Re: Global keyboard hook
Did you see this post ?
http://www.purebasic.fr/english/viewtop ... 25#p411225
http://www.purebasic.fr/english/viewtop ... 25#p411225
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: Global keyboard hook
Code: Select all
EnableExplicit
#KeyDownMask = 1 << 10
#KeyUpMask = 1 << 11
#FlagsChangedMask = 1 << 12
#kCGKeyboardEventKeycode = 9
ImportC ""
CGEventTapCreate(tap, place, options, eventsOfInterest.q, callback, refcon)
CGEventGetIntegerValueField.q(event, field)
CGEventKeyboardGetUnicodeString(event, maxStringLength, *actualStringLength, *unicodeString)
EndImport
Define eventTap
ProcedureC eventTapFunction(proxy, type, event, refcon)
Protected keyCode = CGEventGetIntegerValueField(event, #kCGKeyboardEventKeycode)
Protected unicodeBuffer.q, bufferLen.i = 4
CGEventKeyboardGetUnicodeString(event, bufferLen, @bufferLen, @unicodeBuffer)
Protected text.s = PeekS(@unicodeBuffer, bufferLen, #PB_Unicode)
SetGadgetText(0, "keyCode : " + Str(keyCode) + #LF$ + "String : " + text)
EndProcedure
If OpenWindow(0, 0, 0, 220, 70, "keyCode tap", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StickyWindow(0, 1)
StringGadget(0, 10, 10, 200, 50, "")
eventTap = CGEventTapCreate(0, 0, 1, #KeyDownMask | #FlagsChangedMask, @eventTapFunction(), 0)
If eventTap
CocoaMessage(0, CocoaMessage(0, 0, "NSRunLoop currentRunLoop"), "addPort:", eventTap, "forMode:$", @"kCFRunLoopDefaultMode")
EndIf
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: Global keyboard hook
Wilbert +100
While I am writing reply about my attempts to change your code for all processes and flags keys you are posting fully working code for me.
Thanks, thanks, thanks.
While I am writing reply about my attempts to change your code for all processes and flags keys you are posting fully working code for me.
Thanks, thanks, thanks.
Re: Global keyboard hook
Here's a modification also showing the keyboard related flags
And a practical example of responding to the key combination [fn] + [esc]
Code: Select all
EnableExplicit
#KeyDownMask = 1 << 10
#KeyUpMask = 1 << 11
#FlagsChangedMask = 1 << 12
#kCGKeyboardEventKeycode = 9
ImportC ""
CGEventTapCreate(tap, place, options, eventsOfInterest.q, callback, refcon)
CGEventGetFlags.q(event)
CGEventGetIntegerValueField.q(event, field)
CGEventKeyboardGetUnicodeString(event, maxStringLength, *actualStringLength, *unicodeString)
EndImport
Define eventTap
ProcedureC eventTapFunction(proxy, type, event, refcon)
Protected keyCode = CGEventGetIntegerValueField(event, #kCGKeyboardEventKeycode)
Protected keyFlags = CGEventGetFlags(event) >> 16 & 255
Protected unicodeBuffer.q, bufferLen.i = 4
CGEventKeyboardGetUnicodeString(event, bufferLen, @bufferLen, @unicodeBuffer)
Protected text.s = PeekS(@unicodeBuffer, bufferLen, #PB_Unicode)
SetGadgetText(0, "keyCode : " + Str(keyCode) + #LF$ + "keyFlags : " + RSet(Bin(keyFlags), 8, "0") + #LF$ + "string : " + text)
EndProcedure
If OpenWindow(0, 0, 0, 220, 90, "keyCode tap", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StickyWindow(0, 1)
StringGadget(0, 10, 10, 200, 70, "")
eventTap = CGEventTapCreate(0, 0, 1, #KeyDownMask | #FlagsChangedMask, @eventTapFunction(), 0)
If eventTap
CocoaMessage(0, CocoaMessage(0, 0, "NSRunLoop currentRunLoop"), "addPort:", eventTap, "forMode:$", @"kCFRunLoopDefaultMode")
EndIf
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Code: Select all
EnableExplicit
#KeyDownMask = 1 << 10
#FlagsChangedMask = 1 << 12
#kCGKeyboardEventKeycode = 9
ImportC ""
CGEventTapCreate(tap, place, options, eventsOfInterest.q, callback, refcon)
CGEventGetFlags.q(event)
CGEventGetIntegerValueField.q(event, field)
EndImport
Define eventTap
Global SpeechSynthesizer = CocoaMessage(0, CocoaMessage(0, 0, "NSSpeechSynthesizer alloc"), "initWithVoice:", #nil)
ProcedureC eventTapFunction(proxy, type, event, refcon)
Protected keyCode = CGEventGetIntegerValueField(event, #kCGKeyboardEventKeycode)
Protected keyFlags = CGEventGetFlags(event) >> 16 & 255
If keyCode = 53 And keyFlags & $80
; [fn] + [esc] pressed
CocoaMessage(0, SpeechSynthesizer, "startSpeakingString:$", @"fn and esc pressed")
EndIf
EndProcedure
If OpenWindow(0, 0, 0, 200, 30, "key tap", #PB_Window_SystemMenu | #PB_Window_Minimize | #PB_Window_NoActivate)
eventTap = CGEventTapCreate(0, 0, 1, #KeyDownMask | #FlagsChangedMask, @eventTapFunction(), 0)
If eventTap
CocoaMessage(0, CocoaMessage(0, 0, "NSRunLoop currentRunLoop"), "addPort:", eventTap, "forMode:$", @"kCFRunLoopDefaultMode")
EndIf
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)