[PATCH v2 0/4] MR10104: user32/edit: Reset capture on WM_LBUTTONDOWN if already enabled
Edit control does not release mouse capture after WM_LBUTTONDOWN has been processed two times in a row. This MR fixes this issue by releasing capture and setting it again on WM_LBUTTONDOWN if window was already captured. I have stumbled upon the app in which edit control process WM_LBUTTONDOWN twice on one mouse click. While in windows it causes no problem, wine edit does not release capture, so other buttons inside this app become unclickable. It happens because when WM_LBUTTONDOWN is processed twice, WM_CAPTURECHANGED is sent and bCaptureState is set to FALSE, then EDIT_WM_LButtonUp does not release capture because bCaptureState is already FALSE. Thing about tests: I have added test case for this problem to be visible. I also had to use todo_wine because SendMessageA for WM_LBUTTONDOWN and WM_LBUTTONUP returns 0 in wine and 1 in windows (which is not the case for combobox test, where SendMessageА for WM_LBUTTONDOWN also returns 1 in wine). -- v2: comctl32_v6/edit: Reset capture on WM_LBUTTONDOWN if already enabled user32/edit: Reset capture on WM_LBUTTONDOWN if already enabled comctl32/tests/edit.c: Test WM_LBUTTONDOWN processing user32/tests/edit.c: Test WM_LBUTTONDOWN processing https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
From: Ivan Ivlev <iviv@etersoft.ru> Signed-off-by: Ivan Ivlev <iviv@etersoft.ru> --- dlls/user32/tests/edit.c | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/dlls/user32/tests/edit.c b/dlls/user32/tests/edit.c index c71b46495aa..14bd34efd53 100644 --- a/dlls/user32/tests/edit.c +++ b/dlls/user32/tests/edit.c @@ -3518,6 +3518,74 @@ static void test_PASSWORDCHAR(void) DestroyWindow (hwEdit); } +BOOL capture_changed = FALSE; + +LRESULT CALLBACK CaptureEditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + WNDPROC origEditProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + if (msg == WM_CAPTURECHANGED) + capture_changed = TRUE; + return CallWindowProcA(origEditProc, hwnd, msg, wp, lp); +} + +int CheckCapture(void) +{ + if(capture_changed){ + capture_changed = FALSE; + return 1; + } + return 0; +} + +static void test_WM_LBUTTONDOWN(void) +{ + HWND hwEdit; + WNDPROC origEditProc; + + hwEdit = CreateWindowExA(0, "EDIT", "Test", ES_LEFT, + 0, 0, 100, 100, NULL, NULL, NULL, NULL); + origEditProc = (WNDPROC)SetWindowLongPtrA(hwEdit, GWLP_WNDPROC, (LONG_PTR)CaptureEditProc); + SetWindowLongPtrA(hwEdit, GWLP_USERDATA, (LONG_PTR)origEditProc); + // test single WM_LBUTTONDOWN processing + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after WM_LBUTTONDOWN\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() == hwEdit, + "Capture not on Edit Control, instead on %p\n", GetCapture()); + + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0), + "WM_LBUTTONUP was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() != hwEdit, + "Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + // test double WM_LBUTTONDOWN processing + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "1/2 WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 WM_LBUTTONDOWN\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "2/2 WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after 2/2 WM_LBUTTONDOWN\n"); + ok(GetCapture() == hwEdit, + "Capture is not on Edit Control, instead on %p\n", GetCapture()); + + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0), + "WM_LBUTTONUP was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() != hwEdit, + "Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + DestroyWindow(hwEdit); +} + START_TEST(edit) { BOOL b; @@ -3558,6 +3626,7 @@ START_TEST(edit) test_dbcs_WM_CHAR(); test_format_rect(); test_PASSWORDCHAR(); + test_WM_LBUTTONDOWN(); UnregisterWindowClasses(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
From: Ivan Ivlev <iviv@etersoft.ru> Signed-off-by: Ivan Ivlev <iviv@etersoft.ru> --- dlls/comctl32/tests/edit.c | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/dlls/comctl32/tests/edit.c b/dlls/comctl32/tests/edit.c index 45c2a243b70..f9dfd3b98e4 100644 --- a/dlls/comctl32/tests/edit.c +++ b/dlls/comctl32/tests/edit.c @@ -3937,6 +3937,74 @@ static void test_PASSWORDCHAR(void) DestroyWindow (hwEdit); } +BOOL capture_changed = FALSE; + +LRESULT CALLBACK CaptureEditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + WNDPROC origEditProc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + if (msg == WM_CAPTURECHANGED) + capture_changed = TRUE; + return CallWindowProcA(origEditProc, hwnd, msg, wp, lp); +} + +int CheckCapture(void) +{ + if(capture_changed){ + capture_changed = FALSE; + return 1; + } + return 0; +} + +static void test_WM_LBUTTONDOWN(void) +{ + HWND hwEdit; + WNDPROC origEditProc; + + hwEdit = CreateWindowExA(0, "EDIT", "Test", ES_LEFT, + 0, 0, 100, 100, NULL, NULL, NULL, NULL); + origEditProc = (WNDPROC)SetWindowLongPtrA(hwEdit, GWLP_WNDPROC, (LONG_PTR)CaptureEditProc); + SetWindowLongPtrA(hwEdit, GWLP_USERDATA, (LONG_PTR)origEditProc); + // test single WM_LBUTTONDOWN processing + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after WM_LBUTTONDOWN\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() == hwEdit, + "Capture not on Edit Control, instead on %p\n", GetCapture()); + + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0), + "WM_LBUTTONUP was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() != hwEdit, + "Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + // test double WM_LBUTTONDOWN processing + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "1/2 WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 WM_LBUTTONDOWN\n"); + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0), + "2/2 WM_LBUTTONDOWN was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after 2/2 WM_LBUTTONDOWN\n"); + ok(GetCapture() == hwEdit, + "Capture is not on Edit Control, instead on %p\n", GetCapture()); + + todo_wine ok(SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0), + "WM_LBUTTONUP was not processed. LastError=%ld\n", GetLastError()); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + ok(GetFocus() == hwEdit, + "Focus is not on Edit Control, instead on %p\n", GetFocus()); + ok(GetCapture() != hwEdit, + "Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + DestroyWindow(hwEdit); +} + START_TEST(edit) { ULONG_PTR ctx_cookie; @@ -3988,6 +4056,7 @@ START_TEST(edit) test_ime(); test_format_rect(); test_PASSWORDCHAR(); + test_WM_LBUTTONDOWN(); UnregisterWindowClasses(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
From: Ivan Ivlev <iviv@etersoft.ru> Signed-off-by: Ivan Ivlev <iviv@etersoft.ru> --- dlls/user32/edit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index cad14800995..aa93d12391f 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -3632,6 +3632,8 @@ static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) INT e; BOOL after_wrap; + if (es->bCaptureState && (GetCapture() == es->hwndSelf)) NtUserReleaseCapture(); + es->bCaptureState = TRUE; NtUserSetCapture(es->hwndSelf); EDIT_ConfinePoint(es, &x, &y); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
From: Ivan Ivlev <iviv@etersoft.ru> Signed-off-by: Ivan Ivlev <iviv@etersoft.ru> --- dlls/comctl32_v6/edit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/comctl32_v6/edit.c b/dlls/comctl32_v6/edit.c index a79e2c0c169..9440deb8c6c 100644 --- a/dlls/comctl32_v6/edit.c +++ b/dlls/comctl32_v6/edit.c @@ -3480,6 +3480,8 @@ static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) INT e; BOOL after_wrap; + if (es->bCaptureState && (GetCapture() == es->hwndSelf)) ReleaseCapture(); + es->bCaptureState = TRUE; SetCapture(es->hwndSelf); EDIT_ConfinePoint(es, &x, &y); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
participants (2)
-
Ivan Ivlev -
Ivan Ivlev (@iviv)