Why are mouse wheel messages delivered to the focus window instead of the window under the mouse?


Douglas asks, "Is there a good reason that mouse wheel messages are sent to the focused window instead of the window under the mouse?"

I don't know if there's a good reason, but there's a reason.

The mouse wheel turned into one of those features whose primary use is not the original intended use. The original intent of the mouse wheel was to control zooming. Spin the wheel forward to zoom in; spin it backward to zoom out. (Or is it the other way? I can never remember.) In practice, though, people preferred to use the mouse wheel to scroll, and zooming got relegated to a secondary feature activated by holding the Ctrl key while turning the wheel.

And it's the Ctrl key that is the key that unlocks the puzzle. That, plus some historical context.

The historical context is that the mouse wheel in Windows was introduced by the Microsoft Hardware team in 1996. This was not synchronized with an operating system release, and Windows Update wasn't a thing yet, not that anybody cared because Internet access was not widespread, and those who did were using mostly dial-up.

Therefore, the hardware team had to invent their own mechanism for mouse wheel messages, with the understanding that operating system support for it wouldn't arrive until the next major release of Windows (which turned out to be Windows 98 and Windows NT 4.0).

You can see the result of this in the zmouse.h header file buried in your SDK. This is a header file from 1996 that nobody wants to delete because doing so might break somebody. (See also: multimon.h.)

The fact that the wheel was intended for zooming rather than scrolling is also evident in the names of the parameters in the header file: The parameter that receives a boolean that tells you whether a wheel mouse is installed is named pf3DSupport. It's also apparent in the header file name: The z represents the third coordinate, emphasizing that the wheel is supposed to move you closer to or further away from the document.

Since the hardware team had to invent something and couldn't rely on help from the window manager team, they had to make do with what they could. Wheel support operated by having a dedicated helper program that listened to the mouse hardware. When it detected that the wheel scrolled, it delivered a custom wheel scroll message: MSH_MOUSE­WHEEL

Okay, now you have a few problems. Let's start with "What do you put in the message payload?"

You have four pieces of information that are essential:

  • The wheel scroll amount.
  • The mouse x-coordinate.
  • The mouse y-coordinate.
  • The keyboard shift key states.

The scroll amount is essential because that's sort of the point of the message. The mouse coordinates are essential in order for the application to know which object to scroll. And the shift key states are essential because that tells the application what action it should take.

The helper program can get the first piece of essential information because it came from the mouse. The second and third pieces can come from Get­Cursor­Pos. But what about the fourth piece?

Keyboard state is input-queue-dependent, and the helper program doesn't have access to the input queue of the window that receives the MSH_MOUSE­WHEEL message. Therefore, in order to preserve sanity, it has to deliver the message to the window with keyboard focus, or a window on the same input queue as the window with keyboard focus, since that's the only input queue that knows whether the Ctrl key is down.²

Besides, even if the helper program could get access to the keyboard state, it didn't really have anywhere to put it. If you want 16-bit applications to support the mouse wheel (and 16-bit applications were still very important in 1996), you have to pack everything into a 16-bit WPARAM and a 32-bit LPARAM, which is enough space to hold three 16-bit pieces of information. And we already have three pieces of information. We're all full!

There's no room for the keyboard state in the MSH_MOUSE­WHEEL message payload, which is just as well because there's no way to get it, so it falls to the application to figure out the keyboard state, which only the window with keyboard focus can do.

We are basically trapped into using the window with keyboard focus.

When the mouse wheel messages were incorporated into the operating system, they decided to give up on supporting 16-bit applications, which opens up another 16 bits of space to provide keyboard state information. And the window manager knows all about keyboard states, so it can figure out what keyboard state to give to the application. But it followed the focus rules to preserve UI compatibility.

It looks like the window manager team in Windows 10 decided to break UI compatibility and deliver the mouse wheel messages to the window that has the mouse. So at least that part of history is now behind us. One down, a few zillion to go.

