Strange behaviour when posting CGEvent to PSN - objective-c

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);

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.

Instead of creating the event with a null CGEventSourceRef try creating an input source this way:
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
Based on this mailing list message, this seems to be the secret for success in posting events with CGEventCreateXXXEvent. A Google search shows that lots of devs are having problems with this, but this is the one success post I've found, and a proper CGEventSourceRef (instead of NULL) seems to be the one difference in this example vs. all the others I've found. It solved my problems with posting keyboard events.

Related

Stop button in LabVIEW cannot be pressed during while loop execution inside an event structure

I am currently working on a LabVIEW project and have found myself stuck on how to make a while loop exit when I press the abort (stop) button. For a simple while loop I understand how to do this - but the problem is that this while loop is nested inside of an event structure and I'm guessing that the button cannot be pressed while the loop is executing. Attached here is a picture of part of my code (that contains this specific event case which is causing me problems): To spend a little more time explaining what the problem is - I think the code is doing what I want it to do (namely output a set of commands in a repeated cycle with a wait timer) but I cannot stop the code mid cycle (pressing the abort button with my mouse does nothing - as in the button doesn't show it being pressed and the indicator shows no change, I also can't use any other functionality of my program during the cycle which I'm assuming is related). And I do not want to stop the LabVIEW program from running entirely - just the code inside the while loop pictured above. This is what the front panel is configured too for completeness:
Essentially what I want to happen is the while loop to execute when I press DWG and in the middle of the cycle be able to abort it. Sorry if my code seems a little messy. Additionally, I've tried the same code with a for loop originally (via a conditional terminal so it could stop early) and that didn't work either. Thanks for any help I appreciate it!
Your problem is that inside the event structure, by default the UI is frozen so no UI actions (keyboard/mouse/etc) are processed until you exit that frame.
Option 1. You can right click the Event Structure and select "Edit events handled by this case" dialog and then uncheck the "Lock panel" checkbox -- that will allow the UI to be live while you are in that frame. I do not recommend this solution generally unless you have an extremely simple user interface because it leads to the user being able to change controls without the events behind those controls being processed (not a good UI experience for users). But if the UI is simple enough, that works.
Option 2. You can create a user event that is the code you want inside your While Loop. When the Deg Wait Go button is pressed, use the "Generate User Event" node to trigger that event. Do the same thing in the user event case so that the event re-triggers itself if and only if the Abort button has not been pressed.
Option 3. Create a separate loop OUTSIDE your UI loop that does your processing with some sort of command queue running between the UI loop and that other loop. The other loop moves into various states at the request of the UI loop... it's the one that does nothing until it receives a "go" message and then keeps looping until it receives a "stop" message. You can Google "queued message handler" for extensive details of this solution. This is the most common solution for complex UI requirements, especially useful for separating concerns of the UI code from the execution code.

