1

I'm writing an application to respond on a hotkey by copying highlighted text into NSPasteboard's generalPasteboard. After looking around here for a solution for sending virtual keystrokes, I found this: How to send a "Cmd-C" keystroke to the active application in objective-c, or tell the application to do a copy operation?

I tried the applescript suggested with NSAppleScript:

NSLog(@"Hotkey Pressed");
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; 

NSAppleScript *playScript;
playScript = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to keystroke \"c\" using command down"];

if([playScript isCompiled] == NO){
[playScript compileAndReturnError:nil];
}

id exerror = [playScript executeAndReturnError:nil];

if(exerror == nil){
 NSLog(@"Script Failed");
}

It works, but only on the first time I hit the hotkey. Each subsequent hit will not grab the highlighted text. The generalPasteboard still contains the same contents as before the script is run again. Clearing the generalPasteboard before I run the code is no use, because then the code fails when attempting to read the pasteboard contents. Here's the log:

 Pastify[16929:a0b] woooooooo! I'm being CALLED
 Pastify[16929:a0b] Hotkey Pressed
 Pastify[16929:a0b] Error loading /Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types:  dlopen(/Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types, 262): no suitable image found.  Did find:
    /Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types: no matching architecture in universal wrapper
 Pastify: OpenScripting.framework - scripting addition "/Library/ScriptingAdditions/Adobe Unit Types.osax" declares no loadable handlers.
 Pastify[16929:a0b] Size of copiedItems: 1
 Pastify[16929:a0b] Content of paste: 2010-04-19 03:41:04.355 Pastify[16929:a0b] woooooooo! I'm being CALLED
 Pastify[16929:a0b] Hotkey Pressed
 Pastify[16929:a0b] Size of copiedItems: 1
 Pastify[16929:a0b] Content of paste: 2010-04-19 03:41:04.355 Pastify[16929:a0b] woooooooo! I'm being CALLED
 Pastify[16929:a0b] Hotkey Pressed
 Pastify[16929:a0b] Size of copiedItems: 1
 Pastify[16929:a0b] Content of paste: 2010-04-19 03:41:04.355 Pastify[16929:a0b] woooooooo! I'm being CALLED

So I tried the next suggested solution:

 CFRelease(CGEventCreate(NULL));

 CGEventRef event1, event2, event3, event4;
 event1 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)50, true);
 event2 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)8, true);
 event3 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)8, false);
 event4 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)50, false);

 CGEventPost(kCGHIDEventTap, event1);
 CGEventPost(kCGHIDEventTap, event2);
 CGEventPost(kCGHIDEventTap, event3);
 CGEventPost(kCGHIDEventTap, event4);

The above should send the keystrokes Command + c, but all I get is a beep, and the pasteboard contents are unchanged.

I'm at wits end - can anyone enlighten me as to what I'm missing or point me out to what I'm overlooking for something so simple?

Community
  • 1
  • 1
bhargav
  • 164
  • 8
  • 3
    Don't pass `error:nil`. For one thing, it takes a pointer to a pointer variable, not a pointer to an object, so the correct constant would be `NULL`, not `nil`. More importantly, don't prevent the frameworks from telling you what went wrong. Pass a pointer to an NSError *variable, and if what you tried to do fails, at least log the error object. – Peter Hosey Apr 19 '10 at 17:51

3 Answers3

1

Just a thought... the cmd-c trick works on the frontmost application. I suspect that the first time you run the hotkey to trigger this that the proper application is frontmost, cmd-c works, then your application does something with the pasteboard. On second run the hotkey is activated but the previous application is no longer frontmost so cmd-c does not work. In essence you're sending cmd-c to some other application. Maybe your application has come to the front or some other.

To avoid this problem you need to make sure the application you are targeting is frontmost. So first you need some way to find out what the frontmost application is and then just before you perform cmd-c activate that application. For example in applescript if I wanted to run cmd-c on Safari I would use:

tell application "Safari" to activate
tell application "System Events" to keystroke "c" using command down
regulus6633
  • 18,848
  • 5
  • 41
  • 49
  • Also, I notice you "alloc" the script so at the bottom of that code you need to release the playScript if you're not using garbage collection: [playScript release]; – regulus6633 Apr 19 '10 at 10:16
1

Is there a reason you can't use a service for this?

If you really want to do it by synthesizing events, your best bet is to post a modified keyboard event rather than down/up - see this question for more details.

Simulating key press events in Mac OS X

If that doesn't work, well, you know where to find me :-)

Community
  • 1
  • 1
Nicholas Riley
  • 43,532
  • 6
  • 101
  • 124
0

I really don't understand why you're doing this at all, but if you want to make sure your events go to a specific process, then use the "ToPSN" variant of CGEventPost.

Azeem.Butt
  • 5,855
  • 1
  • 26
  • 22