Mouse pointer state in Windows applications doesn't change until mouse moves - windows

Did anyone notice that in Windows applications the mouse pointer doesn't change from Hourglass back to normal until you move the mouse?
So even if your application has finished a task and the mouse pointer has been set to go back to default, it will stay as an hourglass until you move the mouse. What is the reason for this, and can be it resolved?
I'm not sure if other people have noticed this but it is quite strange and it might be some kind of event-driven way to conserve OS resources.

The dialog box should maintain the logic of the hourglass. The worker thread should send a message to the dialog itself, telling it to start maintaining an hourglass thread. (You could test this by adding a temporary button to the dialog which starts and stops the hourglass.)
Another thing to be aware of is that having a second process set the hourglass of the first is an odd thing to do. An hourglass should only happen due to user action. While an hourglass is up, typically the only action that should be available to the user is "Cancel [whatever operation is keeping the hourglass up]."

Can it be resolved? Call ShowCursor(FALSE) before you call SetCursor(), and ShowCursor(TRUE) afterwards. Should do the job.

Related

SwapMouseButton does not "resist" a reboot

I am SwapMouseButton function to change the primary mouse button from right to left.
It seems work, the mouse buttons are swapped, and I can go check in the settings and see that they are indeed swapped...
BUT,
If I reboot the computer, the setting goes back to the original setting, undoing my programmatic swap.
Any clue as to what might be going wrong there?
Swapping the mouse buttons via code is not a permanent operation. As you have already discovered, it is reset on the next OS restart. If the user wants the buttons swapped permanently, they should be setting that in the Control Panel, not in your code.
That said, if you really want to handle this in code, then every time your app is run, have it use GetSystemMetrics(SM_SWAPBUTTON) to check if the mouse buttons are currently swapped, and if not then swap them.

Is it possible in wxwidgets to determine if the window is currently visible?

In my wxWidgets (wxPython) app, I am using a 50 ms timer to do some polling and update a window if there are any changes. If changes are detected it calls wxWindow.Refresh to update the window, and the actual updating of the widgets is done in the EVT_PAINT handler. It would be nice if I could disable the painting and/or the timer if the user is not looking at the window anyway. However EVT_PAINT is still being fired even if the window is iconized or hidden behind a different window.
Is there any way to detect if the window is not currently visible on screen, or to prevent EVT_PAINT events from firing unnecessarily?
There is the IsActive method and the EVT_ACTIVATE event to test whether the window has focus, but I want to keep updating the window if it is unfocussed but still visible. The wxWindow.IsShown family of functions doesn't help, they still return True for hidden/iconized windows.
IsShownOnScreen() could help with the iconized case. Edit: But actually does not.
Or you could trap wxEVT_ICONIZE to detect when the window is minimized.
But to be honest I'm rather surprised that refreshing a window hidden behind another one still results in a repaint. If the window in front is not transparent, this really shouldn't happen.

MFC app doesn't seem to receive input messages?

