Signed-off-by: Sergio sdelreal@codeweavers.com --- dlls/user32/dialog.c | 3 + dlls/user32/tests/msg.c | 221 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 222 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/dialog.c b/dlls/user32/dialog.c index 88c2930c06..36a442d32b 100644 --- a/dlls/user32/dialog.c +++ b/dlls/user32/dialog.c @@ -701,7 +701,10 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
if (template.style & WS_VISIBLE && !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)) { + HWND hwnd_capture; ShowWindow( hwnd, SW_SHOWNORMAL ); /* SW_SHOW doesn't always work */ + if ( (hwnd_capture = GetCapture()) && !IsChild( hwnd, hwnd_capture ) && modal_owner ) + SendMessageW(hwnd_capture, WM_CANCELMODE, 0, 0); } return hwnd; } diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index a5bc1c5518..2508bb85d2 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -1675,6 +1675,85 @@ static const struct message WmModalDialogSeq_2[] = { { WM_NCDESTROY, sent }, { 0 } }; +/* remove capture when creating modal dialog */ +static const struct message WmDialogWithCaptureSeq[] = { + { WM_CANCELMODE, sent }, + { WM_CAPTURECHANGED, sent|defwinproc|lparam, 0 }, + { WM_KILLFOCUS, sent }, + { WM_ENABLE, sent|wparam, 0 }, + { HCBT_CREATEWND, hook }, + { WM_SETFONT, sent|optional }, + { WM_INITDIALOG, sent }, + { WM_CHANGEUISTATE, sent|optional }, + { WM_UPDATEUISTATE, sent|optional }, + { WM_ENABLE, sent|wparam, 1 }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { HCBT_ACTIVATE, hook }, + { WM_ACTIVATE, sent }, + { WM_ACTIVATE, sent }, + { HCBT_DESTROYWND, hook }, + { WM_ACTIVATE, sent }, + { WM_ACTIVATE, sent }, + { WM_SETFOCUS, sent|defwinproc }, + { WM_DESTROY, sent }, + { 0 } +}; +static const struct message WmDialogWithCapture2Seq[] = { + { HCBT_CREATEWND, hook|optional }, + { WM_SETFONT, sent }, + { WM_INITDIALOG, sent }, + { WM_CHANGEUISTATE, sent|optional }, + { WM_UPDATEUISTATE, sent|optional }, + { WM_SHOWWINDOW, sent, 1 }, + { HCBT_ACTIVATE, hook }, + { WM_ACTIVATE, sent }, + { WM_ACTIVATE, sent|optional }, + { WM_KILLFOCUS, sent|optional }, + { WM_CANCELMODE, sent }, + { WM_CAPTURECHANGED, sent|defwinproc, 0 }, + { HCBT_DESTROYWND, hook|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_SETFOCUS, sent|defwinproc|optional }, + { WM_DESTROY, sent }, + { 0 } +}; +static const struct message WmDialogNoCaptureSeq[] = { + { WM_CANCELMODE, sent }, + { WM_KILLFOCUS, sent|optional }, + { WM_ENABLE, sent }, + { HCBT_CREATEWND, hook|optional }, + { WM_SETFONT, sent }, + { WM_INITDIALOG, sent }, + { WM_CHANGEUISTATE, sent|optional }, + { WM_UPDATEUISTATE, sent|optional }, + { WM_ENABLE, sent }, + { WM_SHOWWINDOW, sent }, + { HCBT_ACTIVATE, hook|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_ACTIVATE, sent|optional }, + { HCBT_DESTROYWND, hook|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_SETFOCUS, sent|defwinproc|optional }, + { WM_DESTROY, sent }, + { 0 } +}; +/* modeless dialogs don't remove capture */ +static const struct message WmDialogModelessWithCaptureSeq[] = { + { HCBT_CREATEWND, hook|optional }, + { WM_SETFONT, sent }, + { WM_INITDIALOG, sent }, + { WM_CHANGEUISTATE, sent|optional }, + { WM_UPDATEUISTATE, sent|optional }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { HCBT_ACTIVATE, hook|optional }, + { WM_ACTIVATE, sent }, + { WM_ACTIVATE, sent }, + { WM_KILLFOCUS, sent }, + { 0 } +}; + /* SetMenu for NonVisible windows with size change*/ static const struct message WmSetMenuNonVisibleSizeChangeSeq[] = { { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, @@ -4447,6 +4526,47 @@ static INT_PTR CALLBACK TestModalDlgProc2(HWND hwnd, UINT message, WPARAM wParam return 0; }
+static INT_PTR CALLBACK TestModalDlgWithCaptureProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct recvd_message msg; + + if (ignore_message( message )) return 0; + + switch (message) + { + case WM_NCHITTEST: + return HTCLIENT; + case WM_QUERYNEWPALETTE: + case WM_GETTEXT: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + case WM_NCPAINT: + case WM_NCACTIVATE: + case WM_NCCALCSIZE: + case WM_NCDESTROY: + case WM_PAINT: + case WM_ERASEBKGND: + case WM_ACTIVATEAPP: + case WM_CTLCOLORDLG: + case WM_CTLCOLORBTN: + case WM_SETFOCUS: + case WM_KILLFOCUS: + case 0x90: + break; + case WM_SHOWWINDOW: + EndDialog(hwnd, 5); + default: + msg.hwnd = hwnd; + msg.message = message; + msg.flags = sent|wparam|lparam; + msg.wParam = wParam; + msg.lParam = lParam; + msg.descr = "dialog"; + add_message(&msg); + } + return FALSE; +} + static void test_hv_scroll_1(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max) { DWORD style, exstyle; @@ -9771,6 +9891,45 @@ static LRESULT WINAPI HotkeyMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam return ret; }
+static LRESULT WINAPI WindowCaptureProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static LONG defwndproc_counter = 0; + LRESULT ret; + struct recvd_message msg; + + if (ignore_message(message)) return 0; + + switch (message) + { + case WM_NCHITTEST: + return HTCLIENT; + case WM_QUERYNEWPALETTE: + case WM_GETTEXT: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + case WM_NCPAINT: + case WM_NCACTIVATE: + case WM_ERASEBKGND: + case WM_ACTIVATEAPP: + break; + default: + msg.hwnd = hwnd; + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + msg.descr = "capture_window"; + add_message(&msg); + } + + defwndproc_counter++; + ret = DefWindowProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + static BOOL RegisterWindowClasses(void) { WNDCLASSA cls; @@ -9820,6 +9979,10 @@ static BOOL RegisterWindowClasses(void) cls.lpszClassName = "NoCloseWindowClass"; if(!RegisterClassA(&cls)) return FALSE;
+ cls.lpfnWndProc = WindowCaptureProcA; + cls.lpszClassName = "WindowCaptureClass"; + if(!RegisterClassA(&cls)) return FALSE; + ok(GetClassInfoA(0, "#32770", &cls), "GetClassInfo failed\n"); cls.style = 0; cls.hInstance = GetModuleHandleA(0); @@ -13664,6 +13827,7 @@ static void test_dialog_messages(void) WNDCLASSA cls; HWND hdlg, hedit1, hedit2, hfocus, parent, child, child2; LRESULT ret; + int dialog_ret;
#define set_selection(hctl, start, end) \ ret = SendMessageA(hctl, EM_SETSEL, start, end); \ @@ -13820,11 +13984,64 @@ static void test_dialog_messages(void) flush_sequence(); DialogBoxA( 0, "TEST_DIALOG", child2, TestModalDlgProc2 ); ok_sequence(WmModalDialogSeq_2, "ModalDialog2", TRUE); - DestroyWindow(child2); DestroyWindow(child); DestroyWindow(parent); - flush_sequence(); + + /* test that if a window has the mouse captured, modal dialogs should send it WM_CANCELMODE */ + parent = CreateWindowExA(0, "WindowCaptureClass", "Test parent", WS_VISIBLE, + 100, 100, 200, 200, NULL, 0, 0, NULL); + ok(parent != NULL, "Failed creating window.\n"); + child = CreateWindowExA(0, "WindowCaptureClass", "Test child", WS_VISIBLE | WS_CHILD, + 100, 100, 200, 200, parent, 0, 0, NULL); + ok(child != NULL, "Failed creating window.\n"); + + /* test modal dialog being child of window with capture */ + SetCapture(parent); + ok(GetCapture() == parent, "Failed capturing mouse.\n"); + flush_sequence(); + dialog_ret = DialogBoxParamA(0, "TEST_DIALOG", parent, TestModalDlgWithCaptureProc, 0); + ok(dialog_ret > 0, "DialogBoxParamA failed.\n"); + ok_sequence(WmDialogWithCaptureSeq, "ModalDialogWithCapture", FALSE); + ok(GetCapture() == NULL, "Mouse shouldn't be captured by hwnd %p\n", GetCapture()); + + /* test modal dialog unrelated to window with capture */ + SetCapture(parent); + ok(GetCapture() == parent, "Failed capturing mouse.\n"); + flush_sequence(); + dialog_ret = DialogBoxParamA(0, "TEST_DIALOG", NULL, TestModalDlgWithCaptureProc, 0); + ok(dialog_ret > 0, "DialogBoxParamA failed.\n"); + ok_sequence(WmDialogWithCapture2Seq, "ModalDialogWithCapture2", FALSE); + ok(GetCapture() == NULL, "Mouse shouldn't be captured by hwnd %p\n", GetCapture()); + + /* test modal dialog unrelated to window with capture, child */ + SetCapture(child); + ok(GetCapture() == child, "Failed capturing mouse.\n"); + flush_sequence(); + dialog_ret = DialogBoxParamA(0, "TEST_DIALOG", NULL, TestModalDlgWithCaptureProc, 0); + ok(dialog_ret > 0, "DialogBoxParamA failed.\n"); + ok_sequence(WmDialogWithCapture2Seq, "ModalDialogWithCapture3", FALSE); + ok(GetCapture() == NULL, "Mouse shouldn't be captured by hwnd %p\n", GetCapture()); + + /* test modal dialog, no capture */ + flush_sequence(); + dialog_ret = DialogBoxParamA(0, "TEST_DIALOG", parent, TestModalDlgWithCaptureProc, 0); + ok(dialog_ret > 0, "DialogBoxParamA failed.\n"); + ok_sequence(WmDialogNoCaptureSeq, "ModalDialogNoCapture", FALSE); + ok(GetCapture() == NULL, "Mouse shouldn't be captured by hwnd %p\n", GetCapture()); + + /* test that modeless dialogs don't send message */ + SetCapture(parent); + ok(GetCapture() == parent, "Failed capturing mouse.\n"); + flush_sequence(); + hdlg = CreateDialogParamA(0, "TEST_DIALOG", 0, TestModalDlgWithCaptureProc, 0); + ok(hdlg != NULL, "CreateDialogParamA failed.\n"); + ok_sequence(WmDialogModelessWithCaptureSeq, "ModelessDialogWithCapture", FALSE); + ok(GetCapture() == parent, "Mouse should be captured by hwnd %p\n", parent); + + DestroyWindow(child); + DestroyWindow(parent); + DestroyWindow(hdlg); }
static void test_enddialog_seq(HWND dialog, HWND owner)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=54821
Your paranoid android.
=== w7u (32 bit report) ===
user32: msg.c:13945: Test failed: CreateDialogParam_3: 12: the msg 0x001c was expected, but got msg 0x0047 instead
=== w1064v1809 (32 bit report) ===
user32: msg.c:10440: Test failed: did not get expected count for minimum timeout (54 != ~100).
=== debian9 (32 bit WoW report) ===
user32: msg.c:8833: Test failed: WaitForSingleObject failed 102 msg.c:8839: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8839: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8839: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
=== debian9 (64 bit WoW report) ===
user32: clipboard.c:1630: Test failed: gle 5 clipboard.c:1632: Test failed: expected moveable mem 00000000 clipboard.c:1637: Test failed: expected moveable mem 00000000 clipboard.c:1642: Test failed: expected moveable mem 00000000 clipboard.c:1648: Test failed: expected moveable mem 00000000 clipboard.c:1654: Test failed: expected bitmap 00000000 clipboard.c:1655: Test failed: different bitmap 00000000 / 005E003F clipboard.c:1658: Test failed: expected bitmap 00000000 clipboard.c:1659: Test failed: different bitmap 00000000 / 000B0045 clipboard.c:1662: Test failed: expected palette 00000000 clipboard.c:1663: Test failed: different palette 00000000 / 000C0041 clipboard.c:1666: Test failed: expected fixed mem 00000000 clipboard.c:1668: Test failed: expected fixed mem 00000000 clipboard.c:1670: Test failed: gle 1418 win.c:10150: Test failed: Expected foreground window 000E0120, got 00E300D4 win.c:10152: Test failed: GetActiveWindow() = 00000000 win.c:10152: Test failed: GetFocus() = 00000000 win.c:10153: Test failed: Received WM_ACTIVATEAPP(1), did not expect it. win.c:10154: Test failed: Received WM_ACTIVATEAPP(0), did not expect it. win.c:10162: Test failed: Expected foreground window 000E0120, got 00000000 win.c:10164: Test failed: GetActiveWindow() = 00000000 win.c:10164: Test failed: GetFocus() = 00000000 win.c:10172: Test failed: Received WM_ACTIVATEAPP(1), did not expect it.
Sergio sdelreal@codeweavers.com wrote:
@@ -701,7 +701,10 @@ static HWND DIALOG_CreateIndirect( HINSTANCE hInst, LPCVOID dlgTemplate,
if (template.style & WS_VISIBLE && !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)) {
HWND hwnd_capture; ShowWindow( hwnd, SW_SHOWNORMAL ); /* SW_SHOW doesn't always work */
if ( (hwnd_capture = GetCapture()) && !IsChild( hwnd, hwnd_capture ) && modal_owner )
SendMessageW(hwnd_capture, WM_CANCELMODE, 0, 0); }
This is most likely wrong, WM_CANCELMODE should be sent when the capturing window loses focus during ShowWindow( hwnd, SW_SHOWNORMAL ) call.
+static INT_PTR CALLBACK TestModalDlgWithCaptureProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{
- struct recvd_message msg;
- if (ignore_message( message )) return 0;
- switch (message)
- {
case WM_NCHITTEST:
return HTCLIENT;
case WM_QUERYNEWPALETTE:
case WM_GETTEXT:
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
case WM_NCPAINT:
case WM_NCACTIVATE:
case WM_NCCALCSIZE:
case WM_NCDESTROY:
case WM_PAINT:
case WM_ERASEBKGND:
case WM_ACTIVATEAPP:
case WM_CTLCOLORDLG:
case WM_CTLCOLORBTN:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case 0x90:
break;
The fact that you need to filter out so many messages is a clear sign that the test is broken. Same comment applies for the next message proc.