[PATCH v5 0/4] MR10104: user32/edit: set bCaptureState flag after SetCapture()
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). -- v5: comctl32_v6/edit: set bCaptureState flag after SetCapture() user32/edit: set bCaptureState flag after SetCapture() 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 | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dlls/user32/tests/edit.c b/dlls/user32/tests/edit.c index c71b46495aa..ba9095071fe 100644 --- a/dlls/user32/tests/edit.c +++ b/dlls/user32/tests/edit.c @@ -3518,6 +3518,83 @@ static void test_PASSWORDCHAR(void) DestroyWindow (hwEdit); } +static BOOL capture_changed = FALSE; + +static 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); +} + +static 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 SetCapture() processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SetCapture(hwEdit); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + ok(ReleaseCapture(), "Can not release capture\n"); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after ReleaseCapture\n"); + ok(GetCapture() != hwEdit,"Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + /* Test double SetCapture() processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SetCapture(hwEdit); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 SetCapture()\n"); + SetCapture(hwEdit); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after 2/2 SetCapture()\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + + ok(ReleaseCapture(), "Can not release capture\n"); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after ReleaseCapture\n"); + ok(GetCapture() != hwEdit,"Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + /* test single WM_LBUTTONDOWN processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after WM_LBUTTONDOWN\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + + SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + 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"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 WM_LBUTTONDOWN\n"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + 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()); + + SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + ok(GetCapture() != hwEdit, "Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + DestroyWindow(hwEdit); +} + START_TEST(edit) { BOOL b; @@ -3558,6 +3635,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 | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dlls/comctl32/tests/edit.c b/dlls/comctl32/tests/edit.c index 45c2a243b70..652f9dd2b04 100644 --- a/dlls/comctl32/tests/edit.c +++ b/dlls/comctl32/tests/edit.c @@ -3937,6 +3937,83 @@ static void test_PASSWORDCHAR(void) DestroyWindow (hwEdit); } +static BOOL capture_changed = FALSE; + +static 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); +} + +static 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 SetCapture() processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SetCapture(hwEdit); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + ok(ReleaseCapture(), "Can not release capture\n"); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after ReleaseCapture\n"); + ok(GetCapture() != hwEdit,"Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + /* Test double SetCapture() processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SetCapture(hwEdit); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 SetCapture()\n"); + SetCapture(hwEdit); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after 2/2 SetCapture()\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + + ok(ReleaseCapture(), "Can not release capture\n"); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after ReleaseCapture\n"); + ok(GetCapture() != hwEdit,"Capture is on Edit Control %p, expected to be released\n", GetCapture()); + + /* test single WM_LBUTTONDOWN processing */ + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved\n"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after WM_LBUTTONDOWN\n"); + ok(GetCapture() == hwEdit,"Capture is not on Edit Control, instead on %p\n", GetCapture()); + + SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + 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"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + ok(!CheckCapture(), "Unexpected WM_CAPTURECHANGED recieved after 1/2 WM_LBUTTONDOWN\n"); + SendMessageA(hwEdit, WM_LBUTTONDOWN, 1, 0); + 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()); + + SendMessageA(hwEdit, WM_LBUTTONUP, 0, 0); + ok(CheckCapture(), "Expected WM_CAPTURECHANGED was not recieved after WM_LBUTTONUP\n"); + 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 +4065,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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c index cad14800995..4a551f0bbf8 100644 --- a/dlls/user32/edit.c +++ b/dlls/user32/edit.c @@ -3608,8 +3608,8 @@ static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es) INT li; INT ll; - es->bCaptureState = TRUE; NtUserSetCapture(es->hwndSelf); + es->bCaptureState = TRUE; l = EDIT_EM_LineFromChar(es, e); li = EDIT_EM_LineIndex(es, l); @@ -3632,8 +3632,8 @@ static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) INT e; BOOL after_wrap; - es->bCaptureState = TRUE; NtUserSetCapture(es->hwndSelf); + es->bCaptureState = TRUE; EDIT_ConfinePoint(es, &x, &y); e = EDIT_CharFromPos(es, x, y, &after_wrap); EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap); -- 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/comctl32_v6/edit.c b/dlls/comctl32_v6/edit.c index a79e2c0c169..53216dc9afb 100644 --- a/dlls/comctl32_v6/edit.c +++ b/dlls/comctl32_v6/edit.c @@ -3456,8 +3456,8 @@ static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es) INT li; INT ll; - es->bCaptureState = TRUE; SetCapture(es->hwndSelf); + es->bCaptureState = TRUE; l = EDIT_EM_LineFromChar(es, e); li = EDIT_EM_LineIndex(es, l); @@ -3480,8 +3480,8 @@ static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) INT e; BOOL after_wrap; - es->bCaptureState = TRUE; SetCapture(es->hwndSelf); + es->bCaptureState = TRUE; EDIT_ConfinePoint(es, &x, &y); e = EDIT_CharFromPos(es, x, y, &after_wrap); EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10104
Did some minor test changes -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10104#note_136564
participants (2)
-
Ivan Ivlev -
Ivan Ivlev (@iviv)