- Turn on User32 msg test_menu_messages. The Alt-E/F keys work now. This is what...
[reactos.git] / rostests / winetests / user32 / msg.c
index bfc81dc..e0494ef 100755 (executable)
@@ -20,7 +20,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#define _WIN32_WINNT 0x0501 /* For WM_CHANGEUISTATE,QS_RAWINPUT */
+#define _WIN32_WINNT 0x0600 /* For WM_CHANGEUISTATE,QS_RAWINPUT,WM_DWMxxxx */
+#define WINVER 0x0600 /* for WM_GETTITLEBARINFOEX */
 
 #include <assert.h>
 #include <stdarg.h>
@@ -161,7 +162,7 @@ static const struct message WmSWP_ShowOverlappedSeq[] = {
     { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
     { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* Win9x: SWP_NOSENDCHANGING */
     { WM_ACTIVATEAPP, sent|wparam, 1 },
-    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent },
     { WM_GETTEXT, sent|defwinproc|optional },
     { WM_ACTIVATE, sent|wparam, 1 },
     { HCBT_SETFOCUS, hook },
@@ -184,7 +185,7 @@ static const struct message WmSWP_ShowOverlappedSeq[] = {
     { WM_GETTITLEBARINFOEX, sent|optional },
     { WM_PAINT, sent|optional },
     { WM_NCPAINT, sent|beginpaint|optional },
-    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
     { WM_ERASEBKGND, sent|beginpaint|optional },
     { 0 }
 };
@@ -268,7 +269,7 @@ static const struct message WmSWP_ResizeNoZOrder[] = {
     { WM_NCPAINT, sent|optional },
     { WM_GETTEXT, sent|defwinproc|optional },
     { WM_ERASEBKGND, sent|optional },
-    { WM_WINDOWPOSCHANGED, sent|wparam, /*SWP_NOZORDER|*/SWP_NOACTIVATE, 0,
+    { WM_WINDOWPOSCHANGED, sent|wparam|optional, /*SWP_NOZORDER|*/SWP_NOACTIVATE, 0,
       SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_NOSIZE|SWP_NOCLIENTSIZE },
     { WM_MOVE, sent|defwinproc|optional },
     { WM_SIZE, sent|defwinproc|optional },
@@ -297,7 +298,7 @@ static const struct message WmSwitchChild[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_STATECHANGED }, /* in the 1st MDI child */
     { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 }, /* in the 1st MDI child */
     { WM_CHILDACTIVATE, sent|defwinproc }, /* in the 1st MDI child */
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED }, /* in the 1st MDI child */
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED }, /* in the 1st MDI child */
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED }, /* in the 1st MDI child */
     /* Lock redraw 2nd MDI child */
     { WM_SETREDRAW, sent|wparam|defwinproc, 0 }, /* in the 2nd MDI child */
@@ -306,7 +307,7 @@ static const struct message WmSwitchChild[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_STATECHANGED },/* in the 2nd MDI child */
     { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },/* in the 2nd MDI child */
     { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, /* in the 2nd MDI child */
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED }, /* in the 2nd MDI child */
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED }, /* in the 2nd MDI child */
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED }, /* in the 2nd MDI child */
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* in the 2nd MDI child */
     /* Redraw 2nd MDI child */
@@ -631,9 +632,10 @@ static const struct message WmHideOverlappedSeq[] = {
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
     { WM_SIZE, sent|optional }, /* XP doesn't send it */
     { WM_MOVE, sent|optional }, /* XP doesn't send it */
-    { WM_NCACTIVATE, sent|wparam, 0 },
-    { WM_ACTIVATE, sent|wparam, 0 },
-    { WM_ACTIVATEAPP, sent|wparam, 0 },
+    { WM_NCACTIVATE, sent|wparam|optional, 0 },
+    { WM_ACTIVATE, sent|wparam|optional, 0 },
+    { WM_ACTIVATEAPP, sent|wparam|optional, 0 },
+    { HCBT_SETFOCUS, hook|optional },
     { WM_KILLFOCUS, sent|wparam, 0 },
     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
     { WM_IME_NOTIFY, sent|wparam|optional|defwinproc, 1 },
@@ -1776,7 +1778,8 @@ static BOOL ignore_message( UINT message )
             message == WM_GETOBJECT ||
             message == WM_TIMECHANGE ||
             message == WM_DISPLAYCHANGE ||
-            message == WM_DEVICECHANGE);
+            message == WM_DEVICECHANGE ||
+            message == WM_DWMNCRENDERINGCHANGED);
 }
 
 
