| 1 | // Posts input to the logged-in session as the HID system would, for driving the app |
| 2 | // over ssh: `input click X Y`, `input double X Y`, `input drag X Y X2 Y2`, |
| 3 | // `input scroll X Y LINES`, `input type TEXT`, |
| 4 | // `input key KEYCODE [command] [shift] [option] [control]`. |
| 5 | // Points are the display's, from its top left. |
| 6 | #include <ApplicationServices/ApplicationServices.h> |
| 7 | #include <stdlib.h> |
| 8 | #include <string.h> |
| 9 | #include <unistd.h> |
| 10 | |
| 11 | static void post(CGEventRef event) { |
| 12 | CGEventPost(kCGHIDEventTap, event); |
| 13 | CFRelease(event); |
| 14 | usleep(20000); |
| 15 | } |
| 16 | |
| 17 | static void mouse(CGEventType type, CGPoint at, int clicks) { |
| 18 | CGEventRef event = CGEventCreateMouseEvent(NULL, type, at, kCGMouseButtonLeft); |
| 19 | CGEventSetIntegerValueField(event, kCGMouseEventClickState, clicks); |
| 20 | post(event); |
| 21 | } |
| 22 | |
| 23 | static void click(CGPoint at, int clicks) { |
| 24 | mouse(kCGEventLeftMouseDown, at, clicks); |
| 25 | mouse(kCGEventLeftMouseUp, at, clicks); |
| 26 | } |
| 27 | |
| 28 | static void key(CGKeyCode code, CGEventFlags flags, const UniChar *text, UniCharCount length) { |
| 29 | for (int down = 1; down >= 0; down--) { |
| 30 | CGEventRef event = CGEventCreateKeyboardEvent(NULL, code, down); |
| 31 | CGEventSetFlags(event, flags); |
| 32 | if (length) CGEventKeyboardSetUnicodeString(event, length, text); |
| 33 | post(event); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | int main(int argc, char **argv) { |
| 38 | if ((argc == 4 && !strcmp(argv[1], "click")) || (argc == 4 && !strcmp(argv[1], "double"))) { |
| 39 | CGPoint at = CGPointMake(atof(argv[2]), atof(argv[3])); |
| 40 | mouse(kCGEventMouseMoved, at, 0); |
| 41 | click(at, 1); |
| 42 | if (!strcmp(argv[1], "double")) click(at, 2); |
| 43 | } else if (argc == 6 && !strcmp(argv[1], "drag")) { |
| 44 | CGPoint from = CGPointMake(atof(argv[2]), atof(argv[3])); |
| 45 | CGPoint to = CGPointMake(atof(argv[4]), atof(argv[5])); |
| 46 | mouse(kCGEventMouseMoved, from, 0); |
| 47 | mouse(kCGEventLeftMouseDown, from, 1); |
| 48 | for (int step = 1; step <= 10; step++) { |
| 49 | CGPoint at = CGPointMake(from.x + (to.x - from.x) * step / 10, from.y + (to.y - from.y) * step / 10); |
| 50 | mouse(kCGEventLeftMouseDragged, at, 1); |
| 51 | } |
| 52 | mouse(kCGEventLeftMouseUp, to, 1); |
| 53 | } else if (argc == 5 && !strcmp(argv[1], "scroll")) { |
| 54 | CGPoint at = CGPointMake(atof(argv[2]), atof(argv[3])); |
| 55 | mouse(kCGEventMouseMoved, at, 0); |
| 56 | CGEventRef event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 1, atoi(argv[4])); |
| 57 | CGEventSetLocation(event, at); |
| 58 | post(event); |
| 59 | } else if (argc == 3 && !strcmp(argv[1], "type")) { |
| 60 | CFStringRef text = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8); |
| 61 | for (CFIndex i = 0; i < CFStringGetLength(text); i++) { |
| 62 | UniChar c = CFStringGetCharacterAtIndex(text, i); |
| 63 | key(0, 0, &c, 1); |
| 64 | } |
| 65 | CFRelease(text); |
| 66 | } else if (argc >= 3 && !strcmp(argv[1], "key")) { |
| 67 | CGEventFlags flags = 0; |
| 68 | for (int i = 3; i < argc; i++) { |
| 69 | if (!strcmp(argv[i], "command")) flags |= kCGEventFlagMaskCommand; |
| 70 | else if (!strcmp(argv[i], "shift")) flags |= kCGEventFlagMaskShift; |
| 71 | else if (!strcmp(argv[i], "option")) flags |= kCGEventFlagMaskAlternate; |
| 72 | else if (!strcmp(argv[i], "control")) flags |= kCGEventFlagMaskControl; |
| 73 | } |
| 74 | key(atoi(argv[2]), flags, NULL, 0); |
| 75 | } else { |
| 76 | fprintf(stderr, "usage: input click|double X Y | drag X Y X2 Y2 | scroll X Y LINES | type TEXT | key KEYCODE [command] [shift] [option] [control]\n"); |
| 77 | return 2; |
| 78 | } |
| 79 | return 0; |
| 80 | } |