How do I find out the size of the mouse cursor?

Raymond

A customer wanted to know how to find out the size of the mouse cursor. They called Get­System­Metrics(SM_CXICON), but the value that was returned didn’t seem to be right.

The SM_CXICON system metric gives you the size of “standard” icons, which is loosely correlated with the size of the mouse cursor, in the sense that people who choose bigger icons tend also to choose bigger mouse cursors. This is, however, just a correlation; there is no requirement that the two values be aligned with each other in any way.

We learned last time that the SM_CXCURSOR tells you the size the mouse cursor would have been in some hypothetical universe, which doesn’t really help you find out what the size of the cursor is in the current universe based in reality.

In order to answer the question “What is the size of the mouse cursor?” you first have to specify which mouse cursor you are interested in, in the form of an HCURSOR. For example, if you are interested in the size of the arrow cursor, you can call LoadCursor(nullptr, IDC_ARROW)

Once you have your HCURSOR, you can call Get­Icon­Info² to obtain information about it. The bitmaps that are used to draw the cursor are available as the hbmMask and hbmCursor. Interpreting these bitmaps is a bit tricky.

For color cursors, the hbmMask and hbmColor bitmaps are the same size, each of which is the size of the cursor.

For monochrome cursors, the hbmMask is twice the height of the cursor (with the AND mask on top and the XOR mask on the bottom), and there is no hbmColor.

Now, the sizes of the bitmap tell you the nominal size of the mouse cursor, but that’s not the same as the apparent size. For example, the bitmaps for the I-beam cursor may be 32 × 32, but on the screen, the I-beam cursor is tall and skinny. To identify the pixels that will be drawn, you need to study the mask (color cursors) or top half of the mask (monochrome cursors) and look for the black pixels.

You can also use the xHotspot and yHotspot members to tell you how the bitmap is positioned relative to the cursor position.

Don’t forget to delete the hbmMask and hbmColor bitmaps when you’re done. Forgetting to clean up these bitmaps is a common source of GDI resource leaks.

¹ If you aren’t interested in any particular cursor, but rather want to know how big the cursors are “in general”, you could make the assumption that the current set of default cursors consists of cursors that are all roughly the same size, and just measure the arrow cursor. This is not too unreasonable an assumption, because users generally prefer consistency among their cursors, and they are unlikely to choose a set of cursors where, say, the arrow cursor is ten times bigger than the I-beam cursor.

² Even though the name of the function says that it gets icon info, the documentation notes that it also works for cursors.

8 comments

Comments are closed. Login to edit/delete your existing comments

  • Adam Marks

    NOTE : This doesn’t work if you resize the mouse cursor by using “Change mouse pointer size” in Settings. The bitmap size of the cursor will still be 32.

  • Dan Bugglin

    “² Even though the name of the function says that it gets icon info, the documentation notes that it also works for cursors.”

    Fun Fact of the Day: IIRC this is because icons and cursors* are pretty much exactly the same internally, except cursors have a hotspot (the precise pixel where actual mouse interactions happen) and icons don’t. I think the file formats might even be the same. It’s been a while though.

    * – Static CUR cursors, not animated ANI ones.

    • skSdnW

      The on-disk formats are the same size but not the same, cursors don’t support planes nor fast BPP lookups because that is where the hotspot is stored. The resource formats have more differences and cursors can in theory be larger than 256×256 pixels.

      • Antonio Rodríguez

        In the beginning (up to Windows 3.11), cursors were limited to 32×32 pixels and 1 bit per pixel (plus anther one for transparency), just the same as a monochrome icon. At that time, the only difference between a monochrome icon and a cursor was the hotspot. Otherwise, the file formats were exactly the same. I remember being able to use a standard icon editor for creating custom cursors back in the day. Of course, the only limitation was the hotspot which should be at the top left corner, because an icon editor can’t set it.

        • Neil Rashbrook

          Actually monochrome cursors support 4 values, black, white, transparent and inverse. Windows 95-2000 also supported colour XOR masks, although normally cursor editors only allowed black and white (which map to transparent and inverse). You could use colour XOR masks to create interesting effects, of which my favourite was an XOR mask of grey (0x808080) to create a semi-inverse effect which worked on any background (the default inverse effect doesn’t work very well on a grey background). Sadly that effect stopped working in Windows XP.

          • Antonio Rodríguez

            > Actually monochrome cursors support 4 values

            Black, white, transparent and inverse. Four values. Two bits per pixel in two planes, equivalent to two monochrome (1 bpp) bitmaps. Just what I said. What I called the “image” is, technically, the XOR mask, and what I called the “transparency”, is the AND mask.

  • Neil Rashbrook

    I wanted to turn the default I-beam cursor back into a .CUR file to be sure that it was the transparency that was somehow confusing Remote Desktop rather than anything else (see my comment on the previous post but one). It turned out to be harder than I anticipated; I ended up hardcoding most of the file, and just copying the bitmap bits from the monochrome bitmap returned from GetIconInfo, although upside-down of course. Strictly speaking, this isn’t necessarily the default cursor, since it’s only one size, but it sufficed for the purpose. (I tried looking at other Win32 APIs to see if I could get cursor info for other sizes but I was unsuccessful.)

  • Brad Robinson

    When a cursor is loaded with LR_DEFAULTSIZE the cursor can dynamically switch resolution when moved from a lo-dpi to hi-dpi monitor. (eg: create a cursor with multiple resolutions (eg 32×32 and 64×64) with different markings on each resolution and move it between hi and lo dpi monitors and you’ll see the cursor change).

    How does that work? And how does it fit in to this topic of working out the icon size? (since GetIconInfo returns the same info regardless of which monitor the cursor is on). What’s the secret magic going on under there covers here? I’m yet to see anything official on this dynamic resolution switching with cursors.

    See also here: https://stackoverflow.com/questions/68810645/loadcursor-and-mixed-dpi-multiple-monitors