{"id":112412,"date":"2026-06-10T07:00:00","date_gmt":"2026-06-10T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112412"},"modified":"2026-06-10T23:13:57","modified_gmt":"2026-06-11T06:13:57","slug":"20260610-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260610-00\/?p=112412","title":{"rendered":"What&#8217;s the opposite of <CODE>Clip&shy;Cursor<\/CODE> that lets me <I>exclude<\/I> the cursor from a region?"},"content":{"rendered":"<p>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 <code>Set\u00adCursor\u00adPos<\/code> 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.<\/p>\n<p>Let&#8217;s illustrate this with <a title=\"The scratch program\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> our scratch program<\/a>.<\/p>\n<pre>POINT g_pt;\r\nconst RECT g_rcExclude = { 100, 100, 200, 200 };\r\n\r\nRECT ItemRect(POINT pt)\r\n{\r\n    return RECT{ pt.x - 10, pt.y - 10, pt.x + 10, pt.y + 10 };\r\n}\r\n<\/pre>\n<p>The <code>g_pt<\/code> variable holds the location of our object, and the <code>g_rcExclude<\/code> is the rectangle in which the object is forbidden. The <code>ItemRect<\/code> function produces a bounding rectangle for our object so we can draw something there.<\/p>\n<pre>void\r\nPaintContent(HWND hwnd, PAINTSTRUCT* pps)\r\n{\r\n    FillRect(pps-&gt;hdc, &amp;g_rcExclude, (HBRUSH)(COLOR_WINDOWTEXT + 1));\r\n    RECT rcItem = ItemRect(g_pt);\r\n    FillRect(pps-&gt;hdc, &amp;rcItem, (HBRUSH)(COLOR_APPWORKSPACE + 1));\r\n}\r\n<\/pre>\n<p>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.<\/p>\n<pre>void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)\r\n{\r\n    POINT ptNew = { x, y };\r\n\r\n    if (PtInRect(&amp;g_rcExclude, ptNew)) {\r\n        \/\/ Clamp to nearest legal position\r\n        int leftMargin = ptNew.x - g_rcExclude.left;\r\n        int topMargin = ptNew.y - g_rcExclude.top;\r\n        int rightMargin = g_rcExclude.right - ptNew.x;\r\n        int bottomMargin = g_rcExclude.bottom - ptNew.y;\r\n\r\n        int dx, dy;\r\n        int x, y;\r\n        if (leftMargin &lt; rightMargin) {\r\n            x = g_rcExclude.left;\r\n            dx = leftMargin;\r\n        } else {\r\n            x = g_rcExclude.right;\r\n            dx = rightMargin;\r\n        }\r\n        if (topMargin &lt; bottomMargin) {\r\n            y = g_rcExclude.top;\r\n            dy = topMargin;\r\n        } else {\r\n            y = g_rcExclude.bottom;\r\n            dy = bottomMargin;\r\n        }\r\n        if (dx &lt; dy) {\r\n            ptNew.x = x;\r\n        } else {\r\n            ptNew.y = y;\r\n        }\r\n        POINT ptScreen = ptNew;\r\n        ClientToScreen(hwnd, &amp;ptScreen);\r\n        SetCursorPos(ptScreen.x, ptScreen.y);\r\n    }\r\n\r\n    if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {\r\n        RECT rcItem = ItemRect(g_pt);\r\n        InvalidateRect(hwnd, &amp;rcItem, TRUE);\r\n        g_pt = ptNew;\r\n        rcItem = ItemRect(g_pt);\r\n        InvalidateRect(hwnd, &amp;rcItem, TRUE);\r\n    }\r\n}\r\n\r\n\/\/ Add to WndProc\r\n\r\n        HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);\r\n<\/pre>\n<p>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 <code>Set\u00adCursor\u00adPos<\/code>.<\/p>\n<p>Whether or not we had to update the coordinates, if the result produces a new location, then invalidate the object&#8217;s old location (so it will be erased at the next paint cycle), update the object position, and then invalidate the object&#8217;s new position (so it will be drawn at the next paint cycle).<\/p>\n<p>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.<\/p>\n<p>The customer saw that there is a <code>Clip\u00adCursor<\/code> function to constrain the mouse to remain <i>inside<\/i> a rectangle, but is there an inverse version that forces the mouse to remain <i>outside<\/i> a rectangle?<\/p>\n<p>There is no such function, but that&#8217;s okay.<\/p>\n<p>What you can do when the mouse is in an illegal position is just pretend that it&#8217;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.<\/p>\n<p>In the above program, that means we remove the call to <code>Set\u00adCursor\u00adPos<\/code>.<\/p>\n<pre>void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)\r\n{\r\n    POINT ptNew = { x, y };\r\n\r\n    if (PtInRect(&amp;g_rcExclude, ptNew)) {\r\n        \/\/ Clamp to nearest legal position\r\n        int leftMargin = ptNew.x - g_rcExclude.left;\r\n        int topMargin = ptNew.y - g_rcExclude.top;\r\n        int rightMargin = g_rcExclude.right - ptNew.x;\r\n        int bottomMargin = g_rcExclude.bottom - ptNew.y;\r\n\r\n        int dx, dy;\r\n        int x, y;\r\n        if (leftMargin &lt; rightMargin) {\r\n            x = g_rcExclude.left;\r\n            dx = leftMargin;\r\n        } else {\r\n            x = g_rcExclude.right;\r\n            dx = rightMargin;\r\n        }\r\n        if (topMargin &lt; bottomMargin) {\r\n            y = g_rcExclude.top;\r\n            dy = topMargin;\r\n        } else {\r\n            y = g_rcExclude.bottom;\r\n            dy = bottomMargin;\r\n        }\r\n        if (dx &lt; dy) {\r\n            ptNew.x = x;\r\n        } else {\r\n            ptNew.y = y;\r\n        }\r\n        <span style=\"border: dashed 1px currentcolor; border-bottom: none;\">\/\/ <span style=\"text-decoration: line-through;\">POINT ptScreen = ptNew;<\/span>              <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none dashed;\">\/\/ <span style=\"text-decoration: line-through;\">ClientToScreen(hwnd, &amp;ptScreen);<\/span>     <\/span>\r\n        <span style=\"border: dashed 1px currentcolor; border-top: none;\">\/\/ <span style=\"text-decoration: line-through;\">SetCursorPos(ptScreen.x, ptScreen.y);<\/span><\/span>\r\n    }\r\n\r\n    if (g_pt.x != ptNew.x || g_pt.y != ptNew.y) {\r\n        RECT rcItem = ItemRect(g_pt);\r\n        InvalidateRect(hwnd, &amp;rcItem, TRUE);\r\n        g_pt = ptNew;\r\n        rcItem = ItemRect(g_pt);\r\n        InvalidateRect(hwnd, &amp;rcItem, TRUE);\r\n    }\r\n}\r\n<\/pre>\n<p>This time, we don&#8217;t try to punish you for moving the mouse into the forbidden rectangle. But the object won&#8217;t follow the mouse into a forbidden region.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is no such feature, but you can just exclude it virtually.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112412","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>There is no such feature, but you can just exclude it virtually.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112412","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112412"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112412\/revisions"}],"predecessor-version":[{"id":112413,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112412\/revisions\/112413"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112412"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112412"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112412"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}