{"id":42743,"date":"2003-08-29T10:00:00","date_gmt":"2003-08-29T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2003\/08\/29\/painting-only-when-your-window-is-visible-on-the-screen\/"},"modified":"2003-08-29T10:00:00","modified_gmt":"2003-08-29T10:00:00","slug":"painting-only-when-your-window-is-visible-on-the-screen","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030829-00\/?p=42743","title":{"rendered":"Painting only when your window is visible on the screen"},"content":{"rendered":"<p>\n        Sometimes you want to perform an activity, such as updating a status window, only\n        as long as the window is not covered by another window.\n    <\/p>\n<p>\n        The easiest way to determine this is by not actually trying to determine it. For example,\n        here&#8217;s how the taskbar clock updates itself:\n    <\/p>\n<ol>\n<li>\n            It computes how much time will elapse before the next minute ticks over.\n        <\/li>\n<li>\n            It calls <code>SetTimer<\/code> with the amount of time it needs to wait.\n        <\/li>\n<li>\n            When the timer fires, it does an <code>InvalidateRect<\/code> of itself and the kills\n            the timer.\n        <\/li>\n<li>\n            The <code>WM_PAINT<\/code> handler draws the current time, then returns to step 1.\n        <\/li>\n<\/ol>\n<p>\n        If the taskbar clock is not visible, because it got auto-hidden or because somebody\n        covered it, Windows will not deliver a <code>WM_PAINT<\/code> message, so the taskbar\n        clock will simply go idle and consume no CPU time at all. Here&#8217;s how we can make\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">our\n        scratch program<\/a> do the same thing:\n    <\/p>\n<p>\n        Our scratch program displays the current time. It also puts the time into the title\n        bar so we can see the painting action (or lack thereof) when the window is covered\n        or minimized, by watching the taskbar.\n    <\/p>\n<pre>void\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n    TCHAR szTime[100];\n    if (GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL,\n                      szTime, 100)) {\n        SetWindowText(hwnd, szTime);\n        TextOut(pps-&gt;hdc, 0, 0, szTime, lstrlen(szTime));\n    }\n}\n<\/pre>\n<p>\n        Here is the timer callback that fires once we decide it&#8217;s time to update. It merely\n        kills the timer and invalidates the rectangle. The next time the window becomes uncovered,\n        we will get a <code>WM_PAINT<\/code> message. (And if the window is uncovered right\n        now, then we&#8217;ll get one almost immediately.)\n    <\/p>\n<pre>void CALLBACK\nInvalidateAndKillTimer(HWND hwnd, UINT uMsg,\n                       UINT_PTR idTimer, DWORD dwTime)\n{\n    KillTimer(hwnd, idTimer);\n    InvalidateRect(hwnd, NULL, TRUE);\n}\n<\/pre>\n<p>\n        Finally, we add some code to our <code>WM_PAINT<\/code> handler to restart the timer\n        each time we paint a nonempty rectangle.\n    <\/p>\n<pre>void\nOnPaint(HWND hwnd)\n{\n    PAINTSTRUCT ps;\n    BeginPaint(hwnd, &amp;ps);\n    <font color=\"blue\">if (!IsRectEmpty(&amp;ps.rcPaint)) {\n        \/\/ compute time to next update - we update once a second\n        SYSTEMTIME st;\n        GetSystemTime(&amp;st);\n        DWORD dwTimeToNextTick = 1000 - st.wMilliseconds;\n        SetTimer(hwnd, 1, dwTimeToNextTick, InvalidateAndKillTimer);\n    }<\/font>\n    PaintContent(hwnd,&amp;ps);\n    EndPaint(hwnd, &amp;ps);\n}\n<\/pre>\n<p>\n        Compile and run this program, and watch it update the time. When you minimize the\n        window or cover it with another window, the time stops updating. If you take the window\n        and drag it to the bottom of the screen so only the caption is visible, it also stops\n        updating: The <code>WM_PAINT<\/code> message is used to paint the client area, and\n        the client area is no longer on-screen.\n    <\/p>\n<p>\n        This method also stops updating the clock when you switch to another user or lock\n        the workstation, though you can&#8217;t really tell because there&#8217;s no taskbar you can consult\n        to verify. But you can use your speakers: Stick a call to <code>MessageBeep(-1);<\/code> in\n        the <code>PaintContent()<\/code> function, so you will get an annoying beep each time\n        the time is repainted. When you switch to another user or lock the workstation, the\n        beeping will stop.\n    <\/p>\n<p>\n        This technique of invalidation can be extended to cover the case where only one section\n        of the screen is interesting: Instead of invalidating the entire client area, invalidate\n        only the area that you want to update, and restart the timer only if that rectangle\n        is part of the update region. Here are the changes we need to make.\n    <\/p>\n<pre>\/\/ The magic updating rectangle\nRECT g_rcTrigger = { 50, 50, 200, 100 };\n<\/pre>\n<p>\n        When the timer fires, we invalidate only the magic rectangle instead of the entire\n        client area. (As an optimization, I disabled background erasure for reasons you&#8217;ll\n        see later.)\n    <\/p>\n<pre>void CALLBACK\nInvalidateAndKillTimer(HWND hwnd, UINT uMsg,\n                       UINT_PTR idTimer, DWORD dwTime) {\n    KillTimer(hwnd, idTimer);\n    <font color=\"blue\">InvalidateRect(hwnd, &amp;g_rcTrigger, FALSE);<\/font>\n}\n<\/pre>\n<p>\n        To make it more obvious where the magic rectangle is, we draw it in the highlight\n        color and put the time inside it. By using the <code>ETO_OPAQUE<\/code> flag, we draw\n        both the foreground and background simultaneously. Consequently, we don&#8217;t need to\n        have it erased for us.\n    <\/p>\n<pre>void\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n    TCHAR szTime[100];\n    if (GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL,\n                      szTime, 100)) {\n        SetWindowText(hwnd, szTime);\n        COLORREF clrTextPrev = SetTextColor(pps-&gt;hdc,\n                            GetSysColor(COLOR_HIGHLIGHTTEXT));\n        COLORREF clrBkPrev = SetBkColor(pps-&gt;hdc,\n                                GetSysColor(COLOR_HIGHLIGHT));\n        ExtTextOut(pps-&gt;hdc, g_rcTrigger.left, g_rcTrigger.top,\n                   ETO_CLIPPED | ETO_OPAQUE, &amp;g_rcTrigger,\n                   szTime, lstrlen(szTime), NULL);\n        SetBkColor(pps-&gt;hdc, clrBkPrev);\n        SetTextColor(pps-&gt;hdc, clrTextPrev);\n    }\n}\n<\/pre>\n<p>\n        Finally, the code in the <code>WM_PAINT<\/code> handler needs to check the magic rectangle\n        for visibility instead of using the entire client area.\n    <\/p>\n<pre>void\nOnPaint(HWND hwnd)\n{\n    PAINTSTRUCT ps;\n    BeginPaint(hwnd, &amp;ps);\n    if (<font color=\"blue\">RectVisible(ps.hdc, &amp;g_rcTrigger)<\/font>) {\n        \/\/ compute time to next update - we update once a second\n        SYSTEMTIME st;\n        GetSystemTime(&amp;st);\n        DWORD dwTimeToNextTick = 1000 - st.wMilliseconds;\n        SetTimer(hwnd, 1, dwTimeToNextTick, InvalidateAndKillTimer);\n    }\n    PaintContent(hwnd,&amp;ps);\n    EndPaint(hwnd, &amp;ps);\n}\n<\/pre>\n<p>\n        Run this program and do various things to cover up or otherwise prevent the highlight\n        box from painting. Observe that once you cover it up, the title stops updating.\n    <\/p>\n<p>\n        As I noted above, this technique is usually enough for most applications. There&#8217;s\n        an even more complicated (and more expensive) method, too, which I&#8217;ll cover next week.\n    <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes you want to perform an activity, such as updating a status window, only as long as the window is not covered by another window. The easiest way to determine this is by not actually trying to determine it. For example, here&#8217;s how the taskbar clock updates itself: It computes how much time will elapse [&hellip;]<\/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-42743","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sometimes you want to perform an activity, such as updating a status window, only as long as the window is not covered by another window. The easiest way to determine this is by not actually trying to determine it. For example, here&#8217;s how the taskbar clock updates itself: It computes how much time will elapse [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/42743","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=42743"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/42743\/revisions"}],"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=42743"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=42743"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=42743"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}