September 16th, 2024

Some notes on Win32 carets

In Win32, the caret is a visual indication of where the next typed character will be inserted. It is traditionally a thin vertical bar.

Note that all defaults listed below are as of this writing. The values are not contractual, but I’m just providing them for illustration.

You can ask for the caret blink rate by calling the Get­Caret­Blink­Time function (default: 500ms). Note that the caret will stop blinking after a while (default: 5 seconds) to avoid running timers indefinitely if they have a period shorter than one minute. Furthermore, on Remote Desktop connections, caret blinking might be disable entirely. This avoids long-running timers on systems that may be hosting multiple simultaneous interactive users. When you have 100 users signed in, each with a blinking caret, even a timer that fires only once every 500ms is running at a high enough frequency to cause measurable energy consumption.

The caret blink rate also controls the default blink rate used by the Flash­Window­Ex function, and when the system flashes the title bar of a window when you click on its disabled owner, it does so (today) at 8 times the caret blink rate.

When you create a caret, you can let the system choose the dimensions by setting the width and height to zero. Today, this uses the system-defined window border width and height. Unfortunately, the window manager lets you down here: The recommended width is not the window border width but the caret width, which comes from System­Parameters­Info with SPI_GET­CARET­WIDTH. So those defaults don’t help you much; they’re the wrong values.

Another way to create a caret is to use a bitmap. In that case, it is your responsibility to keep the bitmap handle valid for as long as the bitmap caret is in use, viz. until the caret is destroyed, or it is replaced by another caret.

In all cases, the system draws the caret by the classic algorithm of XOR. This works okay if the background is white or black (or close to it). Not so great if the background is some other color, and definitely not so great if the background is gray (because inverted gray is still gray).

In the case where you set the caret to a bitmap, the system will still use XOR to draw it. If your bitmap is not a monochrome bitmap, then things will probably look kind of weird because XOR’ing two colors together tends to produce non-intuitive results.

I guess what you could do is XOR the background color with your desired caret color, and use that color as the color in your caret bitmap. Through the magic of XOR, when the caret bitmap is XOR’d with the background color, your desired caret color pops out.

background ^ (background ^ foreground) = foreground

This is really more of a hack based on a mathematical quirk than an intended design behavior. Bitmap carets were intended to be used with monochrome bitmaps.

There you go, a bunch of loosely-connected paragraphs on Win32 carets.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

5 comments

Discussion is closed. Login to edit/delete existing comments.

  • Neil Rashbrook

    Firefox started honouring this preference as of Firefox 93. But my feed reader is based on Firefox 52, so I never noticed the blink change until recently when the comment field stopped working completely on that old version.

  • Georg Rottensteiner

    This 5 second blinking stop option is probably one of these “you cannot win” situations.

    I’m writing a little IDE for retro development, and users basically complained that the caret vanished 5 seconds in. Which is really really annoying when you want to keep working and just looked somewhere else for a second (or 5 seconds, hurr durr).

    I resorted to a hack-around, that would re-create the caret every 5 seconds. That doesn’t feel clean.
    It is...

    Read more
    • Neil Rashbrook

      if your users don’t want their caret to stop blinking in your application, they might well be able to live with it keeping blinking in other apps. (In my case, that other app is Command Prompt, which always blinks somehow. Maybe it’s not a “real” caret?) If that’s the case, there’s a registry entry they could change…

      • Kalle Niemitalo

        For console applications, the blinking cursor is implemented in the console host (conhost.exe), whose source code is in the Windows Terminal repository on GitHub. It calls GetCaretBlinkTime and sets its own thread-pool timer. It uses neither CreateCaret nor SetCaretPos, but it seems to publish the location of the cursor via a private API in user32.dll for accessibility purposes.

    • Tobias Käs · Edited

      You can do the WPF thing and create the caret with a blank bitmap but correct size, so XOR’ing doesn’t modify the screen but still allows accessibility and other kinds of applications to react to the caret placement and position. Then you can take care of rendering the actual visual caret yourself, with a timer for blinking, and adjust the behavior to work like you desire.