is win32-gdi system-driven WM_PAINT flicker free?

Michele Giordano 1 Reputation point
2021-03-09T18:02:54.877+00:00

hi all

running this code (need to remove comments after Sleep, this web editor forbids this keyword for some reason):

#include <windows.h>
#include <wingdi.h>
//
LRESULT CALLBACK proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
        case WM_ERASEBKGND: return true;break;
        case WM_MOUSEMOVE: InvalidateRect(hwnd, 0, 0); break;
        case WM_PAINT:
        {
            InvalidateRect(hwnd,0,0);
            HBRUSH b= CreateSolidBrush(0x000000ff);
            HBRUSH c= CreateSolidBrush(0x0000ff00);
            HBRUSH d= CreateSolidBrush(0x00ff0000);
            RECT r;
            GetClientRect(hwnd,&r);
            PAINTSTRUCT ps;
            HDC hdc=BeginPaint(hwnd,&ps);
            FillRect(hdc,&r, b); 
            Sleep//(10);
            FillRect(hdc,&r, c);
`           Sleep//(10);
            FillRect(hdc,&r,d);
            EndPaint(hwnd,&ps);
            DeleteObject(b);
            DeleteObject(c);
            DeleteObject(d);
        }
        break;
        default:
            return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    return 0;
}

int main()
{
    HWND hwnd=CreateWindow(WC_DIALOG,0,WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,500,500,0,0,0,0);
    SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)proc);

    MSG msg;

    while (true)
    {
        if (GetMessage(&msg, 0, 0, 0) != WM_CLOSE)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 1;
}

(this web codesnippet editor is a real shame)

leads to the title question:

if you resize the window you will not see any flicker (repaint sended by the system)
if you move mouse inside the window, severe flicker will occurr (repaint sended by me)

how to reproduce the system-driven WM_PAINT?

Windows development | Windows API - Win32
{count} votes

3 answers

Sort by: Most helpful
  1. Castorix31 90,686 Reputation points
    2021-03-10T21:53:02.14+00:00

    An approaching code that Windows does on resizing (a lot simplified) =>

    (I have put Beep(5000, 10); in WM_PAINT to be sure it is called )

    And as SongZhu-MSFT said, remove InvalidateRect(hwnd, 0, 0); from WM_PAINT

    case WM_MOUSEMOVE: 
    {
     LockWindowUpdate(hwnd);
     InvalidateRect(hwnd, 0, 0);
     LockWindowUpdate(NULL);     
    
     ValidateRect(hwnd, NULL);
     UpdateWindow(hwnd);
     MSG nMsg;
     PeekMessage(&nMsg, NULL, 0, 0, PM_REMOVE);
     return 0;
    }
    
    1 person found this answer helpful.

  2. Viorel 122.6K Reputation points
    2021-03-09T21:01:04.373+00:00

    Try something like this:

    #include <Uxtheme.h>
    #pragma comment(lib, "UxTheme")
    . . .
    case WM_PAINT:
    {
        InvalidateRect( hwnd, 0, 0 );
        HBRUSH b = CreateSolidBrush( 0x000000ff );
        HBRUSH c = CreateSolidBrush( 0x0000ff00 );
        HBRUSH d = CreateSolidBrush( 0x00ff0000 );
        RECT r;
        GetClientRect( hwnd, &r );
        PAINTSTRUCT ps;
        HDC hdc0 = BeginPaint( hwnd, &ps );
        HDC hdc;
        HPAINTBUFFER hpb = BeginBufferedPaint( hdc0, &ps.rcPaint, BP_BUFFERFORMAT::BPBF_COMPATIBLEBITMAP, NULL, &hdc );
        FillRect( hdc, &r, b );
        Sleep/**/( 10 );
        FillRect( hdc, &r, c );
        Sleep/**/( 10 );
        FillRect( hdc, &r, d );
        EndBufferedPaint( hpb, TRUE );
        EndPaint( hwnd, &ps );
        DeleteObject( b );
        DeleteObject( c );
        DeleteObject( d );
    }
    break;
    

    Also add single calls to BufferedPaintInit and BufferedPaintUnInit.


  3. Song Zhu - MSFT 906 Reputation points
    2021-03-10T03:20:50.967+00:00

    According to the documentation:

    In your painting code, you have two basic options:

    • Paint the entire client area, regardless of the size of the update region. Anything that falls outside of the update region is clipped. That is, the operating system ignores it.
    • Optimize by painting just the portion of the window inside the update region.
    • If you always paint the entire client area, the code will be simpler. If you have complicated painting logic, however, it can be more efficient to skip the areas outside of the update region.

    According to your code, every time you call WM_PAINT, it will restore the window to the color of the first call to FillRect(hdc, &r, b);.Therefore, when modifying the window size, the system optimizes the process of calling WM_PAINT and skips the update of this part of the area.

    If you need to redraw when the window is resized, you can draw the part you need in WM_SIZING instead of WM_PAINT.

    Finally, there is an error in the code. You should not call InvalidateRect in WM_PAINT, which will make your window redraw repeatedly.


    If the answer is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.