[PATCH] user32: Modal dialogs send WM_CANCELMODE when mouse is captured by unrelated window in same thread.

Sergio sdelreal at codeweavers.com
Wed Jul 17 13:12:48 CDT 2019


Signed-off-by: Sergio <sdelreal at 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)
-- 
2.17.1




More information about the wine-devel mailing list