views:

221

answers:

3

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: http://stackoverflow.com/questions/1505933/how-to-send-a-cmd-c-keystroke-to-the-active-application-in-objective-c-or-tell

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?

+1  A: 

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
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
+1  A: 

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.

http://stackoverflow.com/questions/2379867/simulating-key-press-events-in-mac-osx

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

Nicholas Riley
Indeed, this does work better :] Thanks for the help, nick!
bhargav
A: 

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