惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 【当耐特】
MongoDB | Blog
MongoDB | Blog
Hugging Face - Blog
Hugging Face - Blog
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
B
Blog
小众软件
小众软件
T
Tailwind CSS Blog
MyScale Blog
MyScale Blog
I
InfoQ
Engineering at Meta
Engineering at Meta
Blog — PlanetScale
Blog — PlanetScale
P
Proofpoint News Feed
H
Help Net Security
雷峰网
雷峰网
S
SegmentFault 最新的问题
V
Visual Studio Blog
爱范儿
爱范儿

The Old New Thing

std::call_once vs. std::async - The Old New Thing Magic statics vs. std::call_once - The Old New Thing Why do Microsoft job levels start in the high 50's instead of starting at a sane number like 1? - The Old New Thing Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 1 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing Forcing an ARM64X executable to run as a specific architecture - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing
What’s the opposite of Clip­Cursor that lets me exclude t...
Raymond Chen · 2026-06-10 · via The Old New Thing

A customer wanted to prevent the user from dragging an object into a specific region of their window. Their current implementation detects that the mouse is an in illegal location and uses SetCursorPos to move it to a nearby legal location. However, this creates flicker because the cursor actually does enter the illegal region and then jumps out.

Let’s illustrate this with our scratch program.

POINT g_pt;
const RECT g_rcExclude = { 100, 100, 200, 200 };

RECT ItemRect(POINT pt)
{
    return RECT{ pt.x - 10, pt.y - 10, pt.x + 10, pt.y + 10 };
}

The g_pt variable holds the location of our object, and the g_rcExclude is the rectangle in which the object is forbidden. The ItemRect function produces a bounding rectangle for our object so we can draw something there.

void
PaintContent(HWND hwnd, PAINTSTRUCT* pps)
{
    FillRect(pps->hdc, &g_rcExclude, (HBRUSH)(COLOR_WINDOWTEXT + 1));
    RECT rcItem = ItemRect(g_pt);
    FillRect(pps->hdc, &rcItem, (HBRUSH)(COLOR_APPWORKSPACE + 1));
}

Painting our content is a straightforward matter of drawing the forbidden rectangle in the text color and drawing the object in the app workspace color.

void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
    POINT ptNew = { x, y };

    if (PtInRect(&g_rcExclude, ptNew)) {
        // Clamp to nearest legal position
        int leftMargin = ptNew.x - g_rcExclude.left;
        int topMargin = ptNew.y - g_rcExclude.top;
        int rightMargin = g_rcExclude.right - ptNew.x;
        int bottomMargin = g_rcExclude.bottom - ptNew.y;

        int dx, dy;
        int x, y;
        if (leftMargin < rightMargin) {
            x = g_rcExclude.left;
            dx = leftMargin;
        } else {
            x = g_rcExclude.right;
            dx = rightMargin;
        }
        if (topMargin < bottomMargin) {
            y = g_rcExclude.top;
            dy = topMargin;
        } else {
            y = g_rcExclude.bottom;
            dy = bottomMargin;
        }
        if (dx < dy) {
            ptNew.x = x;
        } else {
            ptNew.y = y;
        }
        POINT ptScreen = ptNew;
        ClientToScreen(hwnd, &ptScreen);
        SetCursorPos(ptScreen.x, ptScreen.y);
    }

    if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {
        RECT rcItem = ItemRect(g_pt);
        InvalidateRect(hwnd, &rcItem, TRUE);
        g_pt = ptNew;
        rcItem = ItemRect(g_pt);
        InvalidateRect(hwnd, &rcItem, TRUE);
    }
}

// Add to WndProc

        HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);

When the mouse moves, we take the mouse position and see if it is in the forbidden rectangle. If so, we update the coordinates to the nearest legal position and move the mouse there with SetCursorPos.

Whether or not we had to update the coordinates, if the result produces a new location, then invalidate the object’s old location (so it will be erased at the next paint cycle), update the object position, and then invalidate the object’s new position (so it will be drawn at the next paint cycle).

When you run this program, you can try to move the mouse into the forbidden rectangle, but the program will shove the mouse back out. However, it flickers a lot bcause the mouse briefly enters the forbidden rectangle before it is expelled from it.

The customer saw that there is a ClipCursor function to constrain the mouse to remain inside a rectangle, but is there an inverse version that forces the mouse to remain outside a rectangle?

There is no such function, but that’s okay.

What you can do when the mouse is in an illegal position is just pretend that it’s in a legal position. Let the user move the mouse into a illegal position, but show the feedback at the nearest legal position, and if they drop the object, let it drop at the nearest legal position.

In the above program, that means we remove the call to SetCursorPos.

void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
    POINT ptNew = { x, y };

    if (PtInRect(&g_rcExclude, ptNew)) {
        // Clamp to nearest legal position
        int leftMargin = ptNew.x - g_rcExclude.left;
        int topMargin = ptNew.y - g_rcExclude.top;
        int rightMargin = g_rcExclude.right - ptNew.x;
        int bottomMargin = g_rcExclude.bottom - ptNew.y;

        int dx, dy;
        int x, y;
        if (leftMargin < rightMargin) {
            x = g_rcExclude.left;
            dx = leftMargin;
        } else {
            x = g_rcExclude.right;
            dx = rightMargin;
        }
        if (topMargin < bottomMargin) {
            y = g_rcExclude.top;
            dy = topMargin;
        } else {
            y = g_rcExclude.bottom;
            dy = bottomMargin;
        }
        if (dx < dy) {
            ptNew.x = x;
        } else {
            ptNew.y = y;
        }
        // POINT ptScreen = ptNew;              
        // ClientToScreen(hwnd, &ptScreen);     
        // SetCursorPos(ptScreen.x, ptScreen.y);
    }

    if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {
        RECT rcItem = ItemRect(g_pt);
        InvalidateRect(hwnd, &rcItem, TRUE);
        g_pt = ptNew;
        rcItem = ItemRect(g_pt);
        InvalidateRect(hwnd, &rcItem, TRUE);
    }
}

This time, we don’t try to punish you for moving the mouse into the forbidden rectangle. But the object won’t follow the mouse into a forbidden region.

Category

Topics

Author

Raymond Chen

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.