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:
uses
System.SysUtils, MacApi.CocoaTypes, MacApi.CoreFoundation, MacApi.CoreGraphics, MacApi.KeyCodes;
procedure PressCmdLShortcut;
var
EventSourceRef: CGEventSourceRef;
KeyDownRef, KeyUpRef: CGEventRef;
begin
KeyDownRef := nil;
KeyUpRef := nil;
EventSourceRef := CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
if EventSourceRef = nil then RaiseLastOSError;
try
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);
finally
if KeyDownRef <> nil then CFRelease(KeyDownRef);
if KeyUpRef <> nil then CFRelease(KeyUpRef);
CFRelease(EventSourceRef);
end;
end;
Alternatively, you can use a little cross platform (Mac/Windows) wrapper interface I’ve written… more on which in my next post!