@@ -2473,7 +2476,7 @@ static const struct message WmCreateMDIchildVisibleMaxSeq1[] = {
     { WM_GETMINMAXINFO, sent },
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_STATECHANGED  },
     { WM_NCCALCSIZE, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -2526,7 +2529,7 @@ static const struct message WmCreateMDIchildVisibleMaxSeq2[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -2548,7 +2551,7 @@ static const struct message WmCreateMDIchildVisibleMaxSeq2[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -2613,7 +2616,7 @@ static const struct message WmCreateMDIchildVisibleMaxSeq3[] = {
     { WM_GETMINMAXINFO, sent },
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
 
      /* in MDI frame */
@@ -2719,7 +2722,7 @@ static const struct message WmCreateMDIchildInvisibleMaxSeq4[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_STATECHANGED },
     { WM_GETMINMAXINFO, sent|defwinproc },
     { WM_NCCALCSIZE, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOREDRAW|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
      /* in MDI frame */
@@ -2761,7 +2764,7 @@ static const struct message WmDestroyMDIchildVisibleMaxSeq2[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
     { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
 
     /* restore the 2nd MDI child */
@@ -2772,7 +2775,7 @@ static const struct message WmDestroyMDIchildVisibleMaxSeq2[] = {
 
     { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
 
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
 
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
@@ -3019,7 +3022,7 @@ static const struct message WmMaximizeMDIchildInvisibleSeq[] = {
     { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
     { WM_SETFOCUS, sent|optional|defwinproc },
     { WM_MDIACTIVATE, sent|optional|defwinproc },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -3134,7 +3137,7 @@ static const struct message WmMaximizeMDIchildVisibleSeq[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -3150,7 +3153,7 @@ static const struct message WmRestoreMDIchildVisibleSeq[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -3167,7 +3170,7 @@ static const struct message WmRestoreMDIchildVisibleSeq_2[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_NOCLIENTSIZE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
@@ -3184,7 +3187,7 @@ static const struct message WmMinimizeMDIchildVisibleSeq[] = {
     { HCBT_MINMAX, hook|lparam, 0, SW_MINIMIZE },
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_NCCALCSIZE, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOCLIENTSIZE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
     { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { WM_CHILDACTIVATE, sent|wparam|lparam|defwinproc, 0, 0 },
@@ -3201,7 +3204,7 @@ static const struct message WmRestoreMDIchildInisibleSeq[] = {
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
     { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOCLIENTMOVE|SWP_STATECHANGED },
     { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
      /* in MDI frame */
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -3357,6 +3360,7 @@ static void test_mdi_messages(void)
     CLIENTCREATESTRUCT client_cs;
     HWND mdi_frame, mdi_child, mdi_child2, active_child;
     BOOL zoomed;
+    RECT rc;
     HMENU hMenu = CreateMenu();
 
     assert(mdi_RegisterWindowClasses());
@@ -3377,12 +3381,13 @@ static void test_mdi_messages(void)
     ok(GetFocus() == mdi_frame, "wrong focus window %p\n", GetFocus());
 
     trace("creating MDI client window\n");
+    GetClientRect(mdi_frame, &rc);
     client_cs.hWindowMenu = 0;
     client_cs.idFirstChild = MDI_FIRST_CHILD_ID;
     mdi_client = CreateWindowExA(0, "MDI_client_class",
                                  NULL,
                                  WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES,
-                                 0, 0, 0, 0,
+                                 rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
                                  mdi_frame, 0, GetModuleHandleA(0), &client_cs);
     assert(mdi_client);
     ok_sequence(WmCreateMDIclientSeq, "Create visible MDI client window", FALSE);
@@ -3568,7 +3573,7 @@ static void test_mdi_messages(void)
     flush_sequence();
 
     ShowWindow(mdi_child2, SW_RESTORE);
-    ok_sequence(WmRestoreMDIchildVisibleSeq_2, "ShowWindow(SW_RESTORE):minimized MDI child", TRUE);
+    ok_sequence(WmRestoreMDIchildVisibleSeq_2, "ShowWindow(SW_RESTORE):minimized MDI child", FALSE);
 
     ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
     ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus());
@@ -4712,10 +4717,10 @@ static void test_messages(void)
     ok(hwnd != 0, "Failed to create custom dialog window\n");
     ok_sequence(WmCreateCustomDialogSeq, "CreateCustomDialog", TRUE);
 
-    /*
+    if(0) {
     trace("testing scroll APIs on a visible dialog %p\n", hwnd);
     test_scroll_messages(hwnd);
-    */
+    }
 
     flush_sequence();
 
@@ -5252,6 +5257,65 @@ static const struct message WmSetStyleOwnerdrawSeq[] =
     { WM_DRAWITEM, sent|wparam|lparam|parent, ID_BUTTON, 0x000010e4 },
     { 0 }
 };
+static const struct message WmSetStateButtonSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmSetStateStaticSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORSTATIC, sent|parent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmSetStateUserSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_HILITE) },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmSetStateOwnerdrawSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_DRAWITEM, sent|wparam|lparam|parent, ID_BUTTON, 0x000120e4 },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmClearStateButtonSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmClearStateOwnerdrawSeq[] =
+{
+    { BM_SETSTATE, sent },
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_DRAWITEM, sent|wparam|lparam|parent, ID_BUTTON, 0x000020e4 },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmSetCheckIgnoredSeq[] =
+{
+    { BM_SETCHECK, sent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message WmSetCheckStaticSeq[] =
+{
+    { BM_SETCHECK, sent },
+    { WM_CTLCOLORSTATIC, sent|parent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
 
 static WNDPROC old_button_proc;
 
@@ -5268,7 +5332,8 @@ static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam,
     case WM_SYNCPAINT:
         break;
     case BM_SETSTATE:
-       ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
+        if (GetCapture())
+            ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
         /* fall through */
     default:
         msg.hwnd = hwnd;
@@ -5312,29 +5377,43 @@ static void test_button_messages(void)
        const struct message *setfocus;
        const struct message *killfocus;
        const struct message *setstyle;
+       const struct message *setstate;
+       const struct message *clearstate;
+       const struct message *setcheck;
     } button[] = {
        { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq,
+          WmSetStateButtonSeq, WmSetStateButtonSeq, WmSetCheckIgnoredSeq },
        { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq,
+          WmSetStateButtonSeq, WmSetStateButtonSeq, WmSetCheckIgnoredSeq },
        { BS_CHECKBOX, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_AUTOCHECKBOX, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_3STATE, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_AUTO3STATE, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_GROUPBOX, DLGC_STATIC,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckIgnoredSeq },
        { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleUserSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleUserSeq,
+          WmSetStateUserSeq, WmClearStateButtonSeq, WmSetCheckIgnoredSeq },
        { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq,
+          WmSetStateStaticSeq, WmSetStateStaticSeq, WmSetCheckStaticSeq },
        { BS_OWNERDRAW, DLGC_BUTTON,
-         WmSetFocusOwnerdrawSeq, WmKillFocusOwnerdrawSeq, WmSetStyleOwnerdrawSeq }
+         WmSetFocusOwnerdrawSeq, WmKillFocusOwnerdrawSeq, WmSetStyleOwnerdrawSeq,
+          WmSetStateOwnerdrawSeq, WmClearStateOwnerdrawSeq, WmSetCheckIgnoredSeq },
     };
     unsigned int i;
     HWND hwnd, parent;
@@ -5361,7 +5440,7 @@ static void test_button_messages(void)
     for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
     {
         MSG msg;
-        DWORD style;
+        DWORD style, state;
 
         trace("button style %08x\n", button[i].style);
 
@@ -5384,6 +5463,7 @@ static void test_button_messages(void)
        UpdateWindow(hwnd);
        SetFocus(0);
        flush_events();
+       SetFocus(0);
        flush_sequence();
 
         log_all_parent_messages++;
@@ -5409,7 +5489,80 @@ static void test_button_messages(void)
         style = GetWindowLongA(hwnd, GWL_STYLE);
         style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
         /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
-        ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
+        ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
+
+        state = SendMessage(hwnd, BM_GETSTATE, 0, 0);
+        ok(state == 0, "expected state 0, got %04x\n", state);
+
+        flush_sequence();
+
+        SendMessage(hwnd, BM_SETSTATE, TRUE, 0);
+        SendMessage(hwnd, WM_APP, 0, 0); /* place a separator mark here */
+        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+        ok_sequence(button[i].setstate, "BM_SETSTATE/TRUE on a button", FALSE);
+
+        state = SendMessage(hwnd, BM_GETSTATE, 0, 0);
+        ok(state == 0x0004, "expected state 0x0004, got %04x\n", state);
+
+        style = GetWindowLongA(hwnd, GWL_STYLE);
+        style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
+        ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
+
+        flush_sequence();
+
+        SendMessage(hwnd, BM_SETSTATE, FALSE, 0);
+        SendMessage(hwnd, WM_APP, 0, 0); /* place a separator mark here */
+        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+        ok_sequence(button[i].clearstate, "BM_SETSTATE/FALSE on a button", FALSE);
+
+        state = SendMessage(hwnd, BM_GETSTATE, 0, 0);
+        ok(state == 0, "expected state 0, got %04x\n", state);
+
+        style = GetWindowLongA(hwnd, GWL_STYLE);
+        style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
+        ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
+
+        state = SendMessage(hwnd, BM_GETCHECK, 0, 0);
+        ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
+
+        flush_sequence();
+
+        SendMessage(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
+        SendMessage(hwnd, WM_APP, 0, 0); /* place a separator mark here */
+        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+        ok_sequence(WmSetCheckIgnoredSeq, "BM_SETCHECK on a button", FALSE);
+
+        state = SendMessage(hwnd, BM_GETCHECK, 0, 0);
+        ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
+
+        style = GetWindowLongA(hwnd, GWL_STYLE);
+        style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
+        ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
+
+        flush_sequence();
+
+        SendMessage(hwnd, BM_SETCHECK, BST_CHECKED, 0);
+        SendMessage(hwnd, WM_APP, 0, 0); /* place a separator mark here */
+        while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+        ok_sequence(button[i].setcheck, "BM_SETCHECK on a button", FALSE);
+
+        state = SendMessage(hwnd, BM_GETCHECK, 0, 0);
+        if (button[i].style == BS_PUSHBUTTON ||
+            button[i].style == BS_DEFPUSHBUTTON ||
+            button[i].style == BS_GROUPBOX ||
+            button[i].style == BS_USERBUTTON ||
+            button[i].style == BS_OWNERDRAW)
+            ok(state == BST_UNCHECKED, "expected check 0, got %04x\n", state);
+        else
+            ok(state == BST_CHECKED, "expected check 1, got %04x\n", state);
+
+        style = GetWindowLongA(hwnd, GWL_STYLE);
+        style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
+        if (button[i].style == BS_RADIOBUTTON ||
+            button[i].style == BS_AUTORADIOBUTTON)
+            ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
+        else
+            ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
 
         log_all_parent_messages--;
 
@@ -6866,8 +7019,8 @@ static const struct message WmVkAppsSeq[] = {
     { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_APPS, 0xc0000001 }, /* XP */
     { WM_KEYUP, wparam|lparam, VK_APPS, 0xc0000001 },
     { WM_KEYUP, sent|wparam|lparam, VK_APPS, 0xc0000001 },
-    { WM_CONTEXTMENU, lparam, /*hwnd*/0, (LPARAM)-1 },
-    { WM_CONTEXTMENU, sent|lparam, /*hwnd*/0, (LPARAM)-1 },
+    { WM_CONTEXTMENU, lparam, /*hwnd*/0, -1 },
+    { WM_CONTEXTMENU, sent|lparam, /*hwnd*/0, -1 },
     { 0 }
 };
 static const struct message WmVkF10Seq[] = {
@@ -6900,6 +7053,32 @@ static const struct message WmVkF10Seq[] = {
     { WM_SYSKEYUP, sent|wparam|lparam, VK_F10, 0xc0000001 },
     { 0 }
 };
+static const struct message WmShiftF10Seq[] = {
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_SHIFT, 1 }, /* XP */
+    { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 0x00000001 },
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_F10, 1 }, /* XP */
+    { WM_SYSKEYDOWN, wparam|lparam, VK_F10, 1 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, VK_F10, 0x00000001 },
+    { WM_CONTEXTMENU, sent|defwinproc|lparam, /*hwnd*/0, -1 },
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_F10, 0xc0000001 }, /* XP */
+    { WM_SYSKEYUP, wparam|lparam, VK_F10, 0xc0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, VK_F10, 0xc0000001 },
+    { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_KEYMENU },
+    { HCBT_SYSCOMMAND, hook },
+    { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+    { WM_INITMENU, sent|defwinproc },
+    { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,MF_SYSMENU|MF_POPUP|MF_HILITE) },
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_SHIFT, 0xd0000001 }, /* XP */
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_ESCAPE, 0x10000001 }, /* XP */
+    { WM_CAPTURECHANGED, sent|defwinproc|wparam|lparam, 0, 0 },
+    { WM_MENUSELECT, sent|defwinproc|wparam|lparam, 0xffff0000, 0 },
+    { WM_EXITMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_ESCAPE, 0xc0000001 }, /* XP */
+    { WM_KEYUP, wparam|lparam, VK_ESCAPE, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_ESCAPE, 0xc0000001 },
+    { 0 }
+};
 
 static void pump_msg_loop(HWND hwnd, HACCEL hAccel)
 {
@@ -7120,6 +7299,16 @@ static void test_accelerators(void)
     pump_msg_loop(hwnd, 0);
     ok_sequence(WmVkF10Seq, "VK_F10 press/release", TRUE);
 
+    trace("testing SHIFT+F10 press/release\n");
+    keybd_event(VK_SHIFT, 0, 0, 0);
+    keybd_event(VK_F10, 0, 0, 0);
+    keybd_event(VK_F10, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_ESCAPE, 0, 0, 0);
+    keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, 0);
+    ok_sequence(WmShiftF10Seq, "SHIFT+F10 press/release", TRUE);
+
     trace("testing Shift+MouseButton press/release\n");
     /* first, move mouse pointer inside of the window client area */
     GetClientRect(hwnd, &rc);
@@ -9324,17 +9513,21 @@ static void test_PeekMessage(void)
         skip( "queuing key events not supported\n" );
         goto done;
     }
-    ok(qstatus == MAKELONG(QS_KEY, QS_KEY),
+    ok(qstatus == MAKELONG(QS_KEY, QS_KEY) ||
+       /* keybd_event seems to trigger a sent message on NT4 */
+       qstatus == MAKELONG(QS_KEY|QS_SENDMESSAGE, QS_KEY|QS_SENDMESSAGE),
        "wrong qstatus %08x\n", qstatus);
 
     PostMessageA(info.hwnd, WM_CHAR, 'z', 0);
     qstatus = GetQueueStatus(qs_all_input);
-    ok(qstatus == MAKELONG(QS_POSTMESSAGE, QS_POSTMESSAGE|QS_KEY),
+    ok(qstatus == MAKELONG(QS_POSTMESSAGE, QS_POSTMESSAGE|QS_KEY) ||
+       qstatus == MAKELONG(QS_POSTMESSAGE, QS_POSTMESSAGE|QS_KEY|QS_SENDMESSAGE),
        "wrong qstatus %08x\n", qstatus);
 
     InvalidateRect(info.hwnd, NULL, FALSE);
     qstatus = GetQueueStatus(qs_all_input);
-    ok(qstatus == MAKELONG(QS_PAINT, QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+    ok(qstatus == MAKELONG(QS_PAINT, QS_PAINT|QS_POSTMESSAGE|QS_KEY) ||
+       qstatus == MAKELONG(QS_PAINT, QS_PAINT|QS_POSTMESSAGE|QS_KEY|QS_SENDMESSAGE),
        "wrong qstatus %08x\n", qstatus);
 
     trace("signalling to send message\n");
@@ -9896,7 +10089,7 @@ static void pump_msg_loop_timeout(DWORD timeout, BOOL inject_mouse_move)
             /* Timer proc messages are not dispatched to the window proc,
              * and therefore not logged.
              */
-            if (msg.message == WM_TIMER || msg.message == WM_SYSTIMER)
+            if ((msg.message == WM_TIMER || msg.message == WM_SYSTIMER) && msg.hwnd)
             {
                 struct recvd_message s_msg;
 
@@ -10291,8 +10484,8 @@ static const struct message WmRestore_5[] = {
 };
 static const struct message WmHide_1[] = {
     { WM_SHOWWINDOW, sent|wparam, 0 },
-    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE, 0, SWP_NOACTIVATE },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE, 0, SWP_NOACTIVATE },
     { HCBT_ACTIVATE, hook|optional },
     { HCBT_SETFOCUS, hook|optional }, /* win2000 sends it */
     { 0 }
@@ -10840,6 +11033,7 @@ static const struct message SetActiveWindowSeq4[] =
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE },
     { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
     { WM_NCACTIVATE, sent|wparam, 1 },
     { WM_GETTEXT, sent|defwinproc|optional },
     { WM_ACTIVATE, sent|wparam, 1 },
@@ -11469,7 +11663,7 @@ static const struct message wm_popup_menu_1[] =
     { WM_MENUSELECT, sent|wparam, MAKEWPARAM(200,MF_HILITE) },
     { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, 'E', 0xf0000001 },
     { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_MENU, 0xd0000001 },
-    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_RETURN, 0x10000001 },
+    { HCBT_KEYSKIPPED, hook|wparam|lparam|optional, VK_RETURN, 0x10000001, 0, 0x40000000 },
     { HCBT_DESTROYWND, hook|optional }, /* Win9x doesn't create a window */
     { WM_UNINITMENUPOPUP, sent|lparam, 0, 0 },
     { WM_MENUSELECT, sent|wparam|lparam, MAKEWPARAM(0,0xffff), 0 },
@@ -12014,11 +12208,315 @@ static void test_PostMessage(void)
     flush_events();
 }
 
+static const struct
+{
+    DWORD exp, broken;
+    BOOL todo;
+} wait_idle_expect[] =
+{
+/* 0 */  { WAIT_TIMEOUT, WAIT_TIMEOUT, FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, WAIT_TIMEOUT, FALSE },
+         { WAIT_TIMEOUT, WAIT_TIMEOUT, FALSE },
+/* 5 */  { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, WAIT_TIMEOUT, FALSE },
+         { 0,            0,            FALSE },
+         { 0,            0,            FALSE },
+/* 10 */ { 0,            0,            FALSE },
+         { 0,            0,            FALSE },
+         { 0,            WAIT_TIMEOUT, FALSE },
+         { 0,            0,            FALSE },
+         { 0,            0,            FALSE },
+/* 15 */ { 0,            0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+         { WAIT_TIMEOUT, 0,            FALSE },
+/* 20 */ { WAIT_TIMEOUT, 0,            FALSE },
+};
+
+static DWORD CALLBACK do_wait_idle_child_thread( void *arg )
+{
+    MSG msg;
+
+    PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );
+    Sleep( 200 );
+    MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT );
+    return 0;
+}
+
+static void do_wait_idle_child( int arg )
+{
+    WNDCLASS cls;
+    MSG msg;
+    HWND hwnd = 0;
+    HANDLE thread;
+    DWORD id;
+    HANDLE start_event = OpenEventA( EVENT_ALL_ACCESS, FALSE, "test_WaitForInputIdle_start" );
+    HANDLE end_event = OpenEventA( EVENT_ALL_ACCESS, FALSE, "test_WaitForInputIdle_end" );
+
+    memset( &cls, 0, sizeof(cls) );
+    cls.lpfnWndProc   = DefWindowProc;
+    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    cls.hCursor       = LoadCursor(0, IDC_ARROW);
+    cls.lpszClassName = "TestClass";
+    RegisterClass( &cls );
+
+    PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );  /* create the msg queue */
+
+    ok( start_event != 0, "failed to create start event, error %u\n", GetLastError() );
+    ok( end_event != 0, "failed to create end event, error %u\n", GetLastError() );
+
+    switch (arg)
+    {
+    case 0:
+        SetEvent( start_event );
+        break;
+    case 1:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, 0, 0, 0, PM_REMOVE );
+        break;
+    case 2:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );
+        PostThreadMessage( GetCurrentThreadId(), WM_COMMAND, 0x1234, 0xabcd );
+        PeekMessage( &msg, 0, 0, 0, PM_REMOVE );
+        break;
+    case 3:
+        SetEvent( start_event );
+        Sleep( 200 );
+        SendMessage( HWND_BROADCAST, WM_WININICHANGE, 0, 0 );
+        break;
+    case 4:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE|PM_NOYIELD )) DispatchMessage( &msg );
+        break;
+    case 5:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+        break;
+    case 6:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        while (PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE ))
+        {
+            GetMessage( &msg, 0, 0, 0 );
+            DispatchMessage( &msg );
+        }
+        break;
+    case 7:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        SetTimer( hwnd, 3, 1, NULL );
+        Sleep( 200 );
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE|PM_NOYIELD )) DispatchMessage( &msg );
+        break;
+    case 8:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );
+        MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT );
+        break;
+    case 9:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+        for (;;) GetMessage( &msg, 0, 0, 0 );
+        break;
+    case 10:
+        SetEvent( start_event );
+        Sleep( 200 );
+        hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+        SetTimer( hwnd, 3, 1, NULL );
+        Sleep( 200 );
+        while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+        break;
+    case 11:
+        SetEvent( start_event );
+        Sleep( 200 );
+        return;  /* exiting the process makes WaitForInputIdle return success too */
+    case 12:
+        PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );
+        Sleep( 200 );
+        MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT );
+        SetEvent( start_event );
+        break;
+    case 13:
+        SetEvent( start_event );
+        PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );
+        Sleep( 200 );
+        thread = CreateThread( NULL, 0, do_wait_idle_child_thread, NULL, 0, &id );
+        WaitForSingleObject( thread, 10000 );
+        CloseHandle( thread );
+        break;
+    case 14:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, HWND_TOPMOST, 0, 0, PM_NOREMOVE );
+        break;
+    case 15:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, HWND_BROADCAST, 0, 0, PM_NOREMOVE );
+        break;
+    case 16:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, HWND_BOTTOM, 0, 0, PM_NOREMOVE );
+        break;
+    case 17:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, (HWND)0xdeadbeef, 0, 0, PM_NOREMOVE );
+        break;
+    case 18:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, HWND_NOTOPMOST, 0, 0, PM_NOREMOVE );
+        break;
+    case 19:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, HWND_MESSAGE, 0, 0, PM_NOREMOVE );
+        break;
+    case 20:
+        SetEvent( start_event );
+        Sleep( 200 );
+        PeekMessage( &msg, GetDesktopWindow(), 0, 0, PM_NOREMOVE );
+        break;
+    }
+    WaitForSingleObject( end_event, 2000 );
+    CloseHandle( start_event );
+    CloseHandle( end_event );
+    if (hwnd) DestroyWindow( hwnd );
+}
+
+static LRESULT CALLBACK wait_idle_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
+{
+    if (msg == WM_WININICHANGE) Sleep( 200 );  /* make sure the child waits */
+    return DefWindowProcA( hwnd, msg, wp, lp );
+}
+
+static DWORD CALLBACK wait_idle_thread( void *arg )
+{
+    WNDCLASS cls;
+    MSG msg;
+    HWND hwnd;
+
+    memset( &cls, 0, sizeof(cls) );
+    cls.lpfnWndProc   = wait_idle_proc;
+    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    cls.hCursor       = LoadCursor(0, IDC_ARROW);
+    cls.lpszClassName = "TestClass";
+    RegisterClass( &cls );
+
+    hwnd = CreateWindowExA(0, "TestClass", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL);
+    while (GetMessage( &msg, 0, 0, 0 )) DispatchMessage( &msg );
+    DestroyWindow(hwnd);
+    return 0;
+}
+
+static void test_WaitForInputIdle( char *argv0 )
+{
+    char path[MAX_PATH];
+    PROCESS_INFORMATION pi;
+    STARTUPINFOA startup;
+    BOOL ret;
+    HANDLE start_event, end_event, thread;
+    unsigned int i;
+    DWORD id;
+    const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)GetModuleHandleA(0);
+    const IMAGE_NT_HEADERS *nt = (const IMAGE_NT_HEADERS *)((const char *)dos + dos->e_lfanew);
+    BOOL console_app = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI);
+
+    if (console_app)  /* build the test with -mwindows for better coverage */
+        trace( "not built as a GUI app, WaitForInputIdle may not be fully tested\n" );
+
+    start_event = CreateEventA(NULL, 0, 0, "test_WaitForInputIdle_start");
+    end_event = CreateEventA(NULL, 0, 0, "test_WaitForInputIdle_end");
+    ok(start_event != 0, "failed to create start event, error %u\n", GetLastError());
+    ok(end_event != 0, "failed to create end event, error %u\n", GetLastError());
+
+    memset( &startup, 0, sizeof(startup) );
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    thread = CreateThread( NULL, 0, wait_idle_thread, NULL, 0, &id );
+
+    for (i = 0; i < sizeof(wait_idle_expect)/sizeof(wait_idle_expect[0]); i++)
+    {
+        ResetEvent( start_event );
+        ResetEvent( end_event );
+        sprintf( path, "%s msg %u", argv0, i );
+        ret = CreateProcessA( NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi );
+        ok( ret, "CreateProcess '%s' failed err %u.\n", path, GetLastError() );
+        if (ret)
+        {
+            ret = WaitForSingleObject( start_event, 5000 );
+            ok( ret == WAIT_OBJECT_0, "%u: WaitForSingleObject failed\n", i );
+            if (ret == WAIT_OBJECT_0)
+            {
+                ret = WaitForInputIdle( pi.hProcess, 1000 );
+                if (ret == WAIT_FAILED)
+                    ok( console_app ||
+                        ret == wait_idle_expect[i].exp ||
+                        broken(ret == wait_idle_expect[i].broken),
+                        "%u: WaitForInputIdle error %08x expected %08x\n",
+                        i, ret, wait_idle_expect[i].exp );
+                else if (wait_idle_expect[i].todo)
+                    todo_wine
+                    ok( ret == wait_idle_expect[i].exp || broken(ret == wait_idle_expect[i].broken),
+                        "%u: WaitForInputIdle error %08x expected %08x\n",
+                        i, ret, wait_idle_expect[i].exp );
+                else
+                    ok( ret == wait_idle_expect[i].exp || broken(ret == wait_idle_expect[i].broken),
+                        "%u: WaitForInputIdle error %08x expected %08x\n",
+                        i, ret, wait_idle_expect[i].exp );
+                SetEvent( end_event );
+                WaitForSingleObject( pi.hProcess, 1000 );  /* give it a chance to exit on its own */
+            }
+            TerminateProcess( pi.hProcess, 0 );  /* just in case */
+            winetest_wait_child_process( pi.hProcess );
+            ret = WaitForInputIdle( pi.hProcess, 100 );
+            ok( ret == WAIT_FAILED, "%u: WaitForInputIdle after exit error %08x\n", i, ret );
+            CloseHandle( pi.hProcess );
+            CloseHandle( pi.hThread );
+        }
+    }
+    CloseHandle( start_event );
+    PostThreadMessage( id, WM_QUIT, 0, 0 );
+    WaitForSingleObject( thread, 10000 );
+    CloseHandle( thread );
+}
+
 START_TEST(msg)
 {
+    char **test_argv;
     BOOL ret;
     BOOL (WINAPI *pIsWinEventHookInstalled)(DWORD)= 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
 
+    int argc = winetest_get_mainargs( &test_argv );
+    if (argc >= 3)
+    {
+        unsigned int arg;
+        /* Child process. */
+        sscanf (test_argv[2], "%d", (unsigned int *) &arg);
+        do_wait_idle_child( arg );
+        return;
+    }
+
     init_procs();
 
     if (!RegisterWindowClasses()) assert(0);
@@ -12059,6 +12557,7 @@ START_TEST(msg)
     test_ShowWindow();
     test_PeekMessage();
     test_PeekMessage2();
+    test_WaitForInputIdle( test_argv[0] );
     test_scrollwindowex();
     test_messages();
     test_setwindowpos();