It is currently Thu Sep 19, 2019 12:00 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Global keyboard hook
PostPosted: Mon Jan 28, 2013 6:47 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Jul 02, 2009 5:42 am
Posts: 327
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Mon Jan 28, 2013 6:56 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3436
Location: Netherlands
What do you intend to do ?


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Mon Jan 28, 2013 7:40 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Jul 02, 2009 5:42 am
Posts: 327
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Mon Jan 28, 2013 8:40 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3436
Location: Netherlands
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Sun Feb 03, 2013 9:33 am 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1648
Location: Germany
spacebuddy wrote:
Is it possible to create a global keyboard hook for a mac application?

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.

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:
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)


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Mon Feb 04, 2013 4:11 am 
Offline
Enthusiast
Enthusiast

Joined: Thu Jul 02, 2009 5:42 am
Posts: 327
Thanks Shardik, it works perfectly :D :D


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Tue Oct 15, 2013 3:39 pm 
Offline
User
User

Joined: Fri Nov 11, 2011 7:58 am
Posts: 20
I found JNativeHook java library http://code.google.com/p/jnativehook/
Is it really set global hook under MacOSX?


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Tue Oct 15, 2013 7:06 pm 
Offline
Addict
Addict
User avatar

Joined: Thu Apr 21, 2005 2:38 pm
Posts: 1648
Location: Germany
nikoniko wrote:
I found JNativeHook java library http://code.google.com/p/jnativehook/
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:
Is it really set global hook under MacOSX?
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... :wink:


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Tue Oct 15, 2013 10:29 pm 
Offline
PureBasic Team
PureBasic Team
User avatar

Joined: Fri Apr 25, 2003 6:14 pm
Posts: 1701
Location: Germany (Saxony, Deutscheinsiedel)
@shardik: your example works well on MacOS 10.5.8 too, thank you! :D

_________________
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 6:27 am 
Offline
User
User

Joined: Fri Nov 11, 2011 7:58 am
Posts: 20
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... :wink:


Your example is simple but doesn't track all keystrokes (ok, most of them).

It is useful for advanced tool to capture ctrl, shift, etc keys.


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 6:40 am 
Offline
User
User

Joined: Fri Nov 11, 2011 7:58 am
Posts: 20
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/


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 8:05 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3436
Location: Netherlands
Did you see this post ?
viewtopic.php?p=411225#p411225

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 8:20 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3436
Location: Netherlands
Code:
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

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 8:54 am 
Offline
User
User

Joined: Fri Nov 11, 2011 7:58 am
Posts: 20
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.


Top
 Profile  
Reply with quote  
 Post subject: Re: Global keyboard hook
PostPosted: Fri Oct 18, 2013 9:09 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3436
Location: Netherlands
Here's a modification also showing the keyboard related flags
Code:
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


And a practical example of responding to the key combination [fn] + [esc]
Code:
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

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 24 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye