views:

95

answers:

2

I'm using CGPostKeyboardEvent((CGCharCode)0, (CGKeyCode)55, true); to simulate key presses, but say I had a string that I wanted to convert to key presses. How would I go about dividing an NSString up into single characters, and then converting those into key presses? Thanks!

Edit: OK I think I found a solution, but I am unable to get my syntax right. Objective-C really drives me crazy.

Here's my code:

int stringLength = [theData length];
  for (int i = 1; i <= stringLength; i++) {
   char *currentCharacter = [theData characterAtIndex:i];
   CGPostKeyboardEvent((CGCharCode)0, (CGKeyCode)keyCodeForKeyString(currentCharacter), true);
}

-(int)keyCodeForKeyString:(char *)keyString
{
 if (strcmp(keyString, "a") == 0) return 0;
 if (strcmp(keyString, "s") == 0) return 1;
 if (strcmp(keyString, "d") == 0) return 2;
 if (strcmp(keyString, "f") == 0) return 3;
 if (strcmp(keyString, "h") == 0) return 4;
 if (strcmp(keyString, "g") == 0) return 5;
 if (strcmp(keyString, "z") == 0) return 6;
 if (strcmp(keyString, "x") == 0) return 7;
 if (strcmp(keyString, "c") == 0) return 8;
 if (strcmp(keyString, "v") == 0) return 9;
 // what is 10?
 if (strcmp(keyString, "b") == 0) return 11;
 if (strcmp(keyString, "q") == 0) return 12;
 if (strcmp(keyString, "w") == 0) return 13;
 if (strcmp(keyString, "e") == 0) return 14;
 if (strcmp(keyString, "r") == 0) return 15;
 if (strcmp(keyString, "y") == 0) return 16;
 if (strcmp(keyString, "t") == 0) return 17;
 if (strcmp(keyString, "1") == 0) return 18;
 if (strcmp(keyString, "2") == 0) return 19;
 if (strcmp(keyString, "3") == 0) return 20;
 if (strcmp(keyString, "4") == 0) return 21;
 if (strcmp(keyString, "6") == 0) return 22;
 if (strcmp(keyString, "5") == 0) return 23;
 if (strcmp(keyString, "=") == 0) return 24;
 if (strcmp(keyString, "9") == 0) return 25;
 if (strcmp(keyString, "7") == 0) return 26;
 if (strcmp(keyString, "-") == 0) return 27;
 if (strcmp(keyString, "8") == 0) return 28;
 if (strcmp(keyString, "0") == 0) return 29;
 if (strcmp(keyString, "]") == 0) return 30;
 if (strcmp(keyString, "o") == 0) return 31;
 if (strcmp(keyString, "u") == 0) return 32;
 if (strcmp(keyString, "[") == 0) return 33;
 if (strcmp(keyString, "i") == 0) return 34;
 if (strcmp(keyString, "p") == 0) return 35;
 if (strcmp(keyString, "RETURN") == 0) return 36;
 if (strcmp(keyString, "l") == 0) return 37;
 if (strcmp(keyString, "j") == 0) return 38;
 if (strcmp(keyString, "'") == 0) return 39;
 if (strcmp(keyString, "k") == 0) return 40;
 if (strcmp(keyString, ";") == 0) return 41;
 if (strcmp(keyString, "\\") == 0) return 42;
 if (strcmp(keyString, ",") == 0) return 43;
 if (strcmp(keyString, "/") == 0) return 44;
 if (strcmp(keyString, "n") == 0) return 45;
 if (strcmp(keyString, "m") == 0) return 46;
 if (strcmp(keyString, ".") == 0) return 47;

 fprintf(stderr, "keyString %s Not Found. Aborting...\n", keyString);
 exit(EXIT_FAILURE);
}

So my question is, How do I get the keyCodeForKeyString(currentCharacter) inside the argument of CGPostKeyboardEvent?

+2  A: 

That problem is a nightmare, because there are functions to translate key codes to character codes, but not the other way around. Remember that not all characters can be typed, and which ones can be typed depends on the current keyboard layout.

If your goal is to make some text appear in another application, you may be able to do it without translating individual characters by using CGEventKeyboardSetUnicodeString. However, implementation of this feature has been spotty, so be sure to test on all OS versions you care about, and in both Carbon and Cocoa apps.

JWWalker
I see that the arguments for `CGEventKeyboardSetUnicodeString` are `(CGEventRef event, UniCharCount stringLength, const UniChar unicodeString[])`1. What event do I use for the `CGEventRef` argument?2. Will `NSString.length` be compatible with the `UniCharCount` argument and will `NSString` be compatible with the `const UniChar` argument?Thanks for your help.
Wind Up Toy
1. Use `CGEventCreateKeyboardEvent`. 2. Yes, and No. Use `-[NSString getCharacters:range:]` to extract the characters into a buffer.
JWWalker
Then how do I pass the actual string?
Wind Up Toy
Or better yet, would you please show me exactly how to do this? I'm not quite sure how I pass CGEventCreateKeyboardEvent as an argument, because it requires a specific key code. Thanks a ton for your help.
Wind Up Toy
I don't think it matters which key code you pass in this case, I think I've used 0.
JWWalker
I'm afraid I'm not following you.
Wind Up Toy
A: 

This can be a difficult problem, but not for the reason that @JWWalker states.

If you can easily convert keycodes to characters, then it is trivial to convert from a character to a keycode. It's a 1-to-1 mapping between the two (a character has a single keycode, and a keycode has a single [sometimes non-printable] character), so you can easily build the transformation yourself. The ShortcutRecorder project has an NSValueTransformer subclass that does this.

The difficulty is that posting keystrokes does not always result in a synchronous action. Since you're (essentially) communicating to another process, you have no idea how long that process will take to handle your event posts and perform the appropriate action. The time between "posting" and "handling" is indeterminate based on the current load and activity on the system. Usually it happens fairly quickly (and in such a way as to appear mostly synchronous), but it doesn't have to be. My particular example comes from dealing with the pasteboard server, which sometimes takes its very sweet time to respond to a cmd-v keystroke.

If all you're doing is mimicking typing, then that shouldn't be too bad. You can get the char* of your string using -[NSString UTF8String] (or similar), then simply loop through the characters, convert them to keycodes, and post the appropriate events.

Dave DeLong
A one to one mapping between characters and key codes? There are thousands of Unicode characters, but less than 100 keys.
JWWalker
@JWWalker sorry, I was thinking of (primarily) the ASCII set, or the set that's on the physical keyboard.
Dave DeLong