views:

151

answers:

1

EDIT - not getting much input on this so here's the skinny. I'm posting keyboard events to a PSN. I then switch to another window, post some more events (this time tat session level) and switch back to the first window. When I start posting to the PSN again, nothing happens. Until I move the mouse or scroll wheel. Why would this be the case and how can I work around it (if not fix it)?

ORIGINAL - If I set up a loop that posts some keyboard events to a PSN, I find that it works fine except for when first launched. The event only seems to post when i do something with the mouse manually - even just moving it slightly. Here's the details, if they help.

An external application has a list box of text lines, which I am reading by posting copy commands (and checking the pasteboard). Unfortunately this is my only way to get this text.

Sometimes, the application pulls focus away from the list, which I can detect. When this happens, the most reliable way to return focus is by sending a mouse event to click on a text field directly above the list, then send a 'tab' keyboard event to shift the focus onto the list.

So at launch, the loop runs fine, scrolling down the list and copying the text. When focus is shifted away, its is detected fine, and the events are sent to move focus back to the list. But nothing seems to happen. The loop continues detecting that focus has changed, but the events only work once I move the mouse. Or even just use the scroll wheel. Strange.

Once this has happened the first time, it works fine - each time focus moves, the PSN events switch it back without me having to do anything at all.

Here's the code that runs in the loop - verified as working:

    //copy to pasteboard - CMD-V
e3 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, true);
CGEventSetFlags(e3, kCGEventFlagMaskCommand);
CGEventPostToPSN(&psn, e3);
CFRelease(e3);
e4 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, false);
CGEventPostToPSN(&psn, e4);
CFRelease(e4);

//move cursor down
e1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)125, true);
CGEventPostToPSN(&psn, e1);
CFRelease(e1);
e2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)125, false);
CGEventPostToPSN(&psn, e2);
CFRelease(e2);

And here's where I switch focus, also working (except when first required):

    //click in text input box - point is derived earlier
e6 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, point, 0);
CGEventPostToPSN(&psn, e6);
CFRelease(e6);
e7 = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, point, 0);
CGEventPostToPSN(&psn, e7);
CFRelease(e7);

//press tab key to move to chat log table
CGEventRef e = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)48, true);
//CGEventPost(kCGSessionEventTap, e);
CGEventPostToPSN(&psn, e);
CFRelease(e);
CGEventRef e11 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)48, false);
CGEventPostToPSN(&psn, e11);
CFRelease(e11); 
+1  A: 

Hey, i know it's not what you want to hear but applescript works much better for this kind of thing - reading textfields by posting copy commands, setting focused by sending click events to nearby items then sending tab events - it really isn't the best way to do it.

The problem with simulating events is that generating them isn't the whole story. They only become 'Events' if the target app interprets them correctly. Do you know if the target app is even a cocoa app?

For example, in your second bit of code you create 4 events. The target app needs to run it's event processing loop, which you don't have much control over, and check for queued events. If it did this it would find a simultaneous mouse down, mouse up, key down, key up. You want it to interpret these simultaenious events as a mouse click and THEN a keypress.. but who is to say it will?

You may be able to get this to work by adding into the mix a combination of flushing event queues, getting the target app to run it's runloop, throw in some pauses between the simulated events, and fiddling with event timestamps - but it probably won't be that reliable.

In Applescript you could get the text of row 3 of a table in another app by

tell application "System Events"
  tell process targetAppName
    set frontmost to true
    tell window named windowTitle
      tell table 1
        set value of attribute "AXFocused" to true
        set txtField to first text field of row 3
        return value of txtField
      end tell
    end tell
  end tell
end tell

From a cocoa App you can execute a String as an Applescript, you can call functions in different scripts, pass variables, return values, etc.

mustISignUp
There's no way I can use applescript on this. As I mentioned, there is no other way - I can detail why if you're bothered. Thanks for the info though.
Ben Packard
Yeah, i'm interested, i'm doing similar stuff. Just to clarify.. When i say use Applescript - i mean (or could just have easily have meant) from within a Cocoa app.
mustISignUp