January 12th, 2026
like1 reaction

Clipping the focus item when looking for its on-screen location

Last time, we used Active Accessibility to locate the focus item, but I noted that there was a quirk.

The quirk is that the focus item might be clipped.

For example, if you take a File Explorer window, put it into Details mode, and then resize the window to be narrow enough that a horizontal scroll bar appears, then when you ask for the focus item, the accLocation reports where item is on the screen, even though some of it might be clipped out. Our program ends up putting the cursor far outside the File Explorer window, pointing to the bottom right corner of the unclipped item.

My first attempt to fix this was to clip to the focus window.

bool SetCursorPosToLocation(IAccessible* acc, LONG childId, HWND hwndClip)
{
    long x, y, cx, cy;
    VARIANT vt;
    vt.vt = VT_I4;
    vt.lVal = childId;
    if (acc->accLocation(&x, &y, &cx, &cy, vt) == S_OK) {
        RECT rcObject = { x, y, x + cx, y + cy };              
        RECT rcClient;                                         
        if (hwndClip && GetClientRect(hwndClip, &rcClip)) {    
            MapWindowPoints(hwndClip, nullptr,                 
                            (POINT*)&rcObject, 2);             
            if (IsWindow(hwndClip)) {                          
                IntersectRect(&rcObject, &rcObject, &rcClient);
            }                                                  
        }                                                      
        SetCursorPos(rcObject.right - 1, rcObject.bottom - 1); 
        return true;
    }
    return false;
}

We add a parameter to Set­Cursor­Pos­To­Location to provide an optional clip window. If present, we clip the accessible object’s rectangle to the window’s client area. Getting the window’s client area in screen coordinates is a bit tricky: We get the client area in client coordinates, then map the coordinates to screen coordinates. A zero return value from Map­Window­Points function is ambiguous: It could mean failure, or it could mean that the rectangle did not need to be adjusted. Instead of trying to figure out which case we are in, I just revalidate the window handle afterward, on the theory that the only thing that could result in a failure from Map­Window­Points (aside from invalid pointers) is that the window was destroyed while we were talking about it.

If we are able to get the screen coordinates of the clip window’s client area, we intersect it with the item rectangle to find the visible rectangle for the focus item. And that’s the rectangle that we use to position the cursor.

Of course, we also have to pass the clip window.

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) {
        wil::com_ptr_nothrow<IAccessible> acc;
        if (SUCCEEDED(AccessibleObjectFromWindow(info.hwndFocus, OBJID_CARET,
                             IID_PPV_ARGS(acc.put()))) && acc) {
            if (SetCursorPosToLocation(acc.get(), CHILDID_SELF,
                                       info.hwndFocus)) {
                return;
            }
        }
        if (SUCCEEDED(AccessibleObjectFromWindow(info.hwndFocus, OBJID_CLIENT,
                             IID_PPV_ARGS(acc.put()))) && acc) {
            wil::unique_variant vt;
            if (acc->get_accFocus(&vt) == S_OK) {
                auto [childAcc, childId] = GetChild(acc.get(), vt);
                if (childAcc &&
                    SetCursorPosToLocation(childAcc.get(), childId,
                                           info.hwndFocus)) {
                    return;
                }
            }
        }
    }
}

I tried this out, and it worked for the most part, but for some programs, the clipping was too aggressive. Sometimes the focus window was 0×0! One way this could happen is when a program creates a dummy window and keeps keyboard focus on it, using it to soak up and distribute all of the program’s input to other windows. As a result, the focus item is not actually constrained by the focus window.

We’ll address this problem next time.

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.

0 comments