Bonus trivia: The code name for the mouse that introduced the wheel was Magellan, presumably to reflect the emphasis on navigation. You can still see vestiges of this code name in MSDN, for example, in the list of features new to the RichEdit 2.0 control.

¹ A reasonable guess would be that the prefix MSH stands for Microsoft Hardware.

² I guess you could use Get­Async­Key­State to see if the Ctrl key is down, but that is a race condition because the key may not have been pressed at the time the wheel turned, but it was pressed when you processed the wheel message. (Or vice versa.)

Comments (38)
  1. Jon says:

    It appears the code from scroll window under mouse is at least there in Windows 8.1 as you can activate the functionality via a registry edit. Sadly it does not appear to behave correctly with applications that are not DPI aware so on high resolution monitors you can only scroll while in the upper left corner of the window, I haven’t been able to test if this is solved in Windows 10 but it has meant that I am still relying on 3rd party solutions for this functionality in Windows 8.1

  2. Karellen says:

    Wow, interesting. I read your intro and Douglas’ original comment, and thought, well, having the input event go to the window with focus sounds like the right thing to do. I mean, that’s what focus is for. It sounds like what Douglas actually wants is for the scroll event to switch-focus-and-scroll, in the same manner that left-click event does switch-focus-and-click. (Either that, or Douglas wants focus-follows-mouse.) The current behaviour may have been dictated by history, but it seems to me to be the right behaviour anyway.

    So there I was, about to stick up for the status quo, when with your final line, you mention that this is being fixed![0] I was not expecting that. I guess Douglas will be happy, but it still seems like strange behaviour to a (completely unaffected by the change) focus-follows-mouse user like me.

    [0] For suitable values of “fixed” :-)

    1. Smithers says:

      I rather prefer the Douglas-approved behaviour, for two reasons. Firstly, as he says, things done with the mouse usually happen according to where the mouse is; things done with the keyboard happen according to focus. (Although the guy at the desk next to mine has a keyboard with a scroll wheel in it, but that’s just adding confusion.)

      Secondly, I find this behaviour quite useful. I often find myself editing in one window (say, a text editor or IDE) according to something I’m reading in another window (browser showing feature requirements or a protocol specification). It’s quite handy to leave the focus on the document I’m typing in, but leave the mouse cursor hanging over the other window for when I need to scroll through it.

      1. Karellen says:

        “things done with the mouse usually happen according to where the mouse is”

        As I wrote, I don’t agree with this interpretation of the events that actually happen. In my experience, things done with the mouse usually happen according to where the focus is, with the caveat that things done with the mouse *first set focus to where the mouse is*, and then happen according to where the focus is. If scrolling with the mouse followed these rules, that would seem to fix Douglas’ use-case, and be more consistent with other mouse behaviour.

    2. Drak says:

      You can turn it off or on in Windows 10 using the ‘Mouse & Touchpad’ setting ‘Scroll inactive windows when I hover over them’ :)

  3. Entegy says:

    I wonder what apps did break with the changes in Windows 10. That said, I love the change. Scrolling inactive windows is great for when you’re comparing objects in two windows side by side. (or 4 by 4 now in Windows 10 ;))

  4. Smithers says:

    I’m glad to hear that there’s at least some reason for this, as it’s been annoying me ever since I returned to sometimes using Windows after using exclusively Ubuntu for a couple of years. (Ubuntu [at least with the default GNOME/Unity interface] always scrolls whatever is under the mouse cursor, as Douglas desires.)

    What I also find interesting is the way different applications in Windows respond to this. If Command Prompt has the focus, it scrolls no matter where the cursor is. If Visual Studio has the focus, it directs the scroll to whichever part of itself (source editor, output window etc.) the cursor lies over. Outlook follows Visual Studio’s lead if the cursor is over the window, but falls back to scrolling the panel with keyboard focus if the mouse is elsewhere.

    Most interesting of all is Notepad++. If this application has the focus but the cursor is not over it, it apparently forwards the scroll message to whatever window does have the mouse’s attention, resulting in another application scrolling without having focus!

  5. yeah that change in windows10 infuriated me.
    I like to be able to “spin” a numeric edit control up and down. (focus on edit control, scroll up increases the number, down decreases it.)

    but it turns out there’s a configuration buried in the new control panel to set it back to “the one right way”.

    don’t ask me where it is. I hate going in there. It’s so slow with those scrolling dots now. But it is in there somewhere.

    so, yeah, I’m also infuriated by the control panel making me wait. why can’t a control panel app cache everything it needs?

    1. Drak says:

      Windows key, type ‘mouse’ click ‘Mouse and touchpad settings’, change setting ‘Scroll inactive windows when I hover over them’. No that deeply buried?

  6. creaothceann says:

    Thankfully for us <Win10 users there's tools like WizMouse that "do the right thing".

    By the way, was there really no other way to deliver information to the program? Off the top of my head, the OS could send several messages or provide a callback.

    1. You missed the part where the original implementation was not in the OS.

  7. jon says:

    The new Win10 behavior has ups and downs. Works well for “documents” like web pages, file managers, Word etc. Pain in the ass for “small” targets like combo boxes.

    Incidentally, some may say that the mouse wheel was the last truly useful thing to come out of Microsoft :)

  8. VIMH says:

    As always, a very interesting read.

    I could have sworn, though, that in some previous setup of mine (I want to say it was in the Windows 95/98/98SE/Me series) that the scroll wheel DID scroll the control that was under the cursor. I remember it being frustrating when I realized it no longer worked that way, but I can’t recall when that transition happened.

    It *may* have been a function of a utility I bought called Flywheel back in the Windows 95 days that enabled mouse scrolling functionality in any program (back when, as you said, support wasn’t baked in)… but, I honestly thought it was much later than that era when it worked. I wish I could remember, now.

  9. ender says:

    I bought what was probably the first wheel mouse on the market (Genius EasyScroll), which came with it’s own software that delivered wheel messages to the window under pointer (and the same thing happened with every Genius mouse driver until around 2000). Once I switched to a non-Genius mouse, I was quite annoyed that scrolling under pointer no longer worked (and that there was no support for Alt+Tab with side button+wheel) – at first I used various programs I found around the internet, and then I ended up writing an AutoHotKey script that does this: https://eternallybored.org/misc/AHK/

    1. Yuhong Bao says:

      Of course, Genius probably didn’t care about modifier keys.

  10. pc says:

    It is really hard to imagine that the original purpose of adding the wheel to the mouse would be to enable zooming. Was the MS Hardware team just always in CAD or something and constantly needed to zoom, and figured that everybody else must need that feature too? While I do use Ctrl+Wheel on occasion and have found it useful, I despise web sites that somehow capture the wheel event to make it zoom instead. (And I can’t for the life of me figure out why any sane browser would expose that event to the underlying pages for them to manipulate, but I digress.)

    1. Max says:

      It makes some sense. If someone is using a zoom-heavy application, they usually want to zoom on a specific location or point that’s already in view, so a zooming interaction that doesn’t rely on moving the mouse cursor to click on a “Zoom” button is a big boost in usability – it grants much more potential for control over where the zoom is centered. On the other hand, when you scroll you want to see something different, so being able to scroll without moving your mouse doesn’t grant anywhere near that same level of benefit as being able to zoom without moving the mouse. Of course, zooming could always be implemented via a keyboard command or separate tool option, so the addition of a new input method for zooming wasn’t a critical requirement, but the same is true for scrolling.

    2. Ivan K says:

      Same here. I remember the first scroll wheel mouse I used was a new computer either at work or a university engineering co-op job way back when. At first I thought it would be an obtrusive nuisance, and within a half hour (or less) I was annoyed at vertical scroll bars for the screen pixels they wasted. (Of course scroll bars still have their use for dragging down/up large ‘distances’)

      1. Ivan K says:

        … and of course they are also good for a visual cue as to how large the document is and the current view position. Actually, scrollbars are still pretty useful.

        1. Mason Wheeler says:

          Exactly. Even if I never click and drag on them anymore, I wouldn’t want to get rid of scroll bars for precisely this reason.

          1. Tim! says:

            If they were not click targets, scroll bars could be quite a bit narrower and still serve the purpose of determining document length and current position.

            That said, I quite often drag the scroll thumb to scroll more quickly and precisely than the wheel allows, and I absolutely love the (inconsistently implemented) behavior with which I can drag the scroll thumb to reference another part of a document, then drag outside the bounds to cancel the scroll and snap back to my original position.

    3. Evan says:

      > I despise web sites that somehow capture the wheel event to make it zoom instead. (And I can’t for the life of me figure out why any sane browser would expose that event to the underlying pages for them to manipulate, but I digress.)

      What websites even do this? The only ones I can think of are maps, and there I think that zooming is *absolutely* the right thing to do, to the point where if the wheel *didn’t* scroll, those sites would likely be be far more obnoxious.

      1. Simon says:

        I’ve seen a few web-apps that do it… not always maps, but other non-standard widgets where the developer has decided that some sort of zoom behaviour is more appropriate in this app than scrolling would be. Sometimes they’re right, sometimes not… it’s usually acceptable if the app uses a drag-to-scroll model like maps… not so much if they don’t.

      2. pc says:

        No, maps should work like other sites. I want the scrolling the wheel to pan vertically (and tilting the wheel if available to pan horizontally), just like every other application. It’s frustrating to me every time.

        The worst case is when there’s an embedded map (I often run into it on the weather.gov local forecast page for example), where one is happily scrolling down the page and then it comes to where the map is embedded, and the wheel stops scrolling the page you’re on and zooms out the embedded map instead. (Bringing this almost back on topic, I guess you could say that it delivers the wheel event to what’s under the mouse rather than to the document as a whole.)

  11. DWalker says:

    I always want the wheel to scroll the window that I am LOOKING AT. The same with keyboard input; it should go to the window I am looking at. I wonder how far off this kind of thing is, for mainstream adoption….

    1. Brian_EE says:

      What if the window that you’re looking at is not the window with focus and is also not the window the mouse is hovering over? Take my situation – I have a 4 monitor setup – code IDE on one, outlook on the second, schematic tool on third and browser on the fourth. Often I lose the mouse and have to wiggle it and scan across the desktop to locate it. Made worse by new-style Office apps that don’t change title bar color anymore to indicate focus so you have no idea where you’re keystrokes and mouse events are going after being interrupted by the guy next to you.

    2. Engywuck says:

      The problem would of course be if you look at one window and want to type in another. Say you read a file and want to “blind write” keypoints in a new file. Or if you read from a printed sheet of paper and type the text – which window should the keypresses go to?

      1. DWalker says:

        Well, now you are just throwing up obstacles!

        How about, “the wheel should scroll, or the keyboard should type, into the window that I want it to”? That covers everything, and it should be easy to implement. :-)

        1. Dan F says:

          @DWalker Yes! So many times I’ve been looking at one window, typing, wondering where my keystrokes are. Or scrolling progressively harder and harder. Please MS. Can you read our minds?

    3. morlamweb says:

      @DWalker: the system “knows” which window you’re looking at by the window that has focus. Accurately tracking the window that you’re “looking” at would require some sophisticated eye-tracking systems (like the ones used in telemetry to gauge users’ reactions to a new interface). And even then, what would it do for those people who type while looking at the keyboard? Where would their keyboard strokes go?

  12. alegr1 says:

    The well-though applications (this category doesn’t include Windows Explorer) got rid of the old behavior already, independent of Windows version. Visual Studio and Acrobat Reader do this even on Windows 7. Chrome does it for the independent parts of browser window (which may not be real windows).

  13. Yuhong Bao says:

    “The historical context is that the mouse wheel in Windows was introduced by the Microsoft Hardware team in 1996. This was not synchronized with an operating system release”
    Interestingly, the IntelliMouse was announced at around the same time as the release of NT4: http://news.microsoft.com/1996/07/22/microsoft-announces-microsoft-intellimouse/

  14. Joshua says:

    And Kerbal Space Program does in fact use it for zoom as intended. Feels natural too.

    1. Simon says:

      A lot of games do, since the idea of zoom in/out tends to be more important to games than document-style vertical scrolling.

      Out of curiosity, how many people have a mouse with a tilt-wheel, and what do you do with it? I just bought a new mouse, and it’s got one… but it doesn’t seem to do anything by default… I guess it has to be mapped to something?

  15. Cesar says:

    This is often an annoyance for people who are used to the common Linux way, where mouse wheel events are delivered to the window under the mouse. But why does the Linux X server deliver events to the window under the mouse? For similarly hacky reasons!

    Back then, Linux was often used with three-button mice (the middle button is often “paste selection”, or in a web browser, “open link in new window/tab”), but the X protocol allowed for five buttons (and in a slightly more limited way, many more buttons). It would have made more sense to treat the scroll wheel as the Z axis, but the protocol only had fields for the X and Y axis. So whoever added mouse wheel support simply mapped it to buttons 4 and 5 (and 6 and 7 if the mouse wheel can scroll sideways; 8 and 9 are the side buttons). The mouse button press/release events already have a field for the currently pressed modifier keys like Ctrl, so their state can be known even without keyboard focus.

    Once you think of “scroll the mouse wheel” as “click a mouse button a number of times”, it’s clear why it’s delivered to the window under the mouse, exactly like any other mouse button click. Programs just had to learn to treat “button 4” as “scroll up” (or zoom if the Ctrl modifier flag is set in the event), and “button 5” as “scroll down”, and no new event type was needed.

  16. Ian Yates says:

    I’m running Win 8.1 and tried this quickly.
    I have Chrome on one side of my screen and LinqPad on the other.
    If Chrome has keyboard focus, I can mouse over Chrome and scroll Chrome, but mousing over LinqPad scrolls neither app.
    If LinqPad has keyboard focus, then I can mouse over Chrome and scroll Chrome, and mouse over LinqPad and scroll LinqPad, and mousing over the bits of the desktop I can see with a scroll makes no change in either app.

    Curious behaviour I hadn’t noticed. I like the history lesson. When a customer, or a new programmer, asks why something in our software is the way it is, I like to give the background and history, why we’ve preserved backwards compatibility but evolved it over time, etc – often inspired by these articles. Of course, some people don’t appreciate it, but for those that do, they *really* appreciate the extra knowledge and I get kudos for really considering changes in our software rather than just breaking backwards compatibility all over the place (or at least that’s how I like to see it!)

    (just tested – IE 11 behaves the same way as Chrome).

    I could go look it up, but I have work to do… It’d be interesting to see what applications do to globally handle these scroll messages. Do, for example, WPF apps get this done by default and WinForms don’t (habits of the era?). If you have an app that embeds an old widget, like a rich edit control, does the widget also have to buy into this newer behaviour if that’s what you want to provide to the user?

    Enough pontificating :)

  17. Neil says:

    Maybe I don’t understand what GetCursorPos, GetMessagePos, GetAsyncKeyState and GetKeyboardState do, but I don’t see is why it doesn’t work for either a) the helper program to call GetCursorPos and GetAsyncKeyState to find out the state as it processes the hardware signal before dispatching the message or b) the recipient to call GetMessagePos and GetKeyboardState to find out the state at the time of the message.

    If you had a spare block of 128 messages, you could use that to encode the keyboard state…

Comments are closed.

Skip to main content