Last time, we learned how to query the global caret position, but we found that it works only for programs that use the system caret. Fancy programs think that the system caret is old and stodgy and prefer to draw their own caret. How can we learn about those custom carets?
We can use the classic Active Accessibility interface IAccessible and ask the focus window for its caret. Programs that draw their own custom caret are expected to respond to this by telling you where their custom caret is.
GUITHREADINFO info = { sizeof(GUITHREADINFO) };
if (GetGUIThreadInfo(0, &info))
{
if (info.flags & GUI_CARETBLINKING)
{
MapWindowPoints(info.hwndCaret, nullptr, (POINT*)&info.rcCaret, 2);
SetCursorPos(info.rcCaret.right - 1, info.rcCaret.bottom - 1);
return;
}
if (info.hwndFocus != nullptr) {
Microsoft::WRL::ComPtr<IAccessible> acc;
if (SUCCEEDED(AccessibleObjectFromWindow(info.hwndFocus, OBJID_CARET,
IID_PPV_ARGS(&acc))) && acc) {
long x, y, cx, cy;
VARIANT vt{};
vt.vt = VT_I4;
vt.lVal = CHILDID_SELF;
if (acc->accLocation(&x, &y, &cx, &cy, vt) == S_OK) {
SetCursorPos(x + cx - 1, y + cy - 1);
return;
}
}
}
}
This detects the caret in most programs that use a custom caret. I tried Visual Studio, Chromium-based programs, and Microsoft Word, and they all worked. (However, Terminal and Calculator in Worksheet mode didn’t work. They fail to report a caret at all. Sad.)
In the original problem formulation, the goal was to move the cursor to the keyboard focus. The keyboard focus might be represented by a caret, but it might be a selected item on the desktop or some other non-textual focus. We’ll look at that next time.
By Terminal, do you mean Microsoft’s Windows Terminal app? I just tested and the Windows 10 emoji picker knows where the caret is, so I guess they expose it to IME but not to accessibility?