Using program in the background (C#)

e.g: a game is running and when I press a button, I want my program to show up.
another example : I'm on my desktop and when I press a button, I want my cursor to move to a specific position.
how do I achieve that?
Assuming you are using windows, and using c# to develop the application.
1. You will have to develop a window form application keep it running. write a global mouse event handler, when you detect a mouse click on certain target area, bring your app to front
Global mouse event handler
this.WindowState = FormWindowState.Minimized;
this.Show();
this.WindowState = FormWindowState.Normal;
move mose cursor, you could refer to api mentioned below post
How to move mouse cursor using C#?

play sound triggered by click on form control

I'm trying to trigger a sound by clicking on a form control in Excel 11. The handler for the click event tries to play a sound using sndPlaySound32 if it finds certain text on the clipboard.
I've declared the function sndPlaySound32, and call it with (simplified)
whatSound$ = "C:\WINDOWS\Media\Office97\Drop.wav"
sndPlaySound32 whatSound$, &H0
As long as Excel is the front app when I click the control then everything works fine and the sound plays (if the target text is found on the clipboard), but if I'm bringing the text in on the clipboard from another app like Notepad, where Excel is not the front app when the click occurs, then the first click on the control doesn't produce any sound. Subsequent clicks work normally. Other events on the form have no problem with this and continue to respond normally to the first click.
How can I make the sound play on the first click of the control when Excel is not the front app?
Edit 11/25/10:
I can't make the board Comments work. I click Add Comment but my comments aren't shown, so this is a reply to Boost.
I think it's something deeper than just bringing Excel to the front with the first click, because there are several of other actions that are triggered successfully by this first click on the control, (while another app is in front), and they all work just fine. It's only the playSound that doesn't work until subsequent clicks.
I've tried moving the playSound command both to the front and to the end of the list of other actions (e.g. font changes, text resizing etc.) that are supposed to happen with each click, and also tried putting delays in various places, but no joy. If I step through the code, the playSound procedure gets called correctly on the first click but it doesn't produce a sound.
I don't think there's a fix here. An application will not accept mouse events until it is "in focus." If Notepad is in focus, you will transfer focus from it to Excel with the first click, then Excel's event handlers will come into play and respond to subsequent events.
You could (and I don't know if it's even possible) put a transparent windows over the whole screen and interpret mouse-clicks in that context, and pass them to the appropriate underlying windows. SMOP, I suppose.
I finally found a workaround that's simple, reliable, and ugly. Since it won't play the sound on the first click, I just give it another chance. So now, in place of the code in my original post, it looks like this:
whatSound$ = "C:\WINDOWS\Media\Office97\Drop.wav"
sndPlaySound32 whatSound$, &H0
sndPlaySound32 whatSound$, &H0
It still ignores the first playSound command, but it plays the second one every time on the first click.

How to detect Ctrl+V in Silverlight 4?

What is the best way to detect Ctrl+V in Silverlight?
I want to detect Ctrl+V, to get access to the Clipboard.
if (e.Key == Key.V)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
//do what you want on paste
}
}
You have to use this on the keyUp event. More details can be found here: http://msdn.microsoft.com/en-us/library/cc189015%28VS.95%29.aspx
EDIT
To capture the CTRL+V keypress globally in your silverlight application, is fraught with difficulty. Events start at the child elements and bubble down to the parent controls, so simply handling KeyDown on your root UIElement will not work. Any text input control will first get the event and smother it (by setting Handled to true on the event args.) I think that if you use the DOM bridge and subscribe a handler to the browser KeyDown event for the silverlight element itself that you may actually be able to get to it first, and even handle it completely before any silverlight controls can. I think that would be the easiest way to intercept CTRL+V, but I have not tested it.
Original Answer
You should use the System.Windows.Clipboard class.
GetText, which retrieves text from
the clipboard
SetText, which places
text on the clipboard
ContainsText,
which indicates whether the clipboard
currently contains text

How can I tell if a Balloon tip in the system tray has closed?

I have an application that uses a NotifyIcon in the tray to hide/restore the application, as well as pop up notices to the user of application events. My application has a notification queue, and I use the NotificationIcon.BalloonTipClosed event to determine when to reset the balloon and show the next notification (if there's one in the queue).
This method seems to work great in both usual causes (user lets the balloon close itself when it times out, and user clicks "X" in balloon to force it to close), but there's a third case where BalloonTipClosed doesn't get called:
Notification balloon pops up
While it's visible, user right-clicks on notification icon to bring up context menu, causing the balloon to disappear
The BalloonTipClosed event doesn't get triggered in this instance - I figure it's a bug in the framework (I'm using 2.0), but does anybody have an idea around this? If I don't get this event, my application always thinks there's a balloon visible (I have a boolean that prevents it from displaying multiple balloons at once), and it will never show another icon again, as long as it's running.
This belongs as a comment to Aarons answer, but I am not allowed to comment yet.
If you handle the BalloonTipClicked and MouseClick events on the NotifyIcon (as well as the BalloonTipClosed) then you can capture all the ways the balloon can close. The only thing you have to be aware of is that several scenerios will trigger multiple events, so be sure to code around that (something like isClosed = true, and then reset that when a new balloon is displayed).
In the event handler for the BalloonTipClicked Event, I would check to see if the right mouse button was clicked, and if it was set the boolean to false.
Here's what I ended up doing, though I don't particularly like this solution. I added a second timer to the form and set it for 10 seconds. When a notification pops up (when I pop one), I start the timer, and then in BalloonTipClosed, I stop it. If the timer ticks (meaning that BalloonTipClosed hasn't run yet), I display the next tip manually.
The result is that if it hasn't fired yet, I take care of it. However, I'm open to better solutions if anybody has one.
I think this post from Raymond Chen about balloon notifications may help you:
http://blogs.msdn.com/oldnewthing/archive/2009/05/04/9585032.aspx