We have a MFC Visual-C++ application that is not reacting to any user input.
(Note: Currently only known on one machine. The behavior does recur occasionally, but only after the App has been running for several days!)
The application is redrawn when we switch to it via Alt-Tab (or the Task Bar), but we cannot, for example, activate it's main window by clicking on the title bar.
We have already pulled 4 dumps with WinDbg and checked the active instructions. We always were in some redraw code or somesuch inside the main thread (the GUI thread). We definitely were/are not in a modal message loop and the main thread's stack always looked "OK". (Most/all worker threads were idling, waiting for some event, no suspicious code there either.)
When investigating the problem with Spy++, we see the behavior specified also in this separate question, namely that we seem to get paint and activation messages, but no user input is routed to the application. When I have the application window on the screen, and select it to show messages of the main window,
it will only show "generic" "referesh" messages and nothing else
If I drill deeper, and select all messages for the whole process,
this is what we see:
The app is apparently only processing messages on one hidden sub-window (00CB09F0), and what we see there is a constant stream of 200 WM_PAINT messages per second.
Normally this Sub Window isn't processing any messages at all (except refresh WM_PAINT etc. when Windows sends them). It is normally used as a drawing area and the drawing happens through a WM_TIMER message on it's parent (010A09B8) window. (This WM_TIMER message isn't shown on the hanging app either though.)
The performance profile as shown in process explorer looks like this (100% kernel time, more or less):
I'd say that you have a redraw loop in that window that receives the WM_PAINT flood.
That usually happens if you call Invalidate or similar from the processing of the WM_PAINT message, directly or indirectly.
Other posibility is that, since you say that you are using a timer to redraw the window, the actual drawing is taking more time that the time it self, so the messages pile up in the queue.
Yet another posibility is that you are invalidating the window from a different thread than the one making the painting.
Anyway, you should ensure that you are calling Invalidate*() properly (you showed no code), and never from the OnPaint event. And avoid calling UpdateWindow() as this function can mess things if called without a little care.
I've seen this problem when an exception is thrown from a dialog. MFC's DoModal function disables the main program window then reenables it when the dialog returns; exceptions bypass the reenabling part and the main window remains disabled forever.

How does one use onmousedown/onmouseup correctly?

Whenever I write mouse handling code, the onmousedown/onmouseup/onmousemove model always seemed to force me to produce unnecessarily complex code that would still end up causing all sorts of UI bugs.
The main problem which I see even in major pieces of software these days is the "ghost mouse" event where you drag to outside the window and then let go. Once you return back into the window, the application still thinks you have the mouse down even though the button is up. This is especially annoying when you're trying to highlight something that goes to the border of the screen.
Is there a RIGHT way to write mouse code or is the entire model just flawed?
Ordinarily one captures the mouse events on mouse down so the mouse move and mouse up go through your code regardless of the caret moving out of you application window.
More recently this is a problem when running a VM or remote session, its difficult for apps in these to track the mouse outside of the machine screen area represented by a window on a host.
I'm not sure what environment you're attempting to track mouse buttons in, but the best way to handle this is to have a mouse listener that tracks onmouseup 100% of the time after you've detected onmousedown.
That way, it doesn't matter what screen region the user releases the mouse button in. It will reset no matter where it happens.

Detecting a single mouse click in MFC

In MFC a double-mouse click event triggers the following sequence of messages
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBCLK
WM_LBUTTONUP
So responding to the WM_LBUTTONDBCLK message allows you to detect a double-click. But if I just want to detect a single-click how to I distinguish it?
But just looking at the WM_LBUTTONUP message isn't enough as it could be a single-click or it could be the first click of a double-click.
How can I successfully identify just a single-click?
(Please allow me to call these events Mouse Up and Mouse Down. My MFC is a little rusty. And there's this stuff called .NET who's been messing up my terminology lately ;-)
Short story: You don't simply want to know about Mouse Click. You need more.
Long story:
Although this is counter-intuitive, it appears that simply wanting a mouse-click is fairly uncommon. Most often, you'll want to perform some processing on Mouse Down and do some further processing on Mouse Up. The trick is that simply tracking Mouse Up messages is not enough: Mouse Down may not have happened in your window. Do you consider it a valid click then? Especially considering that the Mouse Down processing (such as selecting an item) did not occur.
Going further up the reasoning, you should not rely on receiving a Mouse Up after you processed Mouse Down: User may have moved the mouse and released the button somewhere else (think drag'n'drop), in which case, you don't receive the MouseUp event... unless you capture the mouse on MouseDown to make sure you get mouse event up to Mouse Up even if the mouse left your window.
All in all, you end up tracking Mouse Down, capture the mouse and when you receive Mouse Up, just check if you own the capture. If not, the mouse was either double-clicked (no 2nd mouse down) or Mouse Down happened somewhere else hence you most likely don't care about this Mouse Up.
In conclusion: There's no MouseClick message simply because you wouldn't go very far with it: You need to handle more messages and implement more mechanics anyway.
Oh! And if your dealing with an existing control which already handles all this items and selection stuff, such as a listview, chances are it provides with a similar custom notification such as Item Activate or Item Selection Changed.
I just tried this in Delphi, the behavior is the same: even when a double click is happening, a single click event is issued right after the first one of the two.
I solved it using a timer, which works like this:
deactivate timer on WM_LBUTTONDBLCLK (and set bDbl to true)
activate timer on WM_LBUTTONUP if bDbl==false
deactivate on WM_LBUTTONUP if bDbl==true (and reset bDbl)
I set the interval of the timer to the time returned by GetDoubleClickTime.
MSDN says:
The GetDoubleClickTime function
retrieves the current double-click
time for the mouse. A double-click is
a series of two clicks of the mouse
button, the second occurring within a
specified time after the first. The
double-click time is the maximum
number of milliseconds that may occur
between the first and second click of
a double-click.
If the timer happens to fire then you have the real click. In my case the double click interval is 500ms, so any "real click" will be delayed this long.
You could check WM_LBUTTONDOWN has not been called more than once before WM_LBUTTONUP. In practice Windows does this for you, in that if you get a WM_LBUTTONDBCLK you tend not to get a WM_LBUTTONUP.
You can use PreTranslateMessage() to count the messages as they appear. If you've received only the mouse messages corresponding to a single-click, and the system-configured time for double-clicking has expired, you can safely assume it's a single-click.
As far as I know there is no way to know that this is the case as it is happening, which makes sense -- until the time is expired, there's no way to know that a second click is or isn't coming.
that's a little tricky.
I would detect the WM_LBUTTONDOWN & WM_LBUTTONUP combo, store that event somewhere and set a timeout for a second or so. If there isn't a WM_LBUTTONDBCLK during that timeout then you have a single click.
This might imply you need to have another thread running but I think you could accomplish it with one thread.
I think the solution is to start a timer after the first click & then check the elapsed time after at the next immediate click, this will tell you if it is a single click or double click.
You typically look at #MLButtonUp and you would not have single click and double click behavior on the same mouse button.

Resources