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:

// do not use this function
BOOL AreMouseButtonsSwapped()
{
 BOOL fWasSwapped = SwapMouseButton(FALSE);
 if (fWasSwapped) SwapMouseButton(TRUE);
 return fWasSwapped;
}

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.

BOOL AreMouseButtonsSwapped()
{
 return GetSystemMetrics(SM_SWAPBUTTON);
}
Comments (39)
  1. Peter says:

    And because this information should go to more places, I’ve used the ‘Community Content’ feature of MSDN to add it to the (english version of the) MSDN site.

  2. 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.

  3. Aaargh! says:

    While the swapping back and forth is an ugly way to check if the buttons are swapped, the real problem lies here:

    "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!"

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

  4. a says:

    Why would a program need to know this anyway?

  5. Good Point says:

    "Why would a program need to know this anyway?"

    Because someone (a PM no doubt) filed a defect that said to change all text refering to "Right-Click" to "Left-Click" for left handed mouse users.

  6. Jules says:

    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.

  7. Leo Davidson says:

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

  8. mikeb says:

    Holy crap – how did you guys ever manage to reproduce this problem reliably enough to track down the problem?

  9. Mikkin says:

    Every mouse down event is eventually followed by a mouse up event.

    Only in an ideal world, actual results may differ. (I can’t believe Raymond had to explain this.)

  10. peterchen says:

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

    Or am I missing something, Raymond?

    Nitpicks corner: I know it’s not your fault.

  11. The root of the problem, I think, is SwapMouseButton returning previous state (it shouldn’t have returned any value, or, at most a boolean for success).

  12. MS says:

    "Holy crap – how did you guys ever manage to reproduce this problem reliably enough to track down the problem?"

    I’d guess that it’d be easy to see if you watched the program and what functions it was calling.

  13. David Walker says:

    Good question "a" — why should a program need to know this?  It’s none of the program’s business {speaking of any application program}.

  14. BryanK says:

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

  15. Gazpacho says:

    This looks like another episode of "The developer didn’t manage to find the right MSDN page in the time available."

  16. Evan says:

    @Aaargh

    "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.

  17. poochner says:

    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.

  18. Aaargh! says:

    @evan

    "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.

  19. Peter says:

    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.

  20. Eilene says:

    I am having nothing but the same problems. Dell does not have any answer and this is a new system I have. I think they think I just want another exchange. Please help!

  21. A post from Raymond Chen today reminds me of the first application compatibility issue I debugged (and

  22. Ray Trent says:

    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.

  23. Drak says:

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

  24. meh says:

    @Aaargh!

    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.

  25. peterchen says:

    bryank: but that could happen during the first call to SwapMouseButtons as well, or am I missing something here?

    i.e. as I understand the problem is with SwapMouseButton itself, not the use: when a mouse button is pressed while you call SetMouseButton, things go boom.

  26. Name required says:

    > the user is probably clicking Apply in the mouse settings control panel

    Actually the buttons are swapped *before* the Apply button is clicked (Win XP), as soon as the checkbox is (un)checked as far as I can tell.

  27. Name required says:

    > "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:

    sed s/left mouse button/forefinger mouse button/ myhelp.txt

  28. Dewi Morgan says:

    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.

  29. Igor says:

    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.

  30. Igor says:

    @Dewi:

    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:

    1. to compare default light level against current
    2. 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.

  31. Dewi Morgan says:

    @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.

  32. Thanks for mentioning this.

    I’ll admit i did exactly that in my games; changed the code after reading ;)

  33. Jules, funny I only use MS Intellimouse explorers nowadays LOL.

  34. ender says:

    I got hit by this bug several times on Win9x. IIRC, the solution was to press both buttons at once, then the system somehow found out what’s really happening.

    I still occassionally see a similar problem with keyboard in XP – Windows suddenly starts thinking that either Ctrl, Shift or Alt is pressed – could something similar be the cause?

  35. Name required says:

    Igor – you forgot "3. Tripping over the dog" (in this case the light is *usually* off, but the sneaky dog can also cause this exception to occur when the light is on).

  36. butt-on says:

    Why isn’t all the mouse buttons bindable? To only be able to swap button #1 and button #2 seems like a very tight constraint.

  37. GregM says:

    At the time this was created, there were probably only 2 buttons.  Three-button mice came later.

    Some drivers for multi-button mice already handle this cleanly without having to get the OS involved.

  38. Erzengel says:

    @Raymond: So how did the window manager team fix this particular bug? Was it just a shim put into that particular program?

Comments are closed.