From: Paul Gofman Subject: Re: [PATCH] winex11.drv: Redraw window on Expose event if window surface has no region set. Message-Id: Date: Mon, 6 Apr 2020 15:12:17 +0300 In-Reply-To: <31d80570-6d4e-bb25-a6ae-291ec31b1e69@gmail.com> References: <20200403123457.20981-1-gofmanp@gmail.com> <87ftdghq08.fsf@wine> <31d80570-6d4e-bb25-a6ae-291ec31b1e69@gmail.com> On 4/6/20 14:49, Paul Gofman wrote: > On 4/6/20 13:23, Alexandre Julliard wrote: >> Paul Gofman writes: >> >>> Otherwise the window (partially) obscured by the other window sometimes fails >>> to update when the obscuring window is moved or destroyed. >> That's not correct, there shouldn't be any reason to repaint the entire >> window when the contents are still valid. >> >> Note that not flushing the bits right away is deliberate, that's a >> performance fix. The window will get repainted when the app waits for a >> new message. >> >> If you have a real app where this is an issue, the heuristic could maybe >> be tweaked to catch that case, cf. check_for_driver_events in >> dlls/user32/message.c. >> > I was trying to fix menu glitches in Diablo 1. This particular patch was > fixing leftover menu elements (left from previous menu when navigating). > The application destroys child window and expects the parent to receive > WM_PAINT with appropriate rectangle in PAINTSTRUCT. Tweaking > check_for_driver_events() (or otherwise enforcing extra > flush_window_surfaces()) helps drawing in my test I attached previously, > but not the game. Test program (and the game) still does not receive > WM_PAINT event for (partially obscured) parent window. I suppose it > fixes the drawing in my test program because the bits of parent window > gets restored from x11 image in x11drv_surface_flush(). But Diablo 1 > does not draw on GDI surfaces. It uses user32 window management > infrastructure and reacts on the window events, but all the actual > drawing goes into ddraw primary surface in exclusive fullscreen mode. > And it depends on receiving the WM_PAINT event the same way as on > Windows to do the necessary updates. I should note that ddraw primary > surface drawing currently does not work correctly as the fullscreen > window gets obscured by the other windows. I use some WIP workarounds to > make exclusive fullscreen mode to work. > > So should I maybe think of how to introduce WM_PAINT generation from > within check_for_driver_events()? Or leave this for now since exclusive > fullscreen mode does not work right anyway at the moment and this is > actually a bigger problem? > > I have updated the test program to illustrate this. With the updated test programs, the rectangles exposed after moving or deleting obscured window are now filled with blue on Windows. If I force flush_window_surfaces() in Wine, the old window bits are restored and the exposed rectangles stay green. #include #include static const char *dbgstr_wm(UINT msg) { #define WN(a) {a, #a} static const struct { UINT msg; const char *s; } msgs[] = { WN(WM_NULL), WN(WM_CREATE), WN(WM_DESTROY), WN(WM_MOVE), WN(WM_SIZE), WN(WM_ACTIVATE), WN(WM_SETFOCUS), WN(WM_KILLFOCUS), WN(WM_ENABLE), WN(WM_SETREDRAW), WN(WM_SETTEXT), WN(WM_GETTEXT), WN(WM_GETTEXTLENGTH), WN(WM_PAINT), WN(WM_CLOSE), WN(WM_QUERYENDSESSION), WN(WM_QUIT), WN(WM_QUERYOPEN), WN(WM_ERASEBKGND), WN(WM_SYSCOLORCHANGE), WN(WM_ENDSESSION), WN(WM_SHOWWINDOW), WN(WM_WININICHANGE), WN(WM_SETTINGCHANGE), WN(WM_DEVMODECHANGE), WN(WM_ACTIVATEAPP), WN(WM_FONTCHANGE), WN(WM_TIMECHANGE), WN(WM_CANCELMODE), WN(WM_SETCURSOR), WN(WM_MOUSEACTIVATE), WN(WM_CHILDACTIVATE), WN(WM_QUEUESYNC), WN(WM_GETMINMAXINFO), WN(WM_PAINTICON), WN(WM_ICONERASEBKGND), WN(WM_NEXTDLGCTL), WN(WM_SPOOLERSTATUS), WN(WM_DRAWITEM), WN(WM_MEASUREITEM), WN(WM_DELETEITEM), WN(WM_VKEYTOITEM), WN(WM_CHARTOITEM), WN(WM_SETFONT), WN(WM_GETFONT), WN(WM_SETHOTKEY), WN(WM_GETHOTKEY), WN(WM_QUERYDRAGICON), WN(WM_COMPAREITEM), WN(WM_COMPACTING), WN(WM_WINDOWPOSCHANGING), WN(WM_WINDOWPOSCHANGED), WN(WM_POWER), WN(WM_COPYDATA), WN(WM_CANCELJOURNAL), WN(WM_NOTIFY), WN(WM_INPUTLANGCHANGEREQUEST), WN(WM_INPUTLANGCHANGE), WN(WM_TCARD), WN(WM_HELP), WN(WM_USERCHANGED), WN(WM_NOTIFYFORMAT), WN(WM_CONTEXTMENU), WN(WM_STYLECHANGING), WN(WM_STYLECHANGED), WN(WM_DISPLAYCHANGE), WN(WM_GETICON), WN(WM_SETICON), WN(WM_NCCREATE), WN(WM_NCDESTROY), WN(WM_NCCALCSIZE), WN(WM_NCHITTEST), WN(WM_NCPAINT), WN(WM_NCACTIVATE), WN(WM_GETDLGCODE), WN(WM_NCMOUSEMOVE), WN(WM_NCLBUTTONDOWN), WN(WM_NCLBUTTONUP), WN(WM_NCLBUTTONDBLCLK), WN(WM_NCRBUTTONDOWN), WN(WM_NCRBUTTONUP), WN(WM_NCRBUTTONDBLCLK), WN(WM_NCMBUTTONDOWN), WN(WM_NCMBUTTONUP), WN(WM_NCMBUTTONDBLCLK), WN(WM_KEYFIRST), WN(WM_KEYDOWN), WN(WM_KEYUP), WN(WM_CHAR), WN(WM_DEADCHAR), WN(WM_SYSKEYDOWN), WN(WM_SYSKEYUP), WN(WM_SYSCHAR), WN(WM_SYSDEADCHAR), WN(WM_KEYLAST), WN(WM_IME_STARTCOMPOSITION), WN(WM_IME_ENDCOMPOSITION), WN(WM_IME_COMPOSITION), WN(WM_IME_KEYLAST), WN(WM_INITDIALOG), WN(WM_COMMAND), WN(WM_SYSCOMMAND), WN(WM_TIMER), WN(WM_HSCROLL), WN(WM_VSCROLL), WN(WM_INITMENU), WN(WM_INITMENUPOPUP), WN(WM_MENUSELECT), WN(WM_MENUCHAR), WN(WM_ENTERIDLE), WN(WM_CTLCOLORMSGBOX), WN(WM_CTLCOLOREDIT), WN(WM_CTLCOLORLISTBOX), WN(WM_CTLCOLORBTN), WN(WM_CTLCOLORDLG), WN(WM_CTLCOLORSCROLLBAR), WN(WM_CTLCOLORSTATIC), WN(WM_MOUSEFIRST), WN(WM_MOUSEMOVE), WN(WM_LBUTTONDOWN), WN(WM_LBUTTONUP), WN(WM_LBUTTONDBLCLK), WN(WM_RBUTTONDOWN), WN(WM_RBUTTONUP), WN(WM_RBUTTONDBLCLK), WN(WM_MBUTTONDOWN), WN(WM_MBUTTONUP), WN(WM_MBUTTONDBLCLK), WN(WM_MOUSEWHEEL), WN(WM_PARENTNOTIFY), WN(WM_ENTERMENULOOP), WN(WM_EXITMENULOOP), WN(WM_NEXTMENU), WN(WM_SIZING), WN(WM_CAPTURECHANGED), WN(WM_MOVING), WN(WM_POWERBROADCAST), WN(WM_DEVICECHANGE), WN(WM_MDICREATE), WN(WM_MDIDESTROY), WN(WM_MDIACTIVATE), WN(WM_MDIRESTORE), WN(WM_MDINEXT), WN(WM_MDIMAXIMIZE), WN(WM_MDITILE), WN(WM_MDICASCADE), WN(WM_MDIICONARRANGE), WN(WM_MDIGETACTIVE), WN(WM_MDISETMENU), WN(WM_ENTERSIZEMOVE), WN(WM_EXITSIZEMOVE), WN(WM_DROPFILES), WN(WM_MDIREFRESHMENU), WN(WM_IME_SETCONTEXT), WN(WM_IME_NOTIFY), WN(WM_IME_CONTROL), WN(WM_IME_COMPOSITIONFULL), WN(WM_IME_SELECT), WN(WM_IME_CHAR), WN(WM_IME_KEYDOWN), WN(WM_IME_KEYUP), WN(WM_MOUSEHOVER), WN(WM_NCMOUSELEAVE), WN(WM_MOUSELEAVE), WN(WM_CUT), WN(WM_COPY), WN(WM_PASTE), WN(WM_CLEAR), WN(WM_UNDO), WN(WM_RENDERFORMAT), WN(WM_RENDERALLFORMATS), WN(WM_DESTROYCLIPBOARD), WN(WM_DRAWCLIPBOARD), WN(WM_PAINTCLIPBOARD), WN(WM_VSCROLLCLIPBOARD), WN(WM_SIZECLIPBOARD), WN(WM_ASKCBFORMATNAME), WN(WM_CHANGECBCHAIN), WN(WM_HSCROLLCLIPBOARD), WN(WM_QUERYNEWPALETTE), WN(WM_PALETTEISCHANGING), WN(WM_PALETTECHANGED), WN(WM_HOTKEY), WN(WM_PRINT), WN(WM_PRINTCLIENT), WN(WM_HANDHELDFIRST), WN(WM_HANDHELDLAST), WN(WM_PENWINFIRST), WN(WM_PENWINLAST), WN(WM_DDE_FIRST), WN(WM_DDE_INITIATE), WN(WM_DDE_TERMINATE), WN(WM_DDE_ADVISE), WN(WM_DDE_UNADVISE), WN(WM_DDE_ACK), WN(WM_DDE_DATA), WN(WM_DDE_REQUEST), WN(WM_DDE_POKE), WN(WM_DDE_EXECUTE), WN(WM_DDE_LAST), WN(WM_USER), WN(WM_APP), }; #undef WN static char unknown_s[64]; unsigned int i; for (i = 0; i < sizeof(msgs) / sizeof(*msgs); ++i) if (msgs[i].msg == msg) return msgs[i].s; sprintf(unknown_s, "UNKNOWN(%#x)", msg); return unknown_s; } static LRESULT CALLBACK TestWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { printf("%s: hwnd %p, msg %s, wParam %p, lParam %p.\n", __func__, hwnd, dbgstr_wm(msg), (void *)wParam, (void *)lParam); fflush(stdout); switch (msg) { case WM_PAINT: { PAINTSTRUCT ps; HBRUSH b; HDC hdc; RECT r; GetClientRect(hwnd, &r); ps.fErase = 0; hdc = BeginPaint(hwnd, &ps); printf("WM_PAINT rect (%ld,%ld)-(%ld,%ld).\n", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); b = CreateSolidBrush(RGB(0xff, 0, 0)); FillRect(hdc, &r, b); DeleteObject(b); EndPaint(hwnd, &ps); return 0; } } return DefWindowProcA(hwnd, msg, wParam, lParam); } static HBRUSH parent_bkgnd_brush; static LRESULT CALLBACK TestParentWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { RECT r; printf("%s: hwnd %p, msg %s, wParam %p, lParam %p.\n", __func__, hwnd, dbgstr_wm(msg), (void *)wParam, (void *)lParam); fflush(stdout); switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; GetClientRect(hwnd, &r); hdc = BeginPaint(hwnd, &ps); printf("WM_PAINT rect (%ld,%ld)-(%ld,%ld).\n", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); FillRect(hdc, &r, parent_bkgnd_brush); EndPaint(hwnd, &ps); return 0; } } return DefWindowProcA(hwnd, msg, wParam, lParam); } static void process_queue(BOOL wait) { MSG msg; while (wait ? GetMessage(&msg, NULL, 0, 0) : PeekMessage(&msg, NULL, 0, 0, TRUE)) DispatchMessage(&msg); } static void test_1(void) { HWND hwnd, parent; WNDCLASSA clsA; memset(&clsA, 0, sizeof(clsA)); clsA.style = 0; clsA.lpfnWndProc = TestWindowProc; clsA.cbClsExtra = 0; clsA.cbWndExtra = 0; clsA.hInstance = GetModuleHandleA(NULL); clsA.hIcon = 0; clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); clsA.hbrBackground = GetStockObject(WHITE_BRUSH); clsA.lpszMenuName = NULL; clsA.lpszClassName = "Test class"; RegisterClassA(&clsA); memset(&clsA, 0, sizeof(clsA)); clsA.style = 0; clsA.lpfnWndProc = TestParentWindowProc; clsA.cbClsExtra = 0; clsA.cbWndExtra = 0; clsA.hInstance = GetModuleHandleA(NULL); clsA.hIcon = 0; clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); clsA.hbrBackground = GetStockObject(BLACK_BRUSH); clsA.lpszMenuName = NULL; clsA.lpszClassName = "Test parent class"; RegisterClassA(&clsA); parent_bkgnd_brush = CreateSolidBrush(RGB(0, 0xff, 0)); parent = CreateWindowExA(0, "Test parent class", "Test owner window", WS_OVERLAPPED, 0, 0, 640, 480, NULL, NULL, GetModuleHandleA(NULL), NULL); hwnd = CreateWindowExA(0, "Test class", "Test window", WS_POPUP, 50, 50, 100, 100, parent, NULL, GetModuleHandleA(NULL), NULL); ShowWindow(parent, SW_SHOW); ShowWindow(hwnd, SW_SHOW); process_queue(FALSE); Sleep(1000); process_queue(FALSE); DeleteObject(parent_bkgnd_brush); parent_bkgnd_brush = CreateSolidBrush(RGB(0, 0, 0xff)); printf("Moving window.\n"); MoveWindow(hwnd, 150, 150, 120, 120, TRUE); process_queue(FALSE); Sleep(1000); printf("Hiding window.\n"); ShowWindow(hwnd, SW_HIDE); process_queue(FALSE); Sleep(1000); printf("Destroying window.\n"); DestroyWindow(hwnd); process_queue(FALSE); Sleep(1000); } int main(int argc, char *argv[]) { test_1(); return 0; }