sync user32 winetest with wine 1.1.28
authorChristoph von Wittich <christoph_vw@reactos.org>
Mon, 24 Aug 2009 12:42:47 +0000 (12:42 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Mon, 24 Aug 2009 12:42:47 +0000 (12:42 +0000)
svn path=/trunk/; revision=42911

rostests/winetests/user32/combo.c
rostests/winetests/user32/cursoricon.c
rostests/winetests/user32/dialog.c
rostests/winetests/user32/input.c
rostests/winetests/user32/menu.c
rostests/winetests/user32/msg.c
rostests/winetests/user32/win.c

index f7450ea..8108dce 100644 (file)
@@ -94,28 +94,29 @@ static void test_setitemheight(DWORD style)
 
 static void test_setfont(DWORD style)
 {
-    HWND hCombo = build_combo(style);
-    HFONT hFont1 = CreateFont(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
-    HFONT hFont2 = CreateFont(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+    HWND hCombo;
+    HFONT hFont1, hFont2;
     RECT r;
     int i;
 
+    if (!is_font_installed("Marlett"))
+    {
+        skip("Marlett font not available\n");
+        return;
+    }
+
     trace("Style %x\n", style);
+
+    hCombo = build_combo(style);
+    hFont1 = CreateFont(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+    hFont2 = CreateFont(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+
     GetClientRect(hCombo, &r);
     expect_rect(r, 0, 0, 100, 24);
     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
     todo_wine expect_rect(r, 5, 5, 105, 105);
 
-    if (!is_font_installed("Marlett"))
-    {
-        skip("Marlett font not available\n");
-        DestroyWindow(hCombo);
-        DeleteObject(hFont1);
-        DeleteObject(hFont2);
-        return;
-    }
-
     if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
     {
         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
@@ -140,11 +141,14 @@ static void test_setfont(DWORD style)
         todo_wine expect_rect(r, 5, 5, 105, 99);
     }
     else
-        skip("Invalid Marlett font heights\n");
+    {
+        ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
+           font_height(hFont1), font_height(hFont2));
+    }
 
     for (i = 1; i < 30; i++)
     {
-        HFONT hFont = CreateFont(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+        HFONT hFont = CreateFont(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
         int height = font_height(hFont);
 
         SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
index 9475534..3f8bd82 100644 (file)
@@ -956,6 +956,262 @@ static void test_CreateIconFromResource(void)
     HeapFree(GetProcessHeap(), 0, hotspot);
 }
 
+static HICON create_test_icon(HDC hdc, int width, int height, int bpp,
+                              BOOL maskvalue, UINT32 *color, int colorSize)
+{
+    ICONINFO iconInfo;
+    BITMAPINFO bitmapInfo;
+    UINT32 *buffer = NULL;
+    UINT32 mask = maskvalue ? 0xFFFFFFFF : 0x00000000;
+
+    memset(&bitmapInfo, 0, sizeof(bitmapInfo));
+    bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bitmapInfo.bmiHeader.biWidth = width;
+    bitmapInfo.bmiHeader.biHeight = height;
+    bitmapInfo.bmiHeader.biPlanes = 1;
+    bitmapInfo.bmiHeader.biBitCount = bpp;
+    bitmapInfo.bmiHeader.biCompression = BI_RGB;
+    bitmapInfo.bmiHeader.biSizeImage = colorSize;
+
+    iconInfo.fIcon = TRUE;
+    iconInfo.xHotspot = 0;
+    iconInfo.yHotspot = 0;
+
+    iconInfo.hbmMask = CreateBitmap( width, height, 1, 1, &mask );
+    if(!iconInfo.hbmMask) return NULL;
+
+    iconInfo.hbmColor = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, NULL, 0);
+    if(!iconInfo.hbmColor || !buffer)
+    {
+        DeleteObject(iconInfo.hbmMask);
+        return NULL;
+    }
+
+    memcpy(buffer, color, colorSize);
+
+    return CreateIconIndirect(&iconInfo);
+}
+
+static BOOL color_match(COLORREF a, COLORREF b)
+{
+    /* 5-bit accuracy is a sufficient test. This will match, so long as
+     * colors are never truncated to less that 3x5-bit accuracy i.e.
+     * paletized. */
+    return (a & 0x00F8F8F8) == (b & 0x00F8F8F8);
+}
+
+static void check_alpha_draw(HDC hdc, BOOL drawiconex, BOOL alpha, int bpp, int line)
+{
+    HICON hicon;
+    UINT32 mask;
+    UINT32 color[2];
+    COLORREF modern_expected, legacy_expected, result;
+
+    mask = 0x00000000;
+    color[0] = 0x00A0B0C0;
+    color[1] = alpha ? 0xFF000000 : 0x00000000;
+    modern_expected = alpha ? 0x00FFFFFF : 0x00C0B0A0;
+    legacy_expected = 0x00C0B0A0;
+
+    hicon = create_test_icon(hdc, 2, 1, bpp, 0, color, sizeof(color));
+    if (!hicon) return;
+
+    SetPixelV(hdc, 0, 0, 0x00FFFFFF);
+
+    if(drawiconex)
+        DrawIconEx(hdc, 0, 0, hicon, 2, 1, 0, NULL, DI_NORMAL);
+    else
+        DrawIcon(hdc, 0, 0, hicon);
+
+    result = GetPixel(hdc, 0, 0);
+    ok (color_match(result, modern_expected) ||         /* Windows 2000 and up */
+        broken(color_match(result, legacy_expected)),   /* Windows NT 4.0, 9X and below */
+        "%s. Expected a close match to %06X (modern) or %06X (legacy) with %s. "
+        "Got %06X from line %d\n",
+        alpha ? "Alpha blending" : "Not alpha blending", modern_expected, legacy_expected,
+        drawiconex ? "DrawIconEx" : "DrawIcon", result, line);
+}
+
+static void check_DrawIcon(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, COLORREF background,
+                           COLORREF modern_expected, COLORREF legacy_expected, int line)
+{
+    COLORREF result;
+    HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
+    if (!hicon) return;
+    SetPixelV(hdc, 0, 0, background);
+    DrawIcon(hdc, 0, 0, hicon);
+    result = GetPixel(hdc, 0, 0);
+
+    ok (color_match(result, modern_expected) ||         /* Windows 2000 and up */
+        broken(color_match(result, legacy_expected)),   /* Windows NT 4.0, 9X and below */
+        "Overlaying Mask %d on Color %06X with DrawIcon. "
+        "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n",
+        maskvalue, color, modern_expected, legacy_expected, result, line);
+}
+
+static void test_DrawIcon(void)
+{
+    BITMAPINFO bitmapInfo;
+    HDC hdcDst = NULL;
+    HBITMAP bmpDst = NULL;
+    HBITMAP bmpOld = NULL;
+    UINT32 *bits = 0;
+
+    hdcDst = CreateCompatibleDC(0);
+    ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n");
+    if (!hdcDst)
+        return;
+
+    if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8)
+    {
+        skip("Windows will distort DrawIcon colors at 8-bpp and less due to palletizing.\n");
+        goto cleanup;
+    }
+
+    memset(&bitmapInfo, 0, sizeof(bitmapInfo));
+    bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bitmapInfo.bmiHeader.biWidth = 1;
+    bitmapInfo.bmiHeader.biHeight = 1;
+    bitmapInfo.bmiHeader.biBitCount = 32;
+    bitmapInfo.bmiHeader.biPlanes = 1;
+    bitmapInfo.bmiHeader.biCompression = BI_RGB;
+    bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32);
+
+    bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+    ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n");
+    if (!bmpDst || !bits)
+        goto cleanup;
+    bmpOld = SelectObject(hdcDst, bmpDst);
+
+    /* Mask is only heeded if alpha channel is always zero */
+    check_DrawIcon(hdcDst, FALSE, 0x00A0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+    check_DrawIcon(hdcDst, TRUE, 0x00A0B0C0, 32, 0x00FFFFFF, 0x003F4F5F, 0x003F4F5F, __LINE__);
+
+    /* Test alpha blending */
+    /* Windows 2000 and up will alpha blend, earlier Windows versions will not */
+    check_DrawIcon(hdcDst, FALSE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+    check_DrawIcon(hdcDst, TRUE, 0xFFA0B0C0, 32, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__);
+
+    check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
+    check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
+    check_DrawIcon(hdcDst, FALSE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__);
+    check_DrawIcon(hdcDst, TRUE, 0x80A0B0C0, 32, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__);
+
+    check_DrawIcon(hdcDst, FALSE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
+    check_DrawIcon(hdcDst, TRUE, 0x01FFFFFF, 32, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
+
+    /* Test detecting of alpha channel */
+    /* If a single pixel's alpha channel is non-zero, the icon
+       will be alpha blended, otherwise it will be draw with
+       and + xor blts. */
+    check_alpha_draw(hdcDst, FALSE, FALSE, 32, __LINE__);
+    check_alpha_draw(hdcDst, FALSE, TRUE, 32, __LINE__);
+
+cleanup:
+    if(bmpOld)
+        SelectObject(hdcDst, bmpOld);
+    if(bmpDst)
+        DeleteObject(bmpDst);
+    if(hdcDst)
+        DeleteDC(hdcDst);
+}
+
+static void check_DrawIconEx(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, UINT flags, COLORREF background,
+                             COLORREF modern_expected, COLORREF legacy_expected, int line)
+{
+    COLORREF result;
+    HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
+    if (!hicon) return;
+    SetPixelV(hdc, 0, 0, background);
+    DrawIconEx(hdc, 0, 0, hicon, 1, 1, 0, NULL, flags);
+    result = GetPixel(hdc, 0, 0);
+
+    ok (color_match(result, modern_expected) ||         /* Windows 2000 and up */
+        broken(color_match(result, legacy_expected)),   /* Windows NT 4.0, 9X and below */
+        "Overlaying Mask %d on Color %06X with DrawIconEx flags %08X. "
+        "Expected a close match to %06X (modern) or %06X (legacy). Got %06X from line %d\n",
+        maskvalue, color, flags, modern_expected, legacy_expected, result, line);
+}
+
+static void test_DrawIconEx(void)
+{
+    BITMAPINFO bitmapInfo;
+    HDC hdcDst = NULL;
+    HBITMAP bmpDst = NULL;
+    HBITMAP bmpOld = NULL;
+    UINT32 bits = 0;
+
+    hdcDst = CreateCompatibleDC(0);
+    ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n");
+    if (!hdcDst)
+        return;
+
+    if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8)
+    {
+        skip("Windows will distort DrawIconEx colors at 8-bpp and less due to palletizing.\n");
+        goto cleanup;
+    }
+
+    memset(&bitmapInfo, 0, sizeof(bitmapInfo));
+    bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bitmapInfo.bmiHeader.biWidth = 1;
+    bitmapInfo.bmiHeader.biHeight = 1;
+    bitmapInfo.bmiHeader.biBitCount = 32;
+    bitmapInfo.bmiHeader.biPlanes = 1;
+    bitmapInfo.bmiHeader.biCompression = BI_RGB;
+    bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32);
+    bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+    ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n");
+    if (!bmpDst || !bits)
+        goto cleanup;
+    bmpOld = SelectObject(hdcDst, bmpDst);
+
+    /* Test null, image only, and mask only drawing */
+    check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, 0, 0x00102030, 0x00102030, 0x00102030, __LINE__);
+    check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, 0, 0x00102030, 0x00102030, 0x00102030, __LINE__);
+
+    check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_MASK, 0x00FFFFFF, 0x00000000, 0x00000000, __LINE__);
+    check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_MASK, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, __LINE__);
+
+    todo_wine
+    {
+        check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, DI_IMAGE, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+        check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, DI_IMAGE, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+    }
+
+    /* Test normal drawing */
+    check_DrawIconEx(hdcDst, FALSE, 0x00A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+    todo_wine check_DrawIconEx(hdcDst, TRUE, 0x00A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x003F4F5F, 0x003F4F5F, __LINE__);
+    check_DrawIconEx(hdcDst, FALSE, 0xFFA0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+
+    /* Test alpha blending */
+    /* Windows 2000 and up will alpha blend, earlier Windows versions will not */
+    check_DrawIconEx(hdcDst, TRUE, 0xFFA0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x003F4F5F, __LINE__);
+
+    check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_NORMAL, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
+    check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_NORMAL, 0x00000000, 0x00605850, 0x00C0B0A0, __LINE__);
+    check_DrawIconEx(hdcDst, FALSE, 0x80A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00DFD7CF, 0x00C0B0A0, __LINE__);
+    check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_NORMAL, 0x00FFFFFF, 0x00DFD7CF, 0x003F4F5F, __LINE__);
+
+    check_DrawIconEx(hdcDst, FALSE, 0x01FFFFFF, 32, DI_NORMAL, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
+    check_DrawIconEx(hdcDst, TRUE, 0x01FFFFFF, 32, DI_NORMAL, 0x00000000, 0x00010101, 0x00FFFFFF, __LINE__);
+
+    /* Test detecting of alpha channel */
+    /* If a single pixel's alpha channel is non-zero, the icon
+       will be alpha blended, otherwise it will be draw with
+       and + xor blts. */
+    check_alpha_draw(hdcDst, TRUE, FALSE, 32, __LINE__);
+    check_alpha_draw(hdcDst, TRUE, TRUE, 32, __LINE__);
+
+cleanup:
+    if(bmpOld)
+        SelectObject(hdcDst, bmpOld);
+    if(bmpDst)
+        DeleteObject(bmpDst);
+    if(hdcDst)
+        DeleteDC(hdcDst);
+}
+
 static void test_DestroyCursor(void)
 {
     static const BYTE bmp_bits[4096];
@@ -1063,6 +1319,8 @@ START_TEST(cursoricon)
     test_CreateIcon();
     test_LoadImage();
     test_CreateIconFromResource();
+    test_DrawIcon();
+    test_DrawIconEx();
     test_DestroyCursor();
     do_parent();
     test_child_process();
index 060625c..dfb2483 100755 (executable)
@@ -901,23 +901,72 @@ static void test_GetDlgItemText(void)
        "string retrieved using GetDlgItemText should have been NULL terminated\n");
 }
 
+static INT_PTR CALLBACK DestroyDlgWinProc (HWND hDlg, UINT uiMsg,
+        WPARAM wParam, LPARAM lParam)
+{
+    if (uiMsg == WM_INITDIALOG)
+    {
+        DestroyWindow(hDlg);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static INT_PTR CALLBACK DestroyOnCloseDlgWinProc (HWND hDlg, UINT uiMsg,
+        WPARAM wParam, LPARAM lParam)
+{
+    switch (uiMsg)
+    {
+    case WM_INITDIALOG:
+        PostMessage(hDlg, WM_CLOSE, 0, 0);
+        return TRUE;
+    case WM_CLOSE:
+        DestroyWindow(hDlg);
+        return TRUE;
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        return TRUE;
+    }
+    return FALSE;
+}
+
 static void test_DialogBoxParamA(void)
 {
-    int ret;
+    INT_PTR ret;
     HWND hwnd_invalid = (HWND)0x4444;
 
     SetLastError(0xdeadbeef);
     ret = DialogBoxParamA(GetModuleHandle(NULL), "IDD_DIALOG" , hwnd_invalid, 0 , 0);
-    ok(0 == ret || broken(ret == -1), "DialogBoxParamA returned %d, expected 0\n", ret);
+    ok(0 == ret || broken(ret == -1), "DialogBoxParamA returned %ld, expected 0\n", ret);
+    ok(ERROR_INVALID_WINDOW_HANDLE == GetLastError() ||
+       broken(GetLastError() == 0xdeadbeef),
+       "got %d, expected ERROR_INVALID_WINDOW_HANDLE\n",GetLastError());
+
+    /* Test a dialog which destroys itself on WM_INITDIALOG. */
+    SetLastError(0xdeadbeef);
+    ret = DialogBoxParamA(GetModuleHandle(NULL), "IDD_DIALOG", 0, DestroyDlgWinProc, 0);
+    ok(-1 == ret, "DialogBoxParamA returned %ld, expected -1\n", ret);
     ok(ERROR_INVALID_WINDOW_HANDLE == GetLastError() ||
+       GetLastError() == ERROR_SUCCESS ||
        broken(GetLastError() == 0xdeadbeef),
        "got %d, expected ERROR_INVALID_WINDOW_HANDLE\n",GetLastError());
+
+    /* Test a dialog which destroys itself on WM_CLOSE. */
+    ret = DialogBoxParamA(GetModuleHandle(NULL), "IDD_DIALOG", 0, DestroyOnCloseDlgWinProc, 0);
+    ok(0 == ret, "DialogBoxParamA returned %ld, expected 0\n", ret);
+
     SetLastError(0xdeadbeef);
     ret = DialogBoxParamA(GetModuleHandle(NULL), "RESOURCE_INVALID" , 0, 0, 0);
-    ok(-1 == ret, "DialogBoxParamA returned %d, expected -1\n", ret);
+    ok(-1 == ret, "DialogBoxParamA returned %ld, expected -1\n", ret);
     ok(ERROR_RESOURCE_NAME_NOT_FOUND == GetLastError() ||
        broken(GetLastError() == 0xdeadbeef),
        "got %d, expected ERROR_RESOURCE_NAME_NOT_FOUND\n",GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = DefDlgProcA(0, WM_ERASEBKGND, 0, 0);
+    ok(ret == 0, "DefDlgProcA returned %ld, expected 0\n", ret);
+    ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE,
+       "got %d, expected ERROR_INVALID_WINDOW_HANDLE\n", GetLastError());
 }
 
 static void test_DisabledDialogTest(void)
@@ -1020,6 +1069,63 @@ static void test_MessageBoxFontTest(void)
     DestroyWindow(hDlg);
 }
 
+static void test_SaveRestoreFocus(void)
+{
+    HWND hDlg;
+    HRSRC hResource;
+    HANDLE hTemplate;
+    DLGTEMPLATE* pTemplate;
+    LONG_PTR foundId;
+    HWND foundHwnd;
+
+    /* create the dialog */
+    hResource = FindResourceA(g_hinst, "MULTI_EDIT_DIALOG", RT_DIALOG);
+    hTemplate = LoadResource(g_hinst, hResource);
+    pTemplate = LockResource(hTemplate);
+
+    hDlg = CreateDialogIndirectParamA(g_hinst, pTemplate, NULL, messageBoxFontDlgWinProc, 0);
+    ok (hDlg != 0, "Failed to create test dialog.\n");
+
+    foundId = GetWindowLongPtr(GetFocus(), GWLP_ID);
+    ok (foundId == 1000, "First edit box should have gained focus on dialog creation. Expected: %d, Found: %ld\n", 1000, foundId);
+
+    /* de- then reactivate the dialog */
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), 0);
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), 0);
+
+    foundId = GetWindowLongPtr(GetFocus(), GWLP_ID);
+    ok (foundId == 1000, "First edit box should have regained focus after dialog reactivation. Expected: %d, Found: %ld\n", 1000, foundId);
+
+    /* select the next tabbable item */
+    SetFocus(GetNextDlgTabItem(hDlg, GetFocus(), FALSE));
+
+    foundId = GetWindowLongPtr(GetFocus(), GWLP_ID);
+    ok (foundId == 1001, "Second edit box should have gained focus. Expected: %d, Found: %ld\n", 1001, foundId);
+
+    /* de- then reactivate the dialog */
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), 0);
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), 0);
+
+    foundId = GetWindowLongPtr(GetFocus(), GWLP_ID);
+    ok (foundId == 1001, "Second edit box should have gained focus after dialog reactivation. Expected: %d, Found: %ld\n", 1001, foundId);
+
+    /* disable the 2nd box */
+    EnableWindow(GetFocus(), FALSE);
+
+    foundHwnd = GetFocus();
+    ok (foundHwnd == NULL, "Second edit box should have lost focus after being disabled. Expected: %p, Found: %p\n", NULL, foundHwnd);
+
+    /* de- then reactivate the dialog */
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_INACTIVE, 0), 0);
+    SendMessage(hDlg, WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), 0);
+
+    foundHwnd = GetFocus();
+    ok (foundHwnd == NULL, "No controls should have gained focus after dialog reactivation. Expected: %p, Found: %p\n", NULL, foundHwnd);
+
+    /* clean up */
+    DestroyWindow(hDlg);
+}
+
 START_TEST(dialog)
 {
     g_hinst = GetModuleHandleA (0);
@@ -1034,4 +1140,5 @@ START_TEST(dialog)
     test_DialogBoxParamA();
     test_DisabledDialogTest();
     test_MessageBoxFontTest();
+    test_SaveRestoreFocus();
 }
