1

I'm trying to design an abstraction layer for windows for various platforms. I decided to add a callback for when a window, or a section of it needs to be redrawn. I'm trying to make it fit into this callback type:

using paint_callback_t = std::function<void(int, int, unsigned int, unsigned int, void*, std::size_t)>;

Where the first 4 parameters are the X, Y coordinates in client space and the width and height of the region that needs to be painted. The problem is in Win32 API there is no obvious way to get the starting X and Y coordinates of a dirty region. When I use BeginPaint in the WM_PAINT case of the WndProc, it returns a device context with it's clipping already set so you can't paint outside the region and the rcPaint member of the PAINTSTRUCT is relative to the clipping space, meaning left and top are 0. GetClipBox also returns the same RECT starting at 0, 0.

Does anyone know how to achieve this? I'm guessing there is some way of maybe creating a Region and then intersecting with the original one to return the original one somehow? I'm really lost on how to achieve this and would appreciate any help.

  • 1
    It is indeed rcPaint, as documented it is relative to the client area of the window. Getting (0, 0) for the upper-left corner is extremely common since Aero, you'd have to drag the window off the screen to the bottom/right to get a different value. – Hans Passant Jul 21 '23 at 18:10
  • @HansPassant Where do you see that it's relative to the client area? When I drag the window outside the screen, I still see the top and left be (0, 0), even in that situation. I am creating a minimal example to check if this is an issue with my implementation and also to help with the answers. – Alexandre Deus Jul 21 '23 at 18:22
  • 1
    What do you mean by 'relative to the clipping space'? `rcPaint` is in client coordinates. If you want to paint on the window frame or title bar, you need to handle `WM_NCPAINT`. – Paul Sanders Jul 21 '23 at 18:26
  • 1
    @PaulSanders: The clipping space can be much smaller than the window. For example if a messagebox pops up over the window, after dismissing the popup, only the portion of the window that the popup obscured is dirty and requires repainting. However in such a case, the `rcPaint` should not be starting at (0,0). – Ben Voigt Jul 21 '23 at 18:28
  • 1
    @BenVoigt Exactly. It's not clear to me what the OP is actually seeing / doing. – Paul Sanders Jul 21 '23 at 18:31
  • @PaulSanders @HansPassant @BenVoigt It seems at least 90% of the `WM_PAINT` messages from dragging offscreen are full repaints of the already visible area, which is what confused me from looking at the values in debugging. You guys are right, the coordinates appear to already be in client space. – Alexandre Deus Jul 21 '23 at 18:49
  • Remember that windows are now rendered to an off-screen frame buffer and then composited. – Jonathan Potter Jul 21 '23 at 23:08

1 Answers1

0

It turns out the rcPaint IS in client space. It's just that dragging the window offscreen performs a lot of full redraws of the visible client area. I made this test program to check the values:

#include <iostream>

#include <windows.h>

LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_PAINT:
        {
            PAINTSTRUCT ps{};
            HDC hdc = BeginPaint(hwnd, &ps);

            std::cout << "Dirty rect: Left=" << ps.rcPaint.left << ", Top=" << ps.rcPaint.top << ", Bottom=" << ps.rcPaint.bottom << ", Right=" << ps.rcPaint.right << "\n";

            EndPaint(hwnd, &ps);

            return 1;
        }
    }

    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

int main(int argc, char* argv[])
{
    HINSTANCE instance = GetModuleHandleW(NULL);

    WNDCLASSW cls{};
    cls.lpfnWndProc = &wndproc;
    cls.hInstance = instance;
    cls.lpszClassName = L"ExampleClass";
    RegisterClassW(&cls);

    HWND hwnd = CreateWindowW(L"ExampleClass", L"Example Window", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, NULL, NULL, instance, NULL);
    ShowWindow(hwnd, SW_NORMAL);

    MSG msg;
    while (GetMessageW(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return 0;
}

And this is an example of the messages I got:

Dirty rect: Left=0, Top=0, Bottom=283, Right=435
Dirty rect: Left=0, Top=0, Bottom=287, Right=440
Dirty rect: Left=0, Top=0, Bottom=290, Right=444
Dirty rect: Left=0, Top=0, Bottom=292, Right=448
Dirty rect: Left=0, Top=0, Bottom=293, Right=451
Dirty rect: Left=0, Top=0, Bottom=295, Right=453
Dirty rect: Left=0, Top=0, Bottom=296, Right=455
Dirty rect: Left=0, Top=0, Bottom=298, Right=457
Dirty rect: Left=0, Top=0, Bottom=299, Right=460
Dirty rect: Left=0, Top=0, Bottom=301, Right=462
Dirty rect: Left=0, Top=0, Bottom=303, Right=464
Dirty rect: Left=0, Top=0, Bottom=305, Right=466
Dirty rect: Left=0, Top=0, Bottom=306, Right=468
Dirty rect: Left=0, Top=0, Bottom=307, Right=470
Dirty rect: Left=0, Top=0, Bottom=308, Right=471
Dirty rect: Left=471, Top=0, Bottom=308, Right=472
Dirty rect: Left=472, Top=0, Bottom=308, Right=473
Dirty rect: Left=473, Top=0, Bottom=308, Right=474
Dirty rect: Left=0, Top=0, Bottom=309, Right=477
Dirty rect: Left=477, Top=0, Bottom=309, Right=478
Dirty rect: Left=478, Top=0, Bottom=309, Right=479
Dirty rect: Left=479, Top=0, Bottom=309, Right=480

Thank you for the help.