PureBasic Forum
https://www.purebasic.fr/english/

SendKey ?
https://www.purebasic.fr/english/viewtopic.php?f=19&t=49200
Page 1 of 1

Author:  Eidos [ Thu Feb 16, 2012 10:40 am ]
Post subject:  SendKey ?

Hi All,

This my first post :) --
Can you please tell, whether a SendKey analog exist in MAC OS?
Any OSX Api function to simulate keybd_event like in Windows?
Is it possible to do it in PureBasic for Mac?

Many thanks!

Author:  WilliamL [ Thu Feb 16, 2012 6:59 pm ]
Post subject:  Re: SendKey ?

What does it do?

viewtopic.php?f=19&t=46205

Author:  Shardik [ Thu Feb 16, 2012 7:07 pm ]
Post subject:  Re: SendKey ?

At first: welcome to the PureBasic Forum, Eidos... :wink:

What do you want to achieve?

If you want to simulate a key press into a StringGadget this can be
done very easily and cross-platform:
Code:
EnableExplicit

OpenWindow(0, 270, 100, 270, 70, "Simulate key press", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
StringGadget(0, 10, 10, WindowWidth(0) - 20, 20, "")
ButtonGadget(1, 30, 40, WindowWidth(0) - 60, 20, "Simulate pressing of <K> key")

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 1
        If EventType() = #PB_EventType_LeftClick
          SetGadgetText(0, GetGadgetText(0) + "K")
        EndIf
      EndIf
  EndSelect
ForEver

If you want to simulate a key press by generating an internal event,
things are becoming more complicated:
Code:
EnableExplicit

ImportC ""
  CreateEvent(MemoryAllocatorRef.L, EventClass.L, EventKind.L, EventTime.D, EventAttributes.L, *EventRef)
  SendEventToEventTargetWithOptions(EventRef.L, EventTargetRef.L, OptionBits.L)
EndImport

#kEventAttributeNone = 0
#kEventClassControl = 'cntl'
#kEventParamTextInputSendKeyboardEvent = 'tske'

Structure EventTypeSpec
  EventClass.L
  EventKind.L
EndStructure

ProcedureC ControlHandler(*NextEventHandler, EventRef.L, UserData.L)
  Protected Result.L

  If GetEventKind_(EventRef) = #kEventParamTextInputSendKeyboardEvent
    SetGadgetText(0, GetGadgetText(0) + "K")
  EndIf
EndProcedure

Dim EventTypes.EventTypeSpec(0)

Define EventRef.L
Define Result.L

OpenWindow(0, 270, 100, 270, 70, "Simulate key press", #PB_Window_SystemMenu | #PB_Window_SizeGadget)
StringGadget(0, 10, 10, WindowWidth(0) - 20, 20, "")
ButtonGadget(1, 30, 40, WindowWidth(0) - 60, 20, "Simulate pressing of <K> key")

EventTypes(0)\EventClass = #kEventClassControl
EventTypes(0)\EventKind = #kEventParamTextInputSendKeyboardEvent
InstallEventHandler_(GetControlEventTarget_(GadgetID(0)), @ControlHandler(), 1, @EventTypes(), 0, 0)

If CreateEvent(0, #kEventClassControl, #kEventParamTextInputSendKeyboardEvent, 0, #kEventAttributeNone, @EventRef)
  Debug "CreateEvent() failed!"
  End
EndIf

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 1
        If EventType() = #PB_EventType_LeftClick
          SendEventToEventTarget_(EventRef, GetControlEventTarget_(GadgetID(0)))
        EndIf
      EndIf
  EndSelect
ForEver

If you want to know how to detect the pressing of keys (and clicks onto
different mouse buttons) in a window (not as input into a StringGadget)
and display these actions in a TextGadget, then you might take a look
into this thread.

Author:  Eidos [ Fri Feb 17, 2012 10:41 am ]
Post subject:  Re: SendKey ?

Thank you for reply!

Actually, I'm writing a simple plugin for QuarkXpress and want to send key/word to edit window of QXP.
I've tried apple script, but that works too slowly and sometimes hanging the whole Qxp.
The solution was to make a simple application that stays on top and sending keystrokes to Quark on demand (pressing button)

The text is compiling inside the plugin and copied into the clipboard. Then switching to Qxp and simulating the APPLE + V (paste) operation. All wanted to do, is to simulate the Apple+V command.

here is the code where I'm trying to achieve this:
Code:
  Repeat
    evt = WaitWindowEvent()
  Select evt
    Case #PB_Event_CloseWindow
      quit = 1
    Case #PB_Event_Gadget
      eg = EventGadget()
      If eg >=0 And eg <=11
        SetClipboardText (GetGadgetText(eg)) ; put text into clipboard ...
        DisableWindow (0, 1) ; remove sticky option for dialog
        ; <<---- Here, simulate Apple+V keystroke
        DisableWindow (0, 0) ; back sticky option
      EndIf
   EndSelect   
  Until quit = 1 


I hope that your second solution will fit my requirements.
Many thanks!

Author:  WilliamL [ Fri Feb 17, 2012 5:56 pm ]
Post subject:  Re: SendKey ?

Well, how about that!?

Anyway, we got two routines from shardik for sending and getting keystrokes.

Thanks again shardik!

Author:  Shardik [ Sat Feb 18, 2012 1:46 pm ]
Post subject:  Re: SendKey ?

Eidos,

thank you for the description of your intended project. Unfortunately I
have no QuarkXpress to code a customized solution, but I have tried to
find a similar solution which starts TextEdit and sends keystrokes to be
displayed in TextEdit (or each other application which currently has the
keyboard focus). You have to send the virtual key codes and the modifier
key mask (for example if you have the need to press the Shift key
simultaneously). Perhaps you should modify the SendKey() procedure to
accept a normal ANSI character which will then of course have to be
translated to the corresponding virtual key code. Since you can call
SendKey() multiple times, there is no need to use the clipboard anymore... :wink:
Code:
EnableExplicit

#kCGHIDEventTap= 0

; ----- Virtual keycodes (defined in Events.h)

#kVK_ANSI_E = $0E
#kVK_ANSI_H = $04
#kVK_ANSI_L = $25
#kVK_ANSI_O = $1F
#kVK_Return = $24

; ----- Modifier key masks (defined in IOLLEvent.h)

#kCGEventFlagMaskShift     = $020000 ; = NX_SHIFTMASK
#kCGEventFlagMaskControl   = $040000 ; = NX_CONTROLMASK
#kCGEventFlagMaskAlternate = $080000 ; = NX_ALTERNATEMASK
#kCGEventFlagMaskCommand   = $100000 ; = NX_COMMANDMASK

ImportC ""
  CFRelease(CFTypeRef.L)
  CGEventCreateKeyboardEvent(CGEventSourceRef.L, CGVirtualKeyCode.U, KeyDown.L)
  CGEventPost(CGEventTapLocation.L, CGEventRef.L)
  CGEventSetFlags(CGEventRef.L, CGEventFlags.L)
EndImport

Procedure.L SendKey(VirtualKey.U, ModifierKey.L = 0)
  Protected KeyEvent.L

  KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #True)

  If KeyEvent
    ; ----- Press key
    CGEventSetFlags(KeyEvent, ModifierKey)
    CGEventPost(#kCGHIDEventTap, KeyEvent)
    CFRelease(KeyEvent)

    KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #False)
    CGEventSetFlags(KeyEvent, ModifierKey)

    If KeyEvent
      ; ----- Release key
      CGEventPost(#kCGHIDEventTap, KeyEvent)
      CFRelease(KeyEvent)
    EndIf
  EndIf

  ProcedureReturn KeyEvent
EndProcedure

Define Msg.S = "Hello!"
Define ProgramID.L

ProgramID = RunProgram("Open", "/Applications/TextEdit.app", "", #PB_Program_Open)

If ProgramID
  ; ----- Wait until TextEdit is running (increase delay if necessary)
  Delay(100)
  ; ----- Send Keys to TextEdit
  SendKey(#kVK_ANSI_H, #kCGEventFlagMaskShift)
  SendKey(#kVK_ANSI_E)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_O)
  SendKey(#kVK_Return)
  CloseProgram(ProgramID)
EndIf

Author:  Eidos [ Sun Feb 19, 2012 1:04 pm ]
Post subject:  Re: SendKey ?

Hi Shardik,

Many thanks for code! That's really useful piece of code.
I came from Windows world and just started with Mac using PB.

I have faced with strange clipboard behavior in PB before implementing your senkey solution in mac.
It seems that SetClipboardText doesn't work properly on my Mac OSX.
The application can put into clipboard text and then read it back. SetClipboardText and GetClipboatdText works fine within the application, BUT SHOW Clipboard operation inside the FINDER doesn't show anything...

Simple text editor, safari and mac Word are showing my entries via APPLE+V (paste from clipboard).

Also, I've tried to copy the text from PB IDE and paste somewhere else: that worked! even showed up in SHOW Clipboard and QuarkXPress, but no luck when using Set/Get clipboard operations.

Can some try to run simple CLIPBOARD example on Mac OS (use only SetClipboardText) and check whether text visible or not in Show Clipboard window.

That's really strange and makes my development efforts useless....

Here is my mac configuration:
Powermac G4, MacOS X 10.4.9. (8p 135), Darwin 8.9.0
QuarkXpress v6.1 PPC

Author:  WilliamL [ Sun Feb 19, 2012 6:45 pm ]
Post subject:  Re: SendKey ?

Works ok for me. I've been using the clipboard copy/paste in a couple of apps and it has been working for me. When I look in 'Show clipboard', from the finder, it's there... ran example and cmd+V when in TextEdit and it pasted correctly. (only tried the text - canceled the pict load)

Code:
; ------------------------------------------------------------
;
;   PureBasic - Clipboard example file
;
;    (c) Fantaisie Software
;
; ------------------------------------------------------------
;

; Paste text to the clipboard..
SetClipboardText("PureBasic pasted text example.")

; Display the contents of the clipboard...
MessageRequester("Info", "Text in the clipboard"+#LF$+"'"+GetClipboardText()+"'", 0)

; Now paste an image...

If LoadImage(0, OpenFileRequester("Load a picture","","Bitmap (*.bmp)|*.bmp",0))
  If SetClipboardImage(0)

    MessageRequester("Info", "Image correctly pasted to the clipboard."+Chr(10)+"Open 'Paint' to test it !", 0)
  EndIf
EndIf

Author:  jvzuck [ Sat Aug 22, 2020 4:24 am ]
Post subject:  Re: SendKey ?

Shardik,
Do you believe this code should still work under Catalina? I'm getting an "invalid memory access." That make sense?
Jonathan

Shardik wrote:
Eidos,

thank you for the description of your intended project. Unfortunately I
have no QuarkXpress to code a customized solution, but I have tried to
find a similar solution which starts TextEdit and sends keystrokes to be
displayed in TextEdit (or each other application which currently has the
keyboard focus). You have to send the virtual key codes and the modifier
key mask (for example if you have the need to press the Shift key
simultaneously). Perhaps you should modify the SendKey() procedure to
accept a normal ANSI character which will then of course have to be
translated to the corresponding virtual key code. Since you can call
SendKey() multiple times, there is no need to use the clipboard anymore... :wink:
Code:
EnableExplicit

#kCGHIDEventTap= 0

; ----- Virtual keycodes (defined in Events.h)

#kVK_ANSI_E = $0E
#kVK_ANSI_H = $04
#kVK_ANSI_L = $25
#kVK_ANSI_O = $1F
#kVK_Return = $24

; ----- Modifier key masks (defined in IOLLEvent.h)

#kCGEventFlagMaskShift     = $020000 ; = NX_SHIFTMASK
#kCGEventFlagMaskControl   = $040000 ; = NX_CONTROLMASK
#kCGEventFlagMaskAlternate = $080000 ; = NX_ALTERNATEMASK
#kCGEventFlagMaskCommand   = $100000 ; = NX_COMMANDMASK

ImportC ""
  CFRelease(CFTypeRef.L)
  CGEventCreateKeyboardEvent(CGEventSourceRef.L, CGVirtualKeyCode.U, KeyDown.L)
  CGEventPost(CGEventTapLocation.L, CGEventRef.L)
  CGEventSetFlags(CGEventRef.L, CGEventFlags.L)
EndImport

Procedure.L SendKey(VirtualKey.U, ModifierKey.L = 0)
  Protected KeyEvent.L

  KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #True)

  If KeyEvent
    ; ----- Press key
    CGEventSetFlags(KeyEvent, ModifierKey)
    CGEventPost(#kCGHIDEventTap, KeyEvent)
    CFRelease(KeyEvent)

    KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #False)
    CGEventSetFlags(KeyEvent, ModifierKey)

    If KeyEvent
      ; ----- Release key
      CGEventPost(#kCGHIDEventTap, KeyEvent)
      CFRelease(KeyEvent)
    EndIf
  EndIf

  ProcedureReturn KeyEvent
EndProcedure

Define Msg.S = "Hello!"
Define ProgramID.L

ProgramID = RunProgram("Open", "/Applications/TextEdit.app", "", #PB_Program_Open)

If ProgramID
  ; ----- Wait until TextEdit is running (increase delay if necessary)
  Delay(100)
  ; ----- Send Keys to TextEdit
  SendKey(#kVK_ANSI_H, #kCGEventFlagMaskShift)
  SendKey(#kVK_ANSI_E)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_O)
  SendKey(#kVK_Return)
  CloseProgram(ProgramID)
EndIf

Author:  mk-soft [ Sat Aug 22, 2020 8:33 am ]
Post subject:  Re: SendKey ?

Code is write for x86.

You mast change the var type long to integer ...
Code:
EnableExplicit

#kCGHIDEventTap= 0

; ----- Virtual keycodes (defined in Events.h)

#kVK_ANSI_E = $0E
#kVK_ANSI_H = $04
#kVK_ANSI_L = $25
#kVK_ANSI_O = $1F
#kVK_Return = $24

; ----- Modifier key masks (defined in IOLLEvent.h)

#kCGEventFlagMaskShift     = $020000 ; = NX_SHIFTMASK
#kCGEventFlagMaskControl   = $040000 ; = NX_CONTROLMASK
#kCGEventFlagMaskAlternate = $080000 ; = NX_ALTERNATEMASK
#kCGEventFlagMaskCommand   = $100000 ; = NX_COMMANDMASK

ImportC ""
  CFRelease(CFTypeRef.i)
  CGEventCreateKeyboardEvent(CGEventSourceRef.i, CGVirtualKeyCode.U, KeyDown.i)
  CGEventPost(CGEventTapLocation.i, CGEventRef.i)
  CGEventSetFlags(CGEventRef.i, CGEventFlags.i)
EndImport

Procedure.i SendKey(VirtualKey.U, ModifierKey.i = 0)
  Protected KeyEvent.i

  KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #True)

  If KeyEvent
    ; ----- Press key
    CGEventSetFlags(KeyEvent, ModifierKey)
    CGEventPost(#kCGHIDEventTap, KeyEvent)
    CFRelease(KeyEvent)

    KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #False)
    CGEventSetFlags(KeyEvent, ModifierKey)

    If KeyEvent
      ; ----- Release key
      CGEventPost(#kCGHIDEventTap, KeyEvent)
      CFRelease(KeyEvent)
    EndIf
  EndIf

  ProcedureReturn KeyEvent
EndProcedure

Define Msg.S = "Hello!"
Define ProgramID.i

ProgramID = RunProgram("Open", "/Applications/TextEdit.app", "", #PB_Program_Open)

If ProgramID
  ; ----- Wait until TextEdit is running (increase delay if necessary)
  Delay(100)
  ; ----- Send Keys to TextEdit
  SendKey(#kVK_ANSI_H, #kCGEventFlagMaskShift)
  SendKey(#kVK_ANSI_E)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_O)
  SendKey(#kVK_Return)
  CloseProgram(ProgramID)
EndIf

Author:  Shardik [ Sat Aug 22, 2020 4:39 pm ]
Post subject:  Re: SendKey ?

jvzuck wrote:
Shardik,
Do you believe this code should still work under Catalina? I'm getting an "invalid memory access."

Unfortunately my old code from 2012 doesn't work anymore on MacOS Mojave and Catalina. It still works up to High Sierra (for x64 compilation you have to make the changes proposed by mk-soft).

In Catalina it seems not possible anymore to start TextEdit with RunProgram(). But it is still possible to start an application like TextEdit with launchApplication:. Unfortunately launchApplication: is deprecated in Big Sur but still working in Catalina. Therefore I have modified my old example from 2012 to also work in Catalina with PB 5.72 x64:
Code:
EnableExplicit

#kCGHIDEventTap= 0

; ----- Virtual keycodes (defined in Events.h)

#kVK_ANSI_E = $0E
#kVK_ANSI_H = $04
#kVK_ANSI_L = $25
#kVK_ANSI_O = $1F
#kVK_Return = $24

; ----- Modifier key masks (defined in IOLLEvent.h)

#kCGEventFlagMaskShift     = $020000 ; = NX_SHIFTMASK
#kCGEventFlagMaskControl   = $040000 ; = NX_CONTROLMASK
#kCGEventFlagMaskAlternate = $080000 ; = NX_ALTERNATEMASK
#kCGEventFlagMaskCommand   = $100000 ; = NX_COMMANDMASK

ImportC ""
  CFRelease(CFTypeRef.I)
  CGEventCreateKeyboardEvent(CGEventSourceRef.I, CGVirtualKeyCode.U, KeyDown.L)
  CGEventPost(CGEventTapLocation.I, CGEventRef.I)
  CGEventSetFlags(CGEventRef.I, CGEventFlags.L)
EndImport

Procedure.L SendKey(VirtualKey.U, ModifierKey.L = 0)
  Protected KeyEvent.I

  KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #True)

  If KeyEvent
    ; ----- Press key
    CGEventSetFlags(KeyEvent, ModifierKey)
    CGEventPost(#kCGHIDEventTap, KeyEvent)
    CFRelease(KeyEvent)

    KeyEvent = CGEventCreateKeyboardEvent(0, VirtualKey, #False)
    CGEventSetFlags(KeyEvent, ModifierKey)

    If KeyEvent
      ; ----- Release key
      CGEventPost(#kCGHIDEventTap, KeyEvent)
      CFRelease(KeyEvent)
    EndIf
  EndIf

  ProcedureReturn KeyEvent
EndProcedure

Define Msg.S = "Hello!"
Define ProgramID.I

If CocoaMessage(0, CocoaMessage(0, 0, "NSWorkspace sharedWorkspace"),
  "launchApplication:$", @"TextEdit")
  ; ----- Wait until TextEdit is running (increase delay if necessary)
  Delay(500)
  ; ----- Send Keys to TextEdit
  SendKey(#kVK_ANSI_H, #kCGEventFlagMaskShift)
  SendKey(#kVK_ANSI_E)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_L)
  SendKey(#kVK_ANSI_O)
  SendKey(#kVK_Return)
EndIf

Author:  jvzuck [ Sat Aug 22, 2020 7:27 pm ]
Post subject:  Re: SendKey ?

So great! Thanks!
Where are the include files of which you speak: events.h and IOLLEvent.h

Presumably, if I wanted to do Command+Shit, I would just AND them, right?
Jonathan

Author:  jvzuck [ Sat Aug 22, 2020 8:02 pm ]
Post subject:  Re: SendKey ?

I found the include files online but it's strange I cannot find them on my Mac, having installed XCode and all.

Final question, your code bring the target application to the foreground. Is there a way to send keys to an application in the background. Let's say you're running a Keynote presentation and you want to log into TextEdit the timecodes each time you change slides. Ideally, you wouldn't bring TextEdit to the foreground to do it. That make sense?
Jonathan

Author:  Shardik [ Sat Aug 22, 2020 8:49 pm ]
Post subject:  Re: SendKey ?

jvzuck wrote:
Where are the include files of which you speak: events.h and IOLLEvent.h
jvzuck wrote:
I found the include files online but it's strange I cannot find them on my Mac, having installed XCode and all.

The Carbon framework (Events.h is a part of it) has been declared deprecated by Apple already for many years. Carbon was officially discontinued and removed entirely with the release of MacOS 10.15 Catalina. Nevertheless you still find the header files in the Xcode app:
- Events.h:
/Applications/Xcode.app/Contents/Developer/Platform/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
- IOLLEvent.h:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/IOKit/hidsystem/IOLLEvent.h

Alternatively you find them on opensource.apple.com
- IOLLEvent.h
and GitHub:
- Events.h

jvzuck wrote:
Presumably, if I wanted to do Command+Shit, I would just AND them, right?

You may combine 2 modifier keys in the second parameter of SendKey() by using the OR operator "|", not the AND operator "&" which would clear each flag! Just try
Code:
Debug 1 & 2 ; Displays 0
Debug 1 | 2 ; Displays 3

Author:  jvzuck [ Sun Aug 23, 2020 1:37 pm ]
Post subject:  Re: SendKey ?

Yes, of course. Thanks. My programming skills are VERY rusty!

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/