index d100610..7506535 100755 (executable)
@@ -44,7 +44,7 @@
  *
  */
 
-#define _WIN32_WINNT 0x401
+#define _WIN32_WINNT 0x500
 #define _WIN32_IE 0x0500
 
 #include <stdarg.h>
 static HWND hWndTest;
 static LONG timetag = 0x10000000;
 
+static struct {
+    LONG last_key_down;
+    LONG last_key_up;
+    LONG last_syskey_down;
+    LONG last_syskey_up;
+    LONG last_char;
+    LONG last_syschar;
+    LONG last_hook_down;
+    LONG last_hook_up;
+    LONG last_hook_syskey_down;
+    LONG last_hook_syskey_up;
+    BOOL expect_alt;
+} key_status;
+
 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
 static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD);
 
@@ -824,7 +838,8 @@ static LRESULT CALLBACK WndProc2(HWND hWnd, UINT Msg, WPARAM wParam,
         Msg != WM_GETTEXT &&
         Msg != WM_GETICON &&
         Msg != WM_IME_SELECT &&
-        Msg != WM_DEVICECHANGE)
+        Msg != WM_DEVICECHANGE &&
+        Msg != WM_TIMECHANGE)
     {
         ok(sent_messages_cnt < MAXKEYMESSAGES, "Too many messages\n");
         if (sent_messages_cnt < MAXKEYMESSAGES)
@@ -922,6 +937,224 @@ static void test_Input_blackbox(void)
     UnhookWindowsHookEx(hook);
 }
 
