Sending virtual keystrokes on OS X


Going through my comments backlog I found a question asking how to send virtual keystrokes on OS X. Short answer is to use the CGEvent functions in MacApi.CoreGraphics – in a nutshell,

Call CGEventSourceCreate to allocate a CGEventSourceRef.
For each keystroke, create a CGEventRef by calling CGEventCreateKeyboardEvent; this takes the CGEventSourceRef just allocated plus an OS X virtual key code. For possible values of the latter, see the MacApi.KeyCodes unit, assuming you have a recent Delphi version, otherwise scan the source for KEY_xxx values. Also, note that you will need separate events for first pressing a key down, then unpressing it.
If required, call CGEventSetFlags on a given CGEventRef to assign a modifier key (alt/shift/ctrl/command); in the case of a character, you’ll typically want to call CGEventKeyboardSetUnicodeString as well rather than messing about trying to figure out the correct key code.
Actually perform the event by passing the CGEventRef to CGEventPost, using kCGHIDEventTap for the first parameter.
Clean up by passing each CGEventRef then the CGEventSourceRef to CFRelease.

The following follows these steps to generate key down and key up events for a Cmd+L shortcut:

System.SysUtils, MacApi.CocoaTypes, MacApi.CoreFoundation, MacApi.CoreGraphics, MacApi.KeyCodes;

procedure PressCmdLShortcut;
EventSourceRef: CGEventSourceRef;
KeyDownRef, KeyUpRef: CGEventRef;
KeyDownRef := nil;
KeyUpRef := nil;
EventSourceRef := CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if EventSourceRef = nil then RaiseLastOSError;
KeyDownRef := CGEventCreateKeyboardEvent(EventSourceRef, KEY_L, 1); //1 = key down
if KeyDownRef = nil then RaiseLastOSError;
CGEventSetFlags(KeyDownRef, kCGEventFlagMaskCommand);
KeyUpRef := CGEventCreateKeyboardEvent(EventSourceRef, KEY_L, 0); //0 = key up
if KeyUpRef = nil then RaiseLastOSError;
CGEventSetFlags(KeyUpRef, kCGEventFlagMaskCommand);
CGEventPost(kCGHIDEventTap, KeyDownRef);
CGEventPost(kCGHIDEventTap, KeyUpRef);
if KeyDownRef <> nil then CFRelease(KeyDownRef);
if KeyUpRef <> nil then CFRelease(KeyUpRef);

Alternatively, you can use a little cross platform (Mac/Windows) wrapper interface I’ve written… more on which in my next post!

Comments are closed.