The wrong way to check whether the mouse buttons have been swapped

Back in the late 1990's, the window manager team received a bug
that said that sometimes the mouse button state
got messed up and the computer acted
as if the buttons were stuck down.
Further investigation revealed that it occurred only when one
particular program was running, and only if the user had enabled mouse
button swapping.

The reason is that the program in question detected whether the
mouse buttons were swapped with a function like this:

The SwapMouseButton function changes the button swap state
and returns the old state.
The way the program checked whether the buttons were swapped
was by unswapping the buttons and using the return value to
determine what the previous setting was,
then re-swapping the buttons if the previous setting was
"Yes, they were swapped."

If you started with the buttons swapped, running this function
created a tiny window where the buttons were momentarily unswapped.
And if you were unlucky enough to click the mouse during this
window of vulnerability, the program saw one mouse button go down
and a different button come up!
Even though it was the same physical button each time,
it was a different logical button, since the meanings of the buttons
had changed.

The correct way of detecting whether mouse buttons are swapped
is just to ask non-intrusively.

Very interesting. I have run into a mouse swap button bug since 95 build 347. Sometimes you will left click and it pops up the right click context menu. I have found that rapidly clicking back and forth between the two buttons for a few seconds eventually fixes it. Still an annoyance though.

Richard Ahlquist: I think I know what you mean. If so, it appears to be caused by a corrupted packet of mouse data… or at least it happened to me a lot more often when I had a mouse with a dodgy cable.

Swapping the mouse buttons over should almost certainly only be done at the user’s request, in which case the user is probably clicking Apply in the mouse settings control panel, in which case none of the issues mentioned in the comments seem worth solving, given that it would add complexity, and potentially bugs, to the vital mouse handling code.

Fixing the program, and adding a compatibility layer for the old versions of it if needed, seems like the more sensible route, unless several other programs made the same mistake (which seems unlikely).

Evan: As I understand it Aaargh! is right. The problem occurs when a mouse button is down while buttons are swapped – whether it’s the first or the last call. A press-and-release between the calls should not matter.

(Think of this: If I call SwapMoouseButton once today, and once again tomorrow, have I opened a barn dor of opportunity?)

peterchen: Yes, you’ve opened a barn door of opportunity, sort of. If you call swap once, then the mouse button gets pressed (while the buttons are reversed), then you call swap again, then the OS won’t recognize the release. (Because the OS gets a press for one button, but a release for the other.)

"Seems like a race condition in the mouse handling code. The swap function should lock the mouse while performing the swap."

That’s not the issue; the swap is instantaneous. It the window between the two calls that makes it a race, and Windows *can’t* lock the mouse out during that interval because it has no clue the second call is coming.

Now, it could be argued that Windows should properly deal with a button being held down during the swap, but even if it’s possible to do better, what the proper semantics are isn’t exactly clear.

Every* book on yarncraft (knitting, crochet, etc) I’ve run across handles this by saying something similar to, "if you’re left handed, just think ‘right hand’ when I say ‘left hand’ and vice versa." Lefties are supposed to be bright and creative, right? And used to dealing with this sort of thing. I honestly think it would confuse some of them because they wouldn’t think the system would think that far ahead. They would do the flip in their minds automatically.

*with one exception that repeated each section, even with flipped photos. Crappy book, too.

"Windows *can’t* lock the mouse out during that interval because it has no clue the second call is coming."

Every mouse down event is eventually followed by a mouse up event. If you swap the mouse buttons, the time between those two events is a critical path. One could postpone the swap until the mouse up event, or you could ‘remember’ what button number was sent to the application when the physical mouse button was pressed down and send the accompanying mouse up event for that button, regardless of the swap occuring when the physical button is released.

Peterchen: the real barn door is open if your application does the swap thing every second. It gives you a much greater chance hitting the window (remember: it happens when you press down, the buttons get reversed, and then you lift up).

You can also get into this state with unfortunate sets of simulated mouse events.

As for why an app needs to know the mouse swap state, there are any number of possible reasons (in my case because I have a user-mode helper application that has to tell my kernel mode *mouse* driver because there’s no reliable way to find this out on a per-user basis from the kernel).

But I think the most likely reason is for tutorial purposes: "Now move your pointer over the <foobar> control and press the <left/right> mouse button.". Users, particularly the ones that need a tutorial at that level of detail in the first place, really *are* too stupid to mentally convert from left to right without calling customer support.

But who knows, maybe it’s an app that comes with some special mouse that has "This button is for <foobar>" printed on it, and the app needs to know when that physical switch is actually depressed, regardless of the swap state.

It’s still hard to comprehend that some programmer would think that swapping the buttons was a good way to determine this.

I can imagine wanting to know this if you were making a pinball game in which the flippers were controlled by the mouse buttons.. Would be sort of silly to have the right flipper go off on a LMB press and vice versa :)

That’s not how Windows works and it gets really messy anyway. You can for example press the left button, and while it’s down click with the right one and only then release the left button. And all permutations of the that.

> "Now move your pointer over the <foobar> control and press the <left/right> mouse button.". Users, particularly the ones that need a tutorial at that level of detail in the first place, really *are* too stupid to mentally convert from left to right without calling customer support.

This problem is much easier to solve by using natural language than code:

Yeah, can’t blame the coder for this. If you have a "SwapMouseButtons" function but no "GetMouseSwapState", and instead have to go groping in an entirely different and unexpected area of the SDK docs for your solution, and the docs on the page don’t even tell you how to check the state of the thing you just set, then what you have is not a bad programmer, but a bad library, with very bad docs. In this case, someone’s corrected this by adding some "community content" to msdn2, referring to this blog, but that’s about a decade late for the programmer described in the OP, and won’t help anyone with offline msdn docs anyway.

Every single "setX" anywhere in a library should have a matching "getX", without exception. Abhorrent though Java is, this is one thing it usually got very right.

As usual everyone is arguing pointlessly and completely missing the point — THIS IS "The wrong way to check whether the mouse buttons have been swapped". How Windows handles it is irrelevant. It’s programmer’s fault, not Windows’.

@Dewi:

Do you check if your room light is on by flipping the light switch twice? Or you are intelligent enough to look for a more efficient way of doing it?

Any decent Windows programmer knows that GetSystemMetrics() returns loads of things which perhaps should be implemented as GetXxx() but not having GetXxx() is still not a good enough excuse to write this horrible code.

Just imagine the same programmer faced with a challenge of determining whether disk is writeable or not and his code incorporated into some data recovery application and you will understand what I am talking about here.

I expected you to bring up the broken lightbulb, or a broken switch as an excuse, but the API for swapping the buttons is not broken so you don’t have any excuse to fiddle with it.

While we are at it, standard ways of checking whether the light is on are:

to compare default light level against current

to check if the switch is in the "On" position

IMO that whole Set and Get thing is extremely inefficient way of programming.

If you have a set of values you can query, it is much easier and less error prone to have one function (like GetSystemMetrics) and to pass a parameter to get different values back. The only thing you have to validate is one function parameter.

On the other hand if you have GetXxx() for each value you can query then you might not have parameters for all of them, but you will have dozens of entry points into your code which you have to check, guard and maintain.

Not to mention that if you want to preserve several settings at once you can’t use arrays and a single call to GetSystemMetrics() inside of a loop anymore but instead you have to explicitly code dozens of GetXxx() calls.

@Igor: wrong metaphor. If I am checking to see if the light is switched on, *and I don’t know the answer already* odds are that I will need to check the switch.

I leave it as an exercise for yourself to figure out why.

You can try on silly non-computing metaphors all day, but my original point stands: the only computing system where a "set" command be separate from a "get" command, is one where you deliberately want to isolate the two comands (financial information walls, etc). In this case, it should be extremely clearly documented, as an exception to the general rule.

Except where separation is a design goal, there is no rational situation where this rule can be broken without it being a flawed design that will increase development and maintenance time.