+static void reset_key_status(void)
+{
+    key_status.last_key_down = -1;
+    key_status.last_key_up = -1;
+    key_status.last_syskey_down = -1;
+    key_status.last_syskey_up = -1;
+    key_status.last_char = -1;
+    key_status.last_syschar = -1;
+    key_status.last_hook_down = -1;
+    key_status.last_hook_up = -1;
+    key_status.last_hook_syskey_down = -1;
+    key_status.last_hook_syskey_up = -1;
+    key_status.expect_alt = FALSE;
+}
+
+static void test_unicode_keys(HWND hwnd, HHOOK hook)
+{
+    TEST_INPUT inputs[2];
+    MSG msg;
+
+    /* init input data that never changes */
+    inputs[1].type = inputs[0].type = INPUT_KEYBOARD;
+    inputs[1].u.ki.dwExtraInfo = inputs[0].u.ki.dwExtraInfo = 0;
+    inputs[1].u.ki.time = inputs[0].u.ki.time = 0;
+
+    /* pressing & releasing a single unicode character */
+    inputs[0].u.ki.wVk = 0;
+    inputs[0].u.ki.wScan = 0x3c0;
+    inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
+
+    reset_key_status();
+    SendInput(1, (INPUT*)inputs, sizeof(INPUT));
+    while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
+        if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
+            TranslateMessage(&msg);
+        }
+        DispatchMessageW(&msg);
+    }
+    ok(key_status.last_key_down == VK_PACKET,
+        "Last keydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_down);
+    ok(key_status.last_char == 0x3c0,
+        "Last char msg wparam should have been 0x3c0 (was: 0x%x)\n", key_status.last_char);
+    if(hook)
+        ok(key_status.last_hook_down == 0x3c0,
+            "Last hookdown msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_down);
+
+    inputs[1].u.ki.wVk = 0;
+    inputs[1].u.ki.wScan = 0x3c0;
+    inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
+
+    reset_key_status();
+    SendInput(1, (INPUT*)(inputs+1), sizeof(INPUT));
+    while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
+        if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
+            TranslateMessage(&msg);
+        }
+        DispatchMessageW(&msg);
+    }
+    ok(key_status.last_key_up == VK_PACKET,
+        "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
+    if(hook)
+        ok(key_status.last_hook_up == 0x3c0,
+            "Last hookup msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_up);
+
+    /* holding alt, pressing & releasing a unicode character, releasing alt */
+    inputs[0].u.ki.wVk = VK_LMENU;
+    inputs[0].u.ki.wScan = 0;
+    inputs[0].u.ki.dwFlags = 0;
+
+    inputs[1].u.ki.wVk = 0;
+    inputs[1].u.ki.wScan = 0x3041;
+    inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE;
+
+    reset_key_status();
+    key_status.expect_alt = TRUE;
+    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
+        if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
+            TranslateMessage(&msg);
+        }
+        DispatchMessageW(&msg);
+    }
+    ok(key_status.last_syskey_down == VK_PACKET,
+        "Last syskeydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_syskey_down);
+    ok(key_status.last_syschar == 0x3041,
+        "Last syschar msg should have been 0x3041 (was: 0x%x)\n", key_status.last_syschar);
+    if(hook)
+        ok(key_status.last_hook_syskey_down == 0x3041,
+            "Last hooksysdown msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_syskey_down);
+
+    inputs[1].u.ki.wVk = 0;
+    inputs[1].u.ki.wScan = 0x3041;
+    inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
+
+    inputs[0].u.ki.wVk = VK_LMENU;
+    inputs[0].u.ki.wScan = 0;
+    inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
+
+    reset_key_status();
+    key_status.expect_alt = TRUE;
+    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
+        if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
+            TranslateMessage(&msg);
+        }
+        DispatchMessageW(&msg);
+    }
+    ok(key_status.last_key_up == VK_PACKET,
+        "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
+    if(hook)
+        ok(key_status.last_hook_up == 0x3041,
+            "Last hook up msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_up);
+}
+
+static LRESULT CALLBACK unicode_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
+        LPARAM lParam )
+{
+    switch(msg){
+    case WM_KEYDOWN:
+        key_status.last_key_down = wParam;
+        break;
+    case WM_SYSKEYDOWN:
+        key_status.last_syskey_down = wParam;
+        break;
+    case WM_KEYUP:
+        key_status.last_key_up = wParam;
+        break;
+    case WM_SYSKEYUP:
+        key_status.last_syskey_up = wParam;
+        break;
+    case WM_CHAR:
+        key_status.last_char = wParam;
+        break;
+    case WM_SYSCHAR:
+        key_status.last_syschar = wParam;
+        break;
+    }
+    return DefWindowProcW(hWnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK llkbd_unicode_hook(int nCode, WPARAM wParam, LPARAM lParam)
+{
+    if(nCode == HC_ACTION){
+        LPKBDLLHOOKSTRUCT info = (LPKBDLLHOOKSTRUCT)lParam;
+        ok(info->vkCode == VK_PACKET || (key_status.expect_alt && info->vkCode == VK_LMENU), "vkCode should have been VK_PACKET[%04x], was: %04x\n", VK_PACKET, info->vkCode);
+        key_status.expect_alt = FALSE;
+        switch(wParam){
+        case WM_KEYDOWN:
+            key_status.last_hook_down = info->scanCode;
+            break;
+        case WM_KEYUP:
+            key_status.last_hook_up = info->scanCode;
+            break;
+        case WM_SYSKEYDOWN:
+            key_status.last_hook_syskey_down = info->scanCode;
+            break;
+        case WM_SYSKEYUP:
+            key_status.last_hook_syskey_up = info->scanCode;
+            break;
+        }
+    }
+    return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+static void test_Input_unicode(void)
+{
+    WCHAR classNameW[] = {'I','n','p','u','t','U','n','i','c','o','d','e',
+        'K','e','y','T','e','s','t','C','l','a','s','s',0};
+    WCHAR windowNameW[] = {'I','n','p','u','t','U','n','i','c','o','d','e',
+        'K','e','y','T','e','s','t',0};
+    MSG msg;
+    WNDCLASSW wclass;
+    HANDLE hInstance = GetModuleHandleW(NULL);
+    HHOOK hook;
+
+    wclass.lpszClassName = classNameW;
+    wclass.style         = CS_HREDRAW | CS_VREDRAW;
+    wclass.lpfnWndProc   = unicode_wnd_proc;
+    wclass.hInstance     = hInstance;
+    wclass.hIcon         = LoadIcon(0, IDI_APPLICATION);
+    wclass.hCursor       = LoadCursor( NULL, IDC_ARROW);
+    wclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    wclass.lpszMenuName  = 0;
+    wclass.cbClsExtra    = 0;
+    wclass.cbWndExtra    = 0;
+    if(!RegisterClassW(&wclass)){
+        win_skip("Unicode functions not supported\n");
+        return;
+    }
+    /* create the test window that will receive the keystrokes */
+    hWndTest = CreateWindowW(wclass.lpszClassName, windowNameW,
+                             WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
+                             NULL, NULL, hInstance, NULL);
+
+    assert(hWndTest);
+    assert(IsWindowUnicode(hWndTest));
+
+    hook = SetWindowsHookExW(WH_KEYBOARD_LL, llkbd_unicode_hook, GetModuleHandleW(NULL), 0);
+    if(!hook)
+        win_skip("unable to set WH_KEYBOARD_LL hook\n");
+
+    ShowWindow(hWndTest, SW_SHOW);
+    SetWindowPos(hWndTest, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
+    SetForegroundWindow(hWndTest);
+    UpdateWindow(hWndTest);
+
+    /* flush pending messages */
+    while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageW(&msg);
+
+    SetFocus(hWndTest);
+
+    test_unicode_keys(hWndTest, hook);
+
+    if(hook)
+        UnhookWindowsHookEx(hook);
+    DestroyWindow(hWndTest);
+}
+
 static void test_keynames(void)
 {
     int i, len;
@@ -1319,6 +1552,7 @@ START_TEST(input)
     {
         test_Input_blackbox();
         test_Input_whitebox();
+        test_Input_unicode();
     }
     else win_skip("SendInput is not available\n");
 
index 0948f69..a388103 100755 (executable)
@@ -536,7 +536,7 @@ static void test_mbs_help( int ispop, int hassub, int mnuopt,
         mi.cbSize = sizeof(mi);
         mi.fMask = MIM_STYLE;
         pGetMenuInfo( hmenu, &mi);
-        mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
+        if( mnuopt) mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
         ret = pSetMenuInfo( hmenu, &mi);
         ok( ret, "SetMenuInfo failed with error %d\n", GetLastError());
     }
@@ -2451,6 +2451,48 @@ static HMENU create_menu_from_data(const struct menu_data *item, INT item_count)
     return hmenu;
 }
 
+/* use InsertMenuItem: does not set the MFT_BITMAP flag,
+ * and does not accept non-magic bitmaps with invalid
+ * bitmap handles */
+static HMENU create_menuitem_from_data(const struct menu_data *item, INT item_count)
+{
+    HMENU hmenu;
+    INT i;
+    BOOL ret;
+    MENUITEMINFO mii = { sizeof( MENUITEMINFO)};
+
+    hmenu = CreateMenu();
+    assert(hmenu != 0);
+
+    for (i = 0; i < item_count; i++)
+    {
+        SetLastError(0xdeadbeef);
+
+        mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE;
+        mii.fType = 0;
+        if(  item[i].type & MFT_BITMAP)
+        {
+            mii.fMask |= MIIM_BITMAP;
+            mii.hbmpItem =  (HBITMAP)item[i].str;
+        }
+        else if(  item[i].type & MFT_SEPARATOR)
+            mii.fType = MFT_SEPARATOR;
+        else
+        {
+            mii.fMask |= MIIM_STRING;
+            mii.dwTypeData =  (LPSTR)item[i].str;
+            mii.cch = strlen(  item[i].str);
+        }
+        mii.fState = 0;
+        if(  item[i].type & MF_HELP) mii.fType |= MF_HELP;
+        mii.wID = item[i].id;
+        ret = InsertMenuItem( hmenu, -1, TRUE, &mii);
+        ok(ret, "%d: InsertMenuItem(%04x, %04x, %p) error %u\n",
+           i, item[i].type, item[i].id, item[i].str, GetLastError());
+    }
+    return hmenu;
+}
+
 static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT item_count)
 {
     INT count, i;
@@ -2479,14 +2521,12 @@ static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT ite
            "%u: expected fType %04x, got %04x\n", i, item[i].type, mii.fType);
         ok(mii.wID == item[i].id,
            "%u: expected wID %04x, got %04x\n", i, item[i].id, mii.wID);
-        if (item[i].type & (MF_BITMAP | MF_SEPARATOR))
-        {
+        if (mii.hbmpItem || !item[i].str)
             /* For some reason Windows sets high word to not 0 for
              * not "magic" ids.
              */
             ok(LOWORD(mii.hbmpItem) == LOWORD(item[i].str),
                "%u: expected hbmpItem %p, got %p\n", i, item[i].str, mii.hbmpItem);
-        }
         else
         {
             ok(mii.cch == strlen(item[i].str),
@@ -2499,6 +2539,7 @@ static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT ite
 
 static void test_InsertMenu(void)
 {
+    HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
     /* Note: XP treats only bitmap handles 1 - 6 as "magic" ones
      * regardless of their id.
      */
@@ -2514,16 +2555,28 @@ static void test_InsertMenu(void)
         { MF_STRING|MF_HELP, 2, "Help" },
         { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) }
     };
-    static const struct menu_data in2[] =
+    static const struct menu_data out1a[] =
+    {
+        { MF_STRING, 1, "File" },
+        { MF_STRING|MF_HELP, 2, "Help" },
+        { MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) }
+    };
+    const struct menu_data in2[] =
+    {
+        { MF_STRING, 1, "File" },
+        { MF_BITMAP|MF_HELP, SC_CLOSE, (char*)hbm },
+        { MF_STRING|MF_HELP, 2, "Help" }
+    };
+    const struct menu_data out2[] =
     {
         { MF_STRING, 1, "File" },
-        { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
+        { MF_BITMAP|MF_HELP, SC_CLOSE, (char*)hbm },
         { MF_STRING|MF_HELP, 2, "Help" }
     };
-    static const struct menu_data out2[] =
+    const struct menu_data out2a[] =
     {
         { MF_STRING, 1, "File" },
-        { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
+        { MF_HELP, SC_CLOSE, (char*)hbm },
         { MF_STRING|MF_HELP, 2, "Help" }
     };
     static const struct menu_data in3[] =
@@ -2550,9 +2603,16 @@ static void test_InsertMenu(void)
         { MF_STRING|MF_HELP, 2, "Help" },
         { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCE(1) }
     };
+    static const struct menu_data out4a[] =
+    {
+        { MF_STRING, 1, "File" },
+        { MF_STRING|MF_HELP, 2, "Help" },
+        { MF_HELP, 1, MAKEINTRESOURCE(1) }
+    };
     HMENU hmenu;
 
 #define create_menu(a) create_menu_from_data((a), sizeof(a)/sizeof((a)[0]))
+#define create_menuitem(a) create_menuitem_from_data((a), sizeof(a)/sizeof((a)[0]))
 #define compare_menu(h, a) compare_menu_data((h), (a), sizeof(a)/sizeof((a)[0]))
 
     hmenu = create_menu(in1);
@@ -2571,7 +2631,25 @@ static void test_InsertMenu(void)
     compare_menu(hmenu, out4);
     DestroyMenu(hmenu);
 
+    /* now using InsertMenuItemInfo */
+    hmenu = create_menuitem(in1);
+    compare_menu(hmenu, out1a);
+    DestroyMenu(hmenu);
+
+    hmenu = create_menuitem(in2);
+    compare_menu(hmenu, out2a);
+    DestroyMenu(hmenu);
+
+    hmenu = create_menuitem(in3);
+    compare_menu(hmenu, out3);
+    DestroyMenu(hmenu);
+
+    hmenu = create_menuitem(in4);
+    compare_menu(hmenu, out4a);
+    DestroyMenu(hmenu);
+
 #undef create_menu
+#undef create_menuitem
 #undef compare_menu
 }
 
@@ -2932,6 +3010,166 @@ static void test_menu_cancelmode(void)
     DestroyWindow( hwnd);
 }
 
+/* show menu trees have a maximum depth */
+static void test_menu_maxdepth(void)
+{
+#define NR_MENUS 100
+    HMENU hmenus[ NR_MENUS];
+    int i;
+    DWORD ret;
+
+    SetLastError(12345678);
+    for( i = 0; i < NR_MENUS; i++) {
+        hmenus[i] = CreatePopupMenu();
+        if( !hmenus[i]) break;
+    }
+    ok( i == NR_MENUS, "could not create more than %d menu's\n", i);
+    for( i = 1; i < NR_MENUS; i++) {
+        ret = AppendMenuA( hmenus[i], MF_POPUP, (UINT_PTR)hmenus[i-1],"test");
+        if( !ret) break;
+    }
+    trace("Maximum depth is %d\n", i);
+    ok( GetLastError() == 12345678, "unexpected error %d\n",  GetLastError());
+    ok( i < NR_MENUS ||
+           broken( i == NR_MENUS), /* win98, NT */
+           "no ( or very large) limit on menu depth!\n");
+
+    for( i = 0; i < NR_MENUS; i++)
+        DestroyMenu( hmenus[i]);
+}
+
+/* bug #12171 */
+static void test_menu_circref(void)
+{
+    HMENU menu1, menu2;
+    DWORD ret;
+
+    menu1 = CreatePopupMenu();
+    menu2 = CreatePopupMenu();
+    ok( menu1 && menu2, "error creating menus.\n");
+    ret = AppendMenuA( menu1, MF_POPUP, (UINT_PTR)menu2, "winetest");
+    ok( ret, "AppendMenu failed, error is %d\n", GetLastError());
+    ret = AppendMenuA( menu1, MF_STRING | MF_HILITE, 123, "winetest");
+    ok( ret, "AppendMenu failed, error is %d\n", GetLastError());
+    /* app chooses an id that happens to clash with its own hmenu */
+    ret = AppendMenuA( menu2, MF_STRING, (UINT_PTR)menu2, "winetest");
+    ok( ret, "AppendMenu failed, error is %d\n", GetLastError());
+    /* now attempt to change the string of the first item of menu1 */
+    ret = ModifyMenuA( menu1, (UINT_PTR)menu2, MF_POPUP, (UINT_PTR)menu2, "menu 2");
+    ok( !ret ||
+            broken( ret), /* win98, NT */
+            "ModifyMenu should have failed.\n");
+    if( !ret) { /* will probably stack fault if the ModifyMenu succeeded */
+        ret = GetMenuState( menu1, 123, 0);
+        ok( ret == MF_HILITE, "GetMenuState returned %x\n",ret);
+    }
+    DestroyMenu( menu2);
+    DestroyMenu( menu1);
+}
+
+/* test how the menu texts are aligned when the menu items have
+ * different combinations of text and bitmaps (bug #13350) */
+static void test_menualign(void)
+{
+    BYTE bmfill[300];
+    HMENU menu;
+    HBITMAP hbm1, hbm2, hbm3;
+    MENUITEMINFO mii = { sizeof(MENUITEMINFO)};
+    DWORD ret;
+    HWND hwnd;
+    MENUINFO mi = { sizeof( MENUINFO)};
+
+    if( !winetest_interactive) {
+        skip( "interactive alignment tests.\n");
+        return;
+    }
+    hwnd = CreateWindowEx(0,
+            "STATIC",
+            "Menu text alignment Test\nPlease make a selection.",
+            WS_OVERLAPPEDWINDOW,
+            100, 100,
+            300, 300,
+            NULL, NULL, 0, NULL);
+    ShowWindow( hwnd, SW_SHOW);
+    /* create bitmaps */
+    memset( bmfill, 0xcc, sizeof( bmfill));
+    hbm1 = CreateBitmap( 10,10,1,1,bmfill);
+    hbm2 = CreateBitmap( 20,20,1,1,bmfill);
+    hbm3 = CreateBitmap( 50,6,1,1,bmfill);
+    ok( hbm1 && hbm2 && hbm3, "Creating bitmaps failed\n");
+    menu = CreatePopupMenu();
+    ok( menu != NULL, "CreatePopupMenu() failed\n");
+    if( pGetMenuInfo) {
+        mi.fMask = MIM_STYLE;
+        ret = pGetMenuInfo( menu, &mi);
+        ok( menu != NULL, "GetMenuInfo() failed\n");
+        ok( 0 == mi.dwStyle, "menuinfo style is %x\n", mi.dwStyle);
+    }
+    /* test 1 */
+    mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID;
+    mii.wID = 1;
+    mii.hbmpItem = hbm1;
+    mii.dwTypeData = (LPSTR) " OK: menu texts are correctly left-aligned.";
+    ret = InsertMenuItem( menu, -1, TRUE, &mii);
+    ok( ret, "InsertMenuItem() failed\n");
+    mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID ;
+    mii.wID = 2;
+    mii.hbmpItem = hbm2;
+    mii.dwTypeData = (LPSTR) " FAIL: menu texts are NOT left-aligned.";
+    ret = InsertMenuItem( menu, -1, TRUE, &mii);
+    ok( ret, "InsertMenuItem() failed\n");
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 110, 200, 0, hwnd, NULL);
+    ok( ret != 2, "User indicated that menu text alignment test 1 failed %d\n", ret);
+    /* test 2*/
+    mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID;
+    mii.wID = 3;
+    mii.hbmpItem = hbm3;
+    mii.dwTypeData = NULL;
+    ret = InsertMenuItem( menu, 0, TRUE, &mii);
+    ok( ret, "InsertMenuItem() failed\n");
+    mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID;
+    mii.wID = 1;
+    mii.hbmpItem = hbm1;
+    /* make the text a bit longer, to keep it readable */
+    /* this bug is on winXP and reproduced on wine */
+    mii.dwTypeData = (LPSTR) " OK: menu texts are to the right of the bitmaps........";
+    ret = SetMenuItemInfo( menu, 1, TRUE, &mii);
+    ok( ret, "SetMenuItemInfo() failed\n");
+    mii.wID = 2;
+    mii.hbmpItem = hbm2;
+    mii.dwTypeData = (LPSTR) " FAIL: menu texts are below the first bitmap.  ";
+    ret = SetMenuItemInfo( menu, 2, TRUE, &mii);
+    ok( ret, "SetMenuItemInfo() failed\n");
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 110, 200, 0, hwnd, NULL);
+    ok( ret != 2, "User indicated that menu text alignment test 2 failed %d\n", ret);
+    /* test 3 */
+    mii.fMask = MIIM_TYPE | MIIM_ID;
+    mii.wID = 3;
+    mii.fType = MFT_BITMAP;
+    mii.dwTypeData = (LPSTR) hbm3;
+    ret = SetMenuItemInfo( menu, 0, TRUE, &mii);
+    ok( ret, "SetMenuItemInfo() failed\n");
+    mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID;
+    mii.wID = 1;
+    mii.hbmpItem = NULL;
+    mii.dwTypeData = (LPSTR) " OK: menu texts are below the bitmap.";
+    ret = SetMenuItemInfo( menu, 1, TRUE, &mii);
+    ok( ret, "SetMenuItemInfo() failed\n");
+    mii.wID = 2;
+    mii.hbmpItem = NULL;
+    mii.dwTypeData = (LPSTR) " FAIL: menu texts are NOT below the bitmap.";
+    ret = SetMenuItemInfo( menu, 2, TRUE, &mii);
+    ok( ret, "SetMenuItemInfo() failed\n");
+    ret = TrackPopupMenu( menu, TPM_RETURNCMD, 110, 200, 0, hwnd, NULL);
+    ok( ret != 2, "User indicated that menu text alignment test 3 failed %d\n", ret);
+    /* cleanup */
+    DeleteObject( hbm1);
+    DeleteObject( hbm2);
+    DeleteObject( hbm3);
+    DestroyMenu( menu);
+    DestroyWindow( hwnd);
+}
+
 START_TEST(menu)
 {
     init_function_pointers();
@@ -2947,6 +3185,7 @@ START_TEST(menu)
         test_CheckMenuRadioItem();
         test_menu_resource_layout();
         test_InsertMenu();
+        test_menualign();
     }
 
     register_menu_check_class();
@@ -2970,4 +3209,6 @@ START_TEST(menu)
     test_menu_hilitemenuitem();
     test_menu_trackpopupmenu();
     test_menu_cancelmode();
+    test_menu_maxdepth();
+    test_menu_circref();
 }
index 580582f..30acaa2 100755 (executable)
@@ -581,7 +581,7 @@ static const struct message WmShowMinOverlappedSeq[] = {
     { WM_GETTEXT, sent|defwinproc|optional },
     { WM_WINDOWPOSCHANGED, sent },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MINIMIZED },
+    { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { WM_NCCALCSIZE, sent|optional },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
@@ -1004,7 +1004,7 @@ static const struct message WmShowChildInvisibleParentSeq_1[] = {
     { WM_CHILDACTIVATE, sent|optional },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOREDRAW|SWP_NOCOPYBITS|SWP_STATECHANGED, 0, SWP_NOACTIVATE },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MINIMIZED },
+    { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
     { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, 0, 0 },
     /* FIXME: Wine creates an icon/title window while Windows doesn't */
@@ -1044,7 +1044,7 @@ static const struct message WmShowChildInvisibleParentSeq_3[] = {
     { WM_CHILDACTIVATE, sent },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOREDRAW|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MINIMIZED },
+    { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
     { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, 0, 0 },
     /* FIXME: Wine creates an icon/title window while Windows doesn't */
@@ -1065,7 +1065,7 @@ static const struct message WmShowChildInvisibleParentSeq_4[] = {
     { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOREDRAW|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MINIMIZED },
+    { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
     { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, 0, 0 },
     /* FIXME: Wine creates an icon/title window while Windows doesn't */
@@ -3186,7 +3186,7 @@ static const struct message WmMinimizeMDIchildVisibleSeq[] = {
     { WM_NCCALCSIZE, sent|wparam, 1 },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOCLIENTSIZE|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MINIMIZED },
+    { WM_SIZE, sent|defwinproc|wparam|lparam, SIZE_MINIMIZED, 0 },
     { WM_CHILDACTIVATE, sent|wparam|lparam|defwinproc, 0, 0 },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
     { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
@@ -5209,6 +5209,49 @@ static const struct message WmSetFontButtonSeq[] =
     { WM_CTLCOLORBTN, sent|defwinproc },
     { 0 }
 };
+static const struct message WmSetStyleButtonSeq[] =
+{
+    { BM_SETSTYLE, sent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
+    { WM_ERASEBKGND, sent|defwinproc|optional }, /* Win9x doesn't send it */
+    { WM_CTLCOLORBTN, sent|parent },
+    { 0 }
+};
+static const struct message WmSetStyleStaticSeq[] =
+{
+    { BM_SETSTYLE, sent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
+    { WM_ERASEBKGND, sent|defwinproc|optional }, /* Win9x doesn't send it */
+    { WM_CTLCOLORSTATIC, sent|parent },
+    { 0 }
+};
+static const struct message WmSetStyleUserSeq[] =
+{
+    { BM_SETSTYLE, sent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
+    { WM_ERASEBKGND, sent|defwinproc|optional }, /* Win9x doesn't send it */
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_PAINT) },
+    { 0 }
+};
+static const struct message WmSetStyleOwnerdrawSeq[] =
+{
+    { BM_SETSTYLE, sent },
+    { WM_APP, sent|wparam|lparam, 0, 0 },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
+    { WM_ERASEBKGND, sent|defwinproc|optional }, /* Win9x doesn't send it */
+    { WM_CTLCOLORBTN, sent|parent },
+    { WM_CTLCOLORBTN, sent|parent|optional }, /* Win9x doesn't send it */
+    { WM_DRAWITEM, sent|wparam|lparam|parent, ID_BUTTON, 0x000010e4 },
+    { 0 }
+};
 
 static WNDPROC old_button_proc;
 
@@ -5268,29 +5311,30 @@ static void test_button_messages(void)
        DWORD dlg_code;
        const struct message *setfocus;
        const struct message *killfocus;
+       const struct message *setstyle;
     } button[] = {
        { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq },
        { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleButtonSeq },
        { BS_CHECKBOX, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_AUTOCHECKBOX, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_3STATE, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_AUTO3STATE, DLGC_BUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_GROUPBOX, DLGC_STATIC,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
-         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq, WmSetStyleUserSeq },
        { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
-         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq, WmSetStyleStaticSeq },
        { BS_OWNERDRAW, DLGC_BUTTON,
-         WmSetFocusOwnerdrawSeq, WmKillFocusOwnerdrawSeq }
+         WmSetFocusOwnerdrawSeq, WmKillFocusOwnerdrawSeq, WmSetStyleOwnerdrawSeq }
     };
     unsigned int i;
     HWND hwnd, parent;
@@ -5319,6 +5363,8 @@ static void test_button_messages(void)
         MSG msg;
         DWORD style;
 
+        trace("button style %08x\n", button[i].style);
+
         hwnd = CreateWindowExA(0, "my_button_class", "test", button[i].style | WS_CHILD | BS_NOTIFY,
                                0, 0, 50, 14, parent, (HMENU)ID_BUTTON, 0, NULL);
        ok(hwnd != 0, "Failed to create button window\n");
@@ -5327,9 +5373,9 @@ static void test_button_messages(void)
         style &= ~(WS_CHILD | BS_NOTIFY);
         /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
         if (button[i].style == BS_USERBUTTON)
-            todo_wine ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
+            ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
         else
-        ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
+            ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
 
        dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
        ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
@@ -5342,7 +5388,6 @@ static void test_button_messages(void)
 
         log_all_parent_messages++;
 
-       trace("button style %08x\n", button[i].style);
         ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
        SetFocus(hwnd);
         SendMessage(hwnd, WM_APP, 0, 0); /* place a separator mark here */
@@ -5354,9 +5399,20 @@ static void test_button_messages(void)
         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
        ok_sequence(button[i].killfocus, "SetFocus(0) on a button", FALSE);
 
+        ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
+
+        SendMessage(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
+        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].setstyle, "BM_SETSTYLE on a button", FALSE);
+
+        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);
+
         log_all_parent_messages--;
 
-        ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
        DestroyWindow(hwnd);
     }
 
@@ -10198,7 +10254,7 @@ static const struct message WmShowMinimized_1[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* win2000 doesn't send it */
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|wparam|defwinproc, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmMinimize_1[] = {
@@ -10208,7 +10264,7 @@ static const struct message WmMinimize_1[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|wparam|defwinproc, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmMinimize_2[] = {
@@ -10217,7 +10273,7 @@ static const struct message WmMinimize_2[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|wparam|defwinproc, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmMinimize_3[] = {
@@ -10225,7 +10281,7 @@ static const struct message WmMinimize_3[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|wparam|defwinproc, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmShowMinNoActivate[] = {
@@ -10233,7 +10289,7 @@ static const struct message WmShowMinNoActivate[] = {
     { WM_WINDOWPOSCHANGING, sent },
     { WM_WINDOWPOSCHANGED, sent },
     { WM_MOVE, sent|defwinproc|optional },
-    { WM_SIZE, sent|wparam|defwinproc|optional, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc|optional, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmMinMax_1[] = {
@@ -10259,7 +10315,7 @@ static const struct message WmMinMax_3[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
     { WM_MOVE, sent|defwinproc|optional },
-    { WM_SIZE, sent|wparam|defwinproc|optional, SIZE_MINIMIZED },
+    { WM_SIZE, sent|wparam|lparam|defwinproc|optional, SIZE_MINIMIZED, 0 },
     { 0 }
 };
 static const struct message WmMinMax_4[] = {
index d45eba3..cf1de25 100644 (file)
@@ -4859,6 +4859,75 @@ static void test_Expose(void)
     DestroyWindow(mw);
 }
 
+static LRESULT CALLBACK TestNCRedraw_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    static UINT ncredrawflags;
+    PAINTSTRUCT ps;
+
+    switch(msg)
+    {
+    case WM_CREATE:
+        ncredrawflags = *(UINT *) (((CREATESTRUCT *)lParam)->lpCreateParams);
+        return 0;
+    case WM_NCPAINT:
+        RedrawWindow(hwnd, NULL, NULL, ncredrawflags);
+        break;
+    case WM_PAINT:
+        BeginPaint(hwnd, &ps);
+        EndPaint(hwnd, &ps);
+        return 0;
+    }
+    return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static void run_NCRedrawLoop(UINT flags)
+{
+    HWND hwnd;
+    MSG msg;
+
+    UINT loopcount = 0;
+
+    hwnd = CreateWindowA("TestNCRedrawClass", "MainWindow",
+                         WS_OVERLAPPEDWINDOW, 0, 0, 200, 100,
+                         NULL, NULL, 0, &flags);
+    ShowWindow(hwnd, SW_SHOW);
+    UpdateWindow(hwnd);
+    while(PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) != 0)
+    {
+        if (msg.message == WM_PAINT) loopcount++;
+        if (loopcount >= 100) break;
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+        MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT);
+    }
+    if (flags == (RDW_INVALIDATE | RDW_FRAME))
+        todo_wine ok(loopcount < 100, "Detected infinite WM_PAINT loop (%x).\n", flags);
+    else
+        ok(loopcount < 100, "Detected infinite WM_PAINT loop (%x).\n", flags);
+    DestroyWindow(hwnd);
+}
+
+static void test_NCRedraw(void)
+{
+    WNDCLASSA wndclass;
+
+    wndclass.lpszClassName = "TestNCRedrawClass";
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = TestNCRedraw_WndProc;
+    wndclass.cbClsExtra = 0;
+    wndclass.cbWndExtra = 0;
+    wndclass.hInstance = 0;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
+    wndclass.lpszMenuName = NULL;
+
+    RegisterClassA(&wndclass);
+
+    run_NCRedrawLoop(RDW_INVALIDATE | RDW_FRAME);
+    run_NCRedrawLoop(RDW_INVALIDATE);
+}
+
 static void test_GetWindowModuleFileName(void)
 {
     HWND hwnd;
@@ -5647,6 +5716,7 @@ START_TEST(win)
     test_SetMenu(hwndMain);
     test_SetFocus(hwndMain);
     test_SetActiveWindow(hwndMain);
+    test_NCRedraw();
 
     test_children_zorder(hwndMain);
     test_popup_zorder(hwndMain2, hwndMain);