| /* | |
| * Unit test suite for cursors and icons. | |
| * | |
| * Copyright 2006 Michael Kaufmann | |
| * Copyright 2007 Dmitry Timoshkov | |
| * Copyright 2007-2008 Andrew Riedi | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include "wine/test.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "pshpack1.h" | |
| typedef struct | |
| { | |
| BYTE bWidth; | |
| BYTE bHeight; | |
| BYTE bColorCount; | |
| BYTE bReserved; | |
| WORD xHotspot; | |
| WORD yHotspot; | |
| DWORD dwDIBSize; | |
| DWORD dwDIBOffset; | |
| } CURSORICONFILEDIRENTRY; | |
| typedef struct | |
| { | |
| WORD idReserved; | |
| WORD idType; | |
| WORD idCount; | |
| CURSORICONFILEDIRENTRY idEntries[1]; | |
| } CURSORICONFILEDIR; | |
| #define RIFF_FOURCC( c0, c1, c2, c3 ) \ | |
| ( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \ | |
| ( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) ) | |
| #define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F') | |
| #define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T') | |
| #define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N') | |
| #define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h') | |
| #define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ') | |
| #define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm') | |
| #define ANI_icon_ID RIFF_FOURCC('i', 'c', 'o', 'n') | |
| #define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e') | |
| #define ANI_FLAG_ICON 0x1 | |
| #define ANI_FLAG_SEQUENCE 0x2 | |
| typedef struct { | |
| DWORD header_size; | |
| DWORD num_frames; | |
| DWORD num_steps; | |
| DWORD width; | |
| DWORD height; | |
| DWORD bpp; | |
| DWORD num_planes; | |
| DWORD display_rate; | |
| DWORD flags; | |
| } ani_header; | |
| typedef struct { | |
| BYTE data[32*32*4]; | |
| BYTE mask_data[32*32/8]; | |
| } ani_data32x32x32; | |
| typedef struct { | |
| CURSORICONFILEDIR icon_info; /* animated cursor frame information */ | |
| BITMAPINFOHEADER bmi_header; /* animated cursor frame header */ | |
| ani_data32x32x32 bmi_data; /* animated cursor frame DIB data */ | |
| } ani_frame32x32x32; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_anih_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| ani_header header; /* animated cursor header */ | |
| } riff_header_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_LIST_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD chunk_type; /* ANI_fram_ID */ | |
| } riff_list_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_icon_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| ani_frame32x32x32 data; /* animated cursor frame */ | |
| } riff_icon32x32x32_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_RIFF_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD chunk_type; /* ANI_ACON_ID */ | |
| riff_header_t header; /* RIFF animated cursor header */ | |
| riff_list_t frame_list; /* RIFF animated cursor frame list info */ | |
| riff_icon32x32x32_t frames[1]; /* array of animated cursor frames */ | |
| } riff_cursor1_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_RIFF_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD chunk_type; /* ANI_ACON_ID */ | |
| riff_header_t header; /* RIFF animated cursor header */ | |
| riff_list_t frame_list; /* RIFF animated cursor frame list info */ | |
| riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ | |
| } riff_cursor3_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_rate_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD rate[3]; /* animated cursor rate data */ | |
| } riff_rate3_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_seq__ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD order[3]; /* animated cursor sequence data */ | |
| } riff_seq3_t; | |
| typedef struct { | |
| DWORD chunk_id; /* ANI_RIFF_ID */ | |
| DWORD chunk_size; /* actual size of data */ | |
| DWORD chunk_type; /* ANI_ACON_ID */ | |
| riff_header_t header; /* RIFF animated cursor header */ | |
| riff_seq3_t seq; /* sequence data for three cursor frames */ | |
| riff_rate3_t rates; /* rate data for three cursor frames */ | |
| riff_list_t frame_list; /* RIFF animated cursor frame list info */ | |
| riff_icon32x32x32_t frames[3]; /* array of three animated cursor frames */ | |
| } riff_cursor3_seq_t; | |
| #define EMPTY_ICON32 \ | |
| { \ | |
| ANI_icon_ID, \ | |
| sizeof(ani_frame32x32x32), \ | |
| { \ | |
| { \ | |
| 0x0, /* reserved */ \ | |
| 0, /* type: icon(1), cursor(2) */ \ | |
| 1, /* count */ \ | |
| { \ | |
| { \ | |
| 32, /* width */ \ | |
| 32, /* height */ \ | |
| 0, /* color count */ \ | |
| 0x0, /* reserved */ \ | |
| 16, /* x hotspot */ \ | |
| 16, /* y hotspot */ \ | |
| sizeof(ani_data32x32x32), /* DIB size */ \ | |
| sizeof(CURSORICONFILEDIR) /* DIB offset */ \ | |
| } \ | |
| } \ | |
| }, \ | |
| { \ | |
| sizeof(BITMAPINFOHEADER), /* structure for DIB-type data */ \ | |
| 32, /* width */ \ | |
| 32*2, /* actual height times two */ \ | |
| 1, /* planes */ \ | |
| 32, /* bpp */ \ | |
| BI_RGB, /* compression */ \ | |
| 0, /* image size */ \ | |
| 0, /* biXPelsPerMeter */ \ | |
| 0, /* biYPelsPerMeter */ \ | |
| 0, /* biClrUsed */ \ | |
| 0 /* biClrImportant */ \ | |
| } \ | |
| /* DIB data: left uninitialized */ \ | |
| } \ | |
| } | |
| riff_cursor1_t empty_anicursor = { | |
| ANI_RIFF_ID, | |
| sizeof(empty_anicursor) - sizeof(DWORD)*2, | |
| ANI_ACON_ID, | |
| { | |
| ANI_anih_ID, | |
| sizeof(ani_header), | |
| { | |
| sizeof(ani_header), | |
| 1, /* frames */ | |
| 1, /* steps */ | |
| 32, /* width */ | |
| 32, /* height */ | |
| 32, /* depth */ | |
| 1, /* planes */ | |
| 10, /* display rate in jiffies */ | |
| ANI_FLAG_ICON /* flags */ | |
| } | |
| }, | |
| { | |
| ANI_LIST_ID, | |
| sizeof(riff_icon32x32x32_t)*(1 /*frames*/) + sizeof(DWORD), | |
| ANI_fram_ID, | |
| }, | |
| { | |
| EMPTY_ICON32 | |
| } | |
| }; | |
| riff_cursor3_t empty_anicursor3 = { | |
| ANI_RIFF_ID, | |
| sizeof(empty_anicursor3) - sizeof(DWORD)*2, | |
| ANI_ACON_ID, | |
| { | |
| ANI_anih_ID, | |
| sizeof(ani_header), | |
| { | |
| sizeof(ani_header), | |
| 3, /* frames */ | |
| 3, /* steps */ | |
| 32, /* width */ | |
| 32, /* height */ | |
| 32, /* depth */ | |
| 1, /* planes */ | |
| 0xbeef, /* display rate in jiffies */ | |
| ANI_FLAG_ICON /* flags */ | |
| } | |
| }, | |
| { | |
| ANI_LIST_ID, | |
| sizeof(riff_icon32x32x32_t)*(3 /*frames*/) + sizeof(DWORD), | |
| ANI_fram_ID, | |
| }, | |
| { | |
| EMPTY_ICON32, | |
| EMPTY_ICON32, | |
| EMPTY_ICON32 | |
| } | |
| }; | |
| riff_cursor3_seq_t empty_anicursor3_seq = { | |
| ANI_RIFF_ID, | |
| sizeof(empty_anicursor3_seq) - sizeof(DWORD)*2, | |
| ANI_ACON_ID, | |
| { | |
| ANI_anih_ID, | |
| sizeof(ani_header), | |
| { | |
| sizeof(ani_header), | |
| 3, /* frames */ | |
| 3, /* steps */ | |
| 32, /* width */ | |
| 32, /* height */ | |
| 32, /* depth */ | |
| 1, /* planes */ | |
| 0xbeef, /* display rate in jiffies */ | |
| ANI_FLAG_ICON|ANI_FLAG_SEQUENCE /* flags */ | |
| } | |
| }, | |
| { | |
| ANI_seq__ID, | |
| sizeof(riff_seq3_t) - sizeof(DWORD)*2, | |
| { 2, 0, 1} /* show frames in a uniquely identifiable order */ | |
| }, | |
| { | |
| ANI_rate_ID, | |
| sizeof(riff_rate3_t) - sizeof(DWORD)*2, | |
| { 0xc0de, 0xcafe, 0xbabe} | |
| }, | |
| { | |
| ANI_LIST_ID, | |
| sizeof(riff_icon32x32x32_t)*(3 /*frames*/) + sizeof(DWORD), | |
| ANI_fram_ID, | |
| }, | |
| { | |
| EMPTY_ICON32, | |
| EMPTY_ICON32, | |
| EMPTY_ICON32 | |
| } | |
| }; | |
| #include "poppack.h" | |
| static char **test_argv; | |
| static int test_argc; | |
| static HWND child = 0; | |
| static HWND parent = 0; | |
| static HANDLE child_process; | |
| #define PROC_INIT (WM_USER+1) | |
| static BOOL (WINAPI *pGetCursorInfo)(CURSORINFO *); | |
| static BOOL (WINAPI *pGetIconInfoExA)(HICON,ICONINFOEXA *); | |
| static BOOL (WINAPI *pGetIconInfoExW)(HICON,ICONINFOEXW *); | |
| static const BOOL is_win64 = (sizeof(void *) > sizeof(int)); | |
| static LRESULT CALLBACK callback_child(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| switch (msg) | |
| { | |
| /* Destroy the cursor. */ | |
| case WM_USER+1: | |
| { | |
| HCURSOR cursor = (HCURSOR)lParam; | |
| ICONINFO info; | |
| BOOL ret; | |
| DWORD error; | |
| memset(&info, 0, sizeof(info)); | |
| ret = GetIconInfo(cursor, &info); | |
| todo_wine ok(ret, "GetIconInfoEx failed with error %u\n", GetLastError()); | |
| todo_wine ok(info.hbmColor != NULL, "info.hmbColor was not set\n"); | |
| todo_wine ok(info.hbmMask != NULL, "info.hmbColor was not set\n"); | |
| DeleteObject(info.hbmColor); | |
| DeleteObject(info.hbmMask); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(cursor); | |
| error = GetLastError(); | |
| ok(!ret || broken(ret) /* win9x */, "DestroyCursor on the active cursor succeeded.\n"); | |
| ok(error == ERROR_DESTROY_OBJECT_OF_OTHER_THREAD || | |
| error == 0xdeadbeef, /* vista */ | |
| "Last error: %u\n", error); | |
| return TRUE; | |
| } | |
| case WM_DESTROY: | |
| PostQuitMessage(0); | |
| return 0; | |
| } | |
| return DefWindowProcA(hwnd, msg, wParam, lParam); | |
| } | |
| static LRESULT CALLBACK callback_parent(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| if (msg == PROC_INIT) | |
| { | |
| child = (HWND) wParam; | |
| return TRUE; | |
| } | |
| return DefWindowProcA(hwnd, msg, wParam, lParam); | |
| } | |
| static void do_child(void) | |
| { | |
| WNDCLASSA class; | |
| MSG msg; | |
| BOOL ret; | |
| /* Register a new class. */ | |
| class.style = CS_GLOBALCLASS; | |
| class.lpfnWndProc = callback_child; | |
| class.cbClsExtra = 0; | |
| class.cbWndExtra = 0; | |
| class.hInstance = GetModuleHandleA(NULL); | |
| class.hIcon = NULL; | |
| class.hCursor = NULL; | |
| class.hbrBackground = NULL; | |
| class.lpszMenuName = NULL; | |
| class.lpszClassName = "cursor_child"; | |
| SetLastError(0xdeadbeef); | |
| ret = RegisterClassA(&class); | |
| ok(ret, "Failed to register window class. Error: %u\n", GetLastError()); | |
| /* Create a window. */ | |
| child = CreateWindowA("cursor_child", "cursor_child", WS_POPUP | WS_VISIBLE, | |
| 0, 0, 200, 200, 0, 0, 0, NULL); | |
| ok(child != 0, "CreateWindowA failed. Error: %u\n", GetLastError()); | |
| /* Let the parent know our HWND. */ | |
| PostMessageA(parent, PROC_INIT, (WPARAM) child, 0); | |
| /* Receive messages. */ | |
| while ((ret = GetMessageA(&msg, 0, 0, 0))) | |
| { | |
| ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); | |
| TranslateMessage(&msg); | |
| DispatchMessageA(&msg); | |
| } | |
| } | |
| static void do_parent(void) | |
| { | |
| char path_name[MAX_PATH]; | |
| PROCESS_INFORMATION info; | |
| STARTUPINFOA startup; | |
| WNDCLASSA class; | |
| MSG msg; | |
| BOOL ret; | |
| /* Register a new class. */ | |
| class.style = CS_GLOBALCLASS; | |
| class.lpfnWndProc = callback_parent; | |
| class.cbClsExtra = 0; | |
| class.cbWndExtra = 0; | |
| class.hInstance = GetModuleHandleA(NULL); | |
| class.hIcon = NULL; | |
| class.hCursor = NULL; | |
| class.hbrBackground = NULL; | |
| class.lpszMenuName = NULL; | |
| class.lpszClassName = "cursor_parent"; | |
| SetLastError(0xdeadbeef); | |
| ret = RegisterClassA(&class); | |
| ok(ret, "Failed to register window class. Error: %u\n", GetLastError()); | |
| /* Create a window. */ | |
| parent = CreateWindowA("cursor_parent", "cursor_parent", WS_POPUP | WS_VISIBLE, | |
| 0, 0, 200, 200, 0, 0, 0, NULL); | |
| ok(parent != 0, "CreateWindowA failed. Error: %u\n", GetLastError()); | |
| /* Start child process. */ | |
| memset(&startup, 0, sizeof(startup)); | |
| startup.cb = sizeof(startup); | |
| startup.dwFlags = STARTF_USESHOWWINDOW; | |
| startup.wShowWindow = SW_SHOWNORMAL; | |
| sprintf(path_name, "%s cursoricon %lx", test_argv[0], (INT_PTR)parent); | |
| ok(CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess failed.\n"); | |
| child_process = info.hProcess; | |
| /* Wait for child window handle. */ | |
| while ((child == 0) && (ret = GetMessageA(&msg, parent, 0, 0))) | |
| { | |
| ok(ret != -1, "GetMessage failed. Error: %u\n", GetLastError()); | |
| TranslateMessage(&msg); | |
| DispatchMessageA(&msg); | |
| } | |
| } | |
| static void finish_child_process(void) | |
| { | |
| SendMessageA(child, WM_CLOSE, 0, 0); | |
| winetest_wait_child_process( child_process ); | |
| CloseHandle(child_process); | |
| } | |
| static void test_child_process(void) | |
| { | |
| static const BYTE bmp_bits[4096]; | |
| HCURSOR cursor; | |
| ICONINFO cursorInfo; | |
| UINT display_bpp; | |
| HDC hdc; | |
| /* Create and set a dummy cursor. */ | |
| hdc = GetDC(0); | |
| display_bpp = GetDeviceCaps(hdc, BITSPIXEL); | |
| ReleaseDC(0, hdc); | |
| cursorInfo.fIcon = FALSE; | |
| cursorInfo.xHotspot = 0; | |
| cursorInfo.yHotspot = 0; | |
| cursorInfo.hbmMask = CreateBitmap(32, 32, 1, 1, bmp_bits); | |
| cursorInfo.hbmColor = CreateBitmap(32, 32, 1, display_bpp, bmp_bits); | |
| cursor = CreateIconIndirect(&cursorInfo); | |
| ok(cursor != NULL, "CreateIconIndirect returned %p.\n", cursor); | |
| SetCursor(cursor); | |
| /* Destroy the cursor. */ | |
| SendMessageA(child, WM_USER+1, 0, (LPARAM) cursor); | |
| } | |
| static BOOL color_match(COLORREF a, COLORREF b) | |
| { | |
| /* 5-bit accuracy is a sufficient test. This will match as long as | |
| * colors are never truncated to less that 3x5-bit accuracy i.e. | |
| * palettized. */ | |
| return (a & 0x00F8F8F8) == (b & 0x00F8F8F8); | |
| } | |
| static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, INT copyWidth, INT copyHeight, | |
| INT expectedWidth, INT expectedHeight, WORD expectedDepth, BOOL dibExpected) | |
| { | |
| HBITMAP copy; | |
| BITMAP origBitmap; | |
| BITMAP copyBitmap; | |
| BOOL orig_is_dib; | |
| BOOL copy_is_dib; | |
| copy = CopyImage(bitmap, IMAGE_BITMAP, copyWidth, copyHeight, flags); | |
| ok(copy != NULL, "CopyImage() failed\n"); | |
| if (copy != NULL) | |
| { | |
| GetObjectA(bitmap, sizeof(origBitmap), &origBitmap); | |
| GetObjectA(copy, sizeof(copyBitmap), ©Bitmap); | |
| orig_is_dib = (origBitmap.bmBits != NULL); | |
| copy_is_dib = (copyBitmap.bmBits != NULL); | |
| if (copy_is_dib && dibExpected | |
| && copyBitmap.bmBitsPixel == 24 | |
| && (expectedDepth == 16 || expectedDepth == 32)) | |
| { | |
| /* Windows 95 doesn't create DIBs with a depth of 16 or 32 bit */ | |
| if (GetVersion() & 0x80000000) | |
| { | |
| expectedDepth = 24; | |
| } | |
| } | |
| if (copy_is_dib && !dibExpected && !(flags & LR_CREATEDIBSECTION)) | |
| { | |
| /* It's not forbidden to create a DIB section if the flag | |
| LR_CREATEDIBSECTION is absent. | |
| Windows 9x does this if the bitmap has a depth that doesn't | |
| match the screen depth, Windows NT doesn't */ | |
| dibExpected = TRUE; | |
| expectedDepth = origBitmap.bmBitsPixel; | |
| } | |
| ok((!(dibExpected ^ copy_is_dib) | |
| && (copyBitmap.bmWidth == expectedWidth) | |
| && (copyBitmap.bmHeight == expectedHeight) | |
| && (copyBitmap.bmBitsPixel == expectedDepth)), | |
| "CopyImage ((%s, %dx%d, %u bpp), %d, %d, %#x): Expected (%s, %dx%d, %u bpp), got (%s, %dx%d, %u bpp)\n", | |
| orig_is_dib ? "DIB" : "DDB", origBitmap.bmWidth, origBitmap.bmHeight, origBitmap.bmBitsPixel, | |
| copyWidth, copyHeight, flags, | |
| dibExpected ? "DIB" : "DDB", expectedWidth, expectedHeight, expectedDepth, | |
| copy_is_dib ? "DIB" : "DDB", copyBitmap.bmWidth, copyBitmap.bmHeight, copyBitmap.bmBitsPixel); | |
| DeleteObject(copy); | |
| } | |
| } | |
| static void test_CopyImage_Bitmap(int depth) | |
| { | |
| HBITMAP ddb, dib; | |
| HDC screenDC; | |
| BITMAPINFO * info; | |
| VOID * bits; | |
| int screen_depth; | |
| unsigned int i; | |
| /* Create a device-independent bitmap (DIB) */ | |
| info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); | |
| info->bmiHeader.biSize = sizeof(info->bmiHeader); | |
| info->bmiHeader.biWidth = 2; | |
| info->bmiHeader.biHeight = 2; | |
| info->bmiHeader.biPlanes = 1; | |
| info->bmiHeader.biBitCount = depth; | |
| info->bmiHeader.biCompression = BI_RGB; | |
| for (i=0; i < 256; i++) | |
| { | |
| info->bmiColors[i].rgbRed = i; | |
| info->bmiColors[i].rgbGreen = i; | |
| info->bmiColors[i].rgbBlue = 255 - i; | |
| info->bmiColors[i].rgbReserved = 0; | |
| } | |
| dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); | |
| /* Create a device-dependent bitmap (DDB) */ | |
| screenDC = GetDC(NULL); | |
| screen_depth = GetDeviceCaps(screenDC, BITSPIXEL); | |
| if (depth == 1 || depth == screen_depth) | |
| { | |
| ddb = CreateBitmap(2, 2, 1, depth, NULL); | |
| } | |
| else | |
| { | |
| ddb = NULL; | |
| } | |
| ReleaseDC(NULL, screenDC); | |
| if (ddb != NULL) | |
| { | |
| test_CopyImage_Check(ddb, 0, 0, 0, 2, 2, depth == 1 ? 1 : screen_depth, FALSE); | |
| test_CopyImage_Check(ddb, 0, 0, 5, 2, 5, depth == 1 ? 1 : screen_depth, FALSE); | |
| test_CopyImage_Check(ddb, 0, 5, 0, 5, 2, depth == 1 ? 1 : screen_depth, FALSE); | |
| test_CopyImage_Check(ddb, 0, 5, 5, 5, 5, depth == 1 ? 1 : screen_depth, FALSE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); | |
| test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); | |
| /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ | |
| test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); | |
| test_CopyImage_Check(ddb, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); | |
| DeleteObject(ddb); | |
| } | |
| if (depth != 1) | |
| { | |
| test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE); | |
| } | |
| test_CopyImage_Check(dib, LR_MONOCHROME, 0, 0, 2, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME, 5, 0, 5, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME, 0, 5, 2, 5, 1, FALSE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME, 5, 5, 5, 5, 1, FALSE); | |
| test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); | |
| /* LR_MONOCHROME is ignored if LR_CREATEDIBSECTION is present */ | |
| test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 0, 2, 2, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 0, 5, 2, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 0, 5, 2, 5, depth, TRUE); | |
| test_CopyImage_Check(dib, LR_MONOCHROME | LR_CREATEDIBSECTION, 5, 5, 5, 5, depth, TRUE); | |
| DeleteObject(dib); | |
| if (depth == 1) | |
| { | |
| /* Special case: A monochrome DIB is converted to a monochrome DDB if | |
| the colors in the color table are black and white. | |
| Skip this test on Windows 95, it always creates a monochrome DDB | |
| in this case */ | |
| if (!(GetVersion() & 0x80000000)) | |
| { | |
| info->bmiHeader.biBitCount = 1; | |
| info->bmiColors[0].rgbRed = 0xFF; | |
| info->bmiColors[0].rgbGreen = 0; | |
| info->bmiColors[0].rgbBlue = 0; | |
| info->bmiColors[1].rgbRed = 0; | |
| info->bmiColors[1].rgbGreen = 0xFF; | |
| info->bmiColors[1].rgbBlue = 0; | |
| dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); | |
| test_CopyImage_Check(dib, 0, 0, 0, 2, 2, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 0, 5, 2, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 0, 5, 2, 5, screen_depth, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 5, 5, 5, screen_depth, FALSE); | |
| DeleteObject(dib); | |
| info->bmiHeader.biBitCount = 1; | |
| info->bmiColors[0].rgbRed = 0; | |
| info->bmiColors[0].rgbGreen = 0; | |
| info->bmiColors[0].rgbBlue = 0; | |
| info->bmiColors[1].rgbRed = 0xFF; | |
| info->bmiColors[1].rgbGreen = 0xFF; | |
| info->bmiColors[1].rgbBlue = 0xFF; | |
| dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); | |
| test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE); | |
| DeleteObject(dib); | |
| info->bmiHeader.biBitCount = 1; | |
| info->bmiColors[0].rgbRed = 0xFF; | |
| info->bmiColors[0].rgbGreen = 0xFF; | |
| info->bmiColors[0].rgbBlue = 0xFF; | |
| info->bmiColors[1].rgbRed = 0; | |
| info->bmiColors[1].rgbGreen = 0; | |
| info->bmiColors[1].rgbBlue = 0; | |
| dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0); | |
| test_CopyImage_Check(dib, 0, 0, 0, 2, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 0, 5, 2, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 0, 5, 2, 5, 1, FALSE); | |
| test_CopyImage_Check(dib, 0, 5, 5, 5, 5, 1, FALSE); | |
| DeleteObject(dib); | |
| } | |
| } | |
| HeapFree(GetProcessHeap(), 0, info); | |
| } | |
| static void test_initial_cursor(void) | |
| { | |
| HCURSOR cursor, cursor2; | |
| DWORD error; | |
| cursor = GetCursor(); | |
| /* Check what handle GetCursor() returns if a cursor is not set yet. */ | |
| SetLastError(0xdeadbeef); | |
| cursor2 = LoadCursorA(NULL, (LPCSTR)IDC_WAIT); | |
| todo_wine { | |
| ok(cursor == cursor2, "cursor (%p) is not IDC_WAIT (%p).\n", cursor, cursor2); | |
| } | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); | |
| } | |
| static void test_icon_info_dbg(HICON hIcon, UINT exp_cx, UINT exp_cy, UINT exp_mask_cy, UINT exp_bpp, int line) | |
| { | |
| ICONINFO info; | |
| DWORD ret; | |
| BITMAP bmMask, bmColor; | |
| ret = GetIconInfo(hIcon, &info); | |
| ok_(__FILE__, line)(ret, "GetIconInfo failed\n"); | |
| /* CreateIcon under XP causes info.fIcon to be 0 */ | |
| ok_(__FILE__, line)(info.xHotspot == exp_cx/2, "info.xHotspot = %u\n", info.xHotspot); | |
| ok_(__FILE__, line)(info.yHotspot == exp_cy/2, "info.yHotspot = %u\n", info.yHotspot); | |
| ok_(__FILE__, line)(info.hbmMask != 0, "info.hbmMask is NULL\n"); | |
| ret = GetObjectA(info.hbmMask, sizeof(bmMask), &bmMask); | |
| ok_(__FILE__, line)(ret == sizeof(bmMask), "GetObject(info.hbmMask) failed, ret %u\n", ret); | |
| if (exp_bpp == 1) | |
| ok_(__FILE__, line)(info.hbmColor == 0, "info.hbmColor should be NULL\n"); | |
| if (info.hbmColor) | |
| { | |
| HDC hdc; | |
| UINT display_bpp; | |
| hdc = GetDC(0); | |
| display_bpp = GetDeviceCaps(hdc, BITSPIXEL); | |
| ReleaseDC(0, hdc); | |
| ret = GetObjectA(info.hbmColor, sizeof(bmColor), &bmColor); | |
| ok_(__FILE__, line)(ret == sizeof(bmColor), "GetObject(info.hbmColor) failed, ret %u\n", ret); | |
| ok_(__FILE__, line)(bmColor.bmBitsPixel == display_bpp /* XP */ || | |
| bmColor.bmBitsPixel == exp_bpp /* Win98 */, | |
| "bmColor.bmBitsPixel = %d\n", bmColor.bmBitsPixel); | |
| ok_(__FILE__, line)(bmColor.bmWidth == exp_cx, "bmColor.bmWidth = %d\n", bmColor.bmWidth); | |
| ok_(__FILE__, line)(bmColor.bmHeight == exp_cy, "bmColor.bmHeight = %d\n", bmColor.bmHeight); | |
| ok_(__FILE__, line)(bmMask.bmBitsPixel == 1, "bmMask.bmBitsPixel = %d\n", bmMask.bmBitsPixel); | |
| ok_(__FILE__, line)(bmMask.bmWidth == exp_cx, "bmMask.bmWidth = %d\n", bmMask.bmWidth); | |
| ok_(__FILE__, line)(bmMask.bmHeight == exp_mask_cy, "bmMask.bmHeight = %d\n", bmMask.bmHeight); | |
| } | |
| else | |
| { | |
| ok_(__FILE__, line)(bmMask.bmBitsPixel == 1, "bmMask.bmBitsPixel = %d\n", bmMask.bmBitsPixel); | |
| ok_(__FILE__, line)(bmMask.bmWidth == exp_cx, "bmMask.bmWidth = %d\n", bmMask.bmWidth); | |
| ok_(__FILE__, line)(bmMask.bmHeight == exp_mask_cy, "bmMask.bmHeight = %d\n", bmMask.bmHeight); | |
| } | |
| if (pGetIconInfoExA) | |
| { | |
| ICONINFOEXA infoex; | |
| memset( &infoex, 0xcc, sizeof(infoex) ); | |
| SetLastError( 0xdeadbeef ); | |
| infoex.cbSize = sizeof(infoex) - 1; | |
| ret = pGetIconInfoExA( hIcon, &infoex ); | |
| ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); | |
| ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %d\n", GetLastError()); | |
| SetLastError( 0xdeadbeef ); | |
| infoex.cbSize = sizeof(infoex) + 1; | |
| ret = pGetIconInfoExA( hIcon, &infoex ); | |
| ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); | |
| ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %d\n", GetLastError()); | |
| SetLastError( 0xdeadbeef ); | |
| infoex.cbSize = sizeof(infoex); | |
| ret = pGetIconInfoExA( (HICON)0xdeadbabe, &infoex ); | |
| ok_(__FILE__, line)(!ret, "GetIconInfoEx succeeded\n"); | |
| ok_(__FILE__, line)(GetLastError() == ERROR_INVALID_CURSOR_HANDLE, | |
| "wrong error %d\n", GetLastError()); | |
| infoex.cbSize = sizeof(infoex); | |
| ret = pGetIconInfoExA( hIcon, &infoex ); | |
| ok_(__FILE__, line)(ret, "GetIconInfoEx failed err %d\n", GetLastError()); | |
| ok_(__FILE__, line)(infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID); | |
| ok_(__FILE__, line)(infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName); | |
| ok_(__FILE__, line)(infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName); | |
| } | |
| } | |
| #define test_icon_info(a,b,c,d,e) test_icon_info_dbg((a),(b),(c),(d),(e),__LINE__) | |
| static void test_CreateIcon(void) | |
| { | |
| static const BYTE bmp_bits[1024]; | |
| HICON hIcon; | |
| HBITMAP hbmMask, hbmColor; | |
| BITMAPINFO *bmpinfo; | |
| ICONINFO info; | |
| HDC hdc; | |
| void *bits; | |
| UINT display_bpp; | |
| int i; | |
| hdc = GetDC(0); | |
| display_bpp = GetDeviceCaps(hdc, BITSPIXEL); | |
| /* these crash under XP | |
| hIcon = CreateIcon(0, 16, 16, 1, 1, bmp_bits, NULL); | |
| hIcon = CreateIcon(0, 16, 16, 1, 1, NULL, bmp_bits); | |
| */ | |
| hIcon = CreateIcon(0, 16, 16, 1, 1, bmp_bits, bmp_bits); | |
| ok(hIcon != 0, "CreateIcon failed\n"); | |
| test_icon_info(hIcon, 16, 16, 32, 1); | |
| DestroyIcon(hIcon); | |
| hIcon = CreateIcon(0, 16, 16, 1, display_bpp, bmp_bits, bmp_bits); | |
| ok(hIcon != 0, "CreateIcon failed\n"); | |
| test_icon_info(hIcon, 16, 16, 16, display_bpp); | |
| DestroyIcon(hIcon); | |
| hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); | |
| ok(hbmMask != 0, "CreateBitmap failed\n"); | |
| hbmColor = CreateBitmap(16, 16, 1, display_bpp, bmp_bits); | |
| ok(hbmColor != 0, "CreateBitmap failed\n"); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = 0; | |
| info.hbmColor = 0; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(!hIcon, "CreateIconIndirect should fail\n"); | |
| ok(GetLastError() == 0xdeadbeaf, "wrong error %u\n", GetLastError()); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = 0; | |
| info.hbmColor = hbmColor; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(!hIcon, "CreateIconIndirect should fail\n"); | |
| ok(GetLastError() == 0xdeadbeaf, "wrong error %u\n", GetLastError()); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = hbmMask; | |
| info.hbmColor = hbmColor; | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 16, 16, 16, display_bpp); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmMask); | |
| DeleteObject(hbmColor); | |
| hbmMask = CreateBitmap(16, 32, 1, 1, bmp_bits); | |
| ok(hbmMask != 0, "CreateBitmap failed\n"); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = hbmMask; | |
| info.hbmColor = 0; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 16, 16, 32, 1); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmMask); | |
| for (i = 0; i <= 4; i++) | |
| { | |
| hbmMask = CreateBitmap(1, i, 1, 1, bmp_bits); | |
| ok(hbmMask != 0, "CreateBitmap failed\n"); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 0; | |
| info.yHotspot = 0; | |
| info.hbmMask = hbmMask; | |
| info.hbmColor = 0; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 1, i / 2, max(i,1), 1); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmMask); | |
| } | |
| /* test creating an icon from a DIB section */ | |
| bmpinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO,bmiColors[256])); | |
| bmpinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| bmpinfo->bmiHeader.biWidth = 32; | |
| bmpinfo->bmiHeader.biHeight = 32; | |
| bmpinfo->bmiHeader.biPlanes = 1; | |
| bmpinfo->bmiHeader.biBitCount = 8; | |
| bmpinfo->bmiHeader.biCompression = BI_RGB; | |
| hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); | |
| ok(hbmColor != NULL, "Expected a handle to the DIB\n"); | |
| if (bits) | |
| memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); | |
| bmpinfo->bmiHeader.biBitCount = 1; | |
| hbmMask = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); | |
| ok(hbmMask != NULL, "Expected a handle to the DIB\n"); | |
| if (bits) | |
| memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = hbmColor; | |
| info.hbmColor = hbmMask; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 32, 32, 32, 8); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmColor); | |
| bmpinfo->bmiHeader.biBitCount = 16; | |
| hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); | |
| ok(hbmColor != NULL, "Expected a handle to the DIB\n"); | |
| if (bits) | |
| memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = hbmColor; | |
| info.hbmColor = hbmMask; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 32, 32, 32, 8); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmColor); | |
| bmpinfo->bmiHeader.biBitCount = 32; | |
| hbmColor = CreateDIBSection( hdc, bmpinfo, DIB_RGB_COLORS, &bits, NULL, 0 ); | |
| ok(hbmColor != NULL, "Expected a handle to the DIB\n"); | |
| if (bits) | |
| memset( bits, 0x55, 32 * 32 * bmpinfo->bmiHeader.biBitCount / 8 ); | |
| info.fIcon = TRUE; | |
| info.xHotspot = 8; | |
| info.yHotspot = 8; | |
| info.hbmMask = hbmColor; | |
| info.hbmColor = hbmMask; | |
| SetLastError(0xdeadbeaf); | |
| hIcon = CreateIconIndirect(&info); | |
| ok(hIcon != 0, "CreateIconIndirect failed\n"); | |
| test_icon_info(hIcon, 32, 32, 32, 8); | |
| DestroyIcon(hIcon); | |
| DeleteObject(hbmMask); | |
| DeleteObject(hbmColor); | |
| HeapFree( GetProcessHeap(), 0, bmpinfo ); | |
| ReleaseDC(0, hdc); | |
| } | |
| /* Shamelessly ripped from dlls/oleaut32/tests/olepicture.c */ | |
| /* 1x1 pixel gif */ | |
| static const unsigned char gifimage[35] = { | |
| 0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff, | |
| 0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44, | |
| 0x01,0x00,0x3b | |
| }; | |
| /* 1x1 pixel jpg */ | |
| static const unsigned char jpgimage[285] = { | |
| 0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2c, | |
| 0x01,0x2c,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x05,0x03,0x04,0x04,0x04,0x03,0x05, | |
| 0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x07,0x0c,0x08,0x07,0x07,0x07,0x07,0x0f,0x0b, | |
| 0x0b,0x09,0x0c,0x11,0x0f,0x12,0x12,0x11,0x0f,0x11,0x11,0x13,0x16,0x1c,0x17,0x13, | |
| 0x14,0x1a,0x15,0x11,0x11,0x18,0x21,0x18,0x1a,0x1d,0x1d,0x1f,0x1f,0x1f,0x13,0x17, | |
| 0x22,0x24,0x22,0x1e,0x24,0x1c,0x1e,0x1f,0x1e,0xff,0xdb,0x00,0x43,0x01,0x05,0x05, | |
| 0x05,0x07,0x06,0x07,0x0e,0x08,0x08,0x0e,0x1e,0x14,0x11,0x14,0x1e,0x1e,0x1e,0x1e, | |
| 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, | |
| 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, | |
| 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0xff,0xc0, | |
| 0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11, | |
| 0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00, | |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4, | |
| 0x00,0x14,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
| 0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, | |
| 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,0x03,0x01, | |
| 0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xb2,0xc0,0x07,0xff,0xd9 | |
| }; | |
| /* 1x1 pixel png */ | |
| static const unsigned char pngimage[285] = { | |
| 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, | |
| 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53, | |
| 0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b, | |
| 0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5, | |
| 0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41, | |
| 0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59, | |
| 0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 | |
| }; | |
| /* 1x1 pixel bmp with gap between palette and bitmap. Correct bitmap contains only | |
| zeroes, gap is 0xFF. */ | |
| static unsigned char bmpimage[70] = { | |
| 0x42,0x4d,0x46,0x00,0x00,0x00,0xDE,0xAD,0xBE,0xEF,0x42,0x00,0x00,0x00,0x28,0x00, | |
| 0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00, | |
| 0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00, | |
| 0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0x55,0x55,0x55,0x00,0xFF,0xFF, | |
| 0xFF,0xFF,0x00,0x00,0x00,0x00 | |
| }; | |
| /* 1x1 pixel bmp using BITMAPCOREHEADER */ | |
| static const unsigned char bmpcoreimage[38] = { | |
| 0x42,0x4d,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x0c,0x00, | |
| 0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xff,0xff,0xff,0x00,0x55,0x55, | |
| 0x55,0x00,0x00,0x00,0x00,0x00 | |
| }; | |
| /* 2x2 pixel gif */ | |
| static const unsigned char gif4pixel[42] = { | |
| 0x47,0x49,0x46,0x38,0x37,0x61,0x02,0x00,0x02,0x00,0xa1,0x00,0x00,0x00,0x00,0x00, | |
| 0x39,0x62,0xfc,0xff,0x1a,0xe5,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x02,0x00, | |
| 0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b | |
| }; | |
| static const DWORD biSize_tests[] = { | |
| 0, | |
| sizeof(BITMAPCOREHEADER) - 1, | |
| sizeof(BITMAPCOREHEADER) + 1, | |
| sizeof(BITMAPINFOHEADER) - 1, | |
| sizeof(BITMAPINFOHEADER) + 1, | |
| sizeof(BITMAPV4HEADER) - 1, | |
| sizeof(BITMAPV4HEADER) + 1, | |
| sizeof(BITMAPV5HEADER) - 1, | |
| sizeof(BITMAPV5HEADER) + 1, | |
| (sizeof(BITMAPCOREHEADER) + sizeof(BITMAPINFOHEADER)) / 2, | |
| (sizeof(BITMAPV4HEADER) + sizeof(BITMAPV5HEADER)) / 2, | |
| 0xdeadbeef, | |
| 0xffffffff | |
| }; | |
| #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) | |
| static void test_LoadImageBitmap(const char * test_desc, HBITMAP hbm) | |
| { | |
| BITMAP bm; | |
| BITMAPINFO bmi; | |
| DWORD ret, pixel = 0; | |
| HDC hdc = GetDC(NULL); | |
| ret = GetObjectA(hbm, sizeof(bm), &bm); | |
| ok(ret == sizeof(bm), "GetObject returned %d\n", ret); | |
| memset(&bmi, 0, sizeof(bmi)); | |
| bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); | |
| bmi.bmiHeader.biWidth = bm.bmWidth; | |
| bmi.bmiHeader.biHeight = bm.bmHeight; | |
| bmi.bmiHeader.biPlanes = 1; | |
| bmi.bmiHeader.biBitCount= 24; | |
| bmi.bmiHeader.biCompression= BI_RGB; | |
| ret = GetDIBits(hdc, hbm, 0, bm.bmHeight, &pixel, &bmi, DIB_RGB_COLORS); | |
| ok(ret == bm.bmHeight, "%s: %d lines were converted, not %d\n", test_desc, ret, bm.bmHeight); | |
| ok(color_match(pixel, 0x00ffffff), "%s: Pixel is 0x%08x\n", test_desc, pixel); | |
| } | |
| static void test_LoadImageFile(const char * test_desc, const unsigned char * image_data, | |
| unsigned int image_size, const char * ext, BOOL expect_success) | |
| { | |
| HANDLE handle; | |
| BOOL ret; | |
| DWORD error, bytes_written; | |
| char filename[64]; | |
| strcpy(filename, "test."); | |
| strcat(filename, ext); | |
| /* Create the test image. */ | |
| handle = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, | |
| FILE_ATTRIBUTE_NORMAL, NULL); | |
| ok(handle != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); | |
| ret = WriteFile(handle, image_data, image_size, &bytes_written, NULL); | |
| ok(ret && bytes_written == image_size, "test file created improperly.\n"); | |
| CloseHandle(handle); | |
| /* Load as cursor. For all tested formats, this should fail */ | |
| SetLastError(0xdeadbeef); | |
| handle = LoadImageA(NULL, filename, IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); | |
| ok(handle == NULL, "%s: IMAGE_CURSOR succeeded incorrectly.\n", test_desc); | |
| error = GetLastError(); | |
| ok(error == 0 || | |
| broken(error == 0xdeadbeef) || /* Win9x */ | |
| broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ | |
| "Last error: %u\n", error); | |
| if (handle != NULL) DestroyCursor(handle); | |
| /* Load as icon. For all tested formats, this should fail */ | |
| SetLastError(0xdeadbeef); | |
| handle = LoadImageA(NULL, filename, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); | |
| ok(handle == NULL, "%s: IMAGE_ICON succeeded incorrectly.\n", test_desc); | |
| error = GetLastError(); | |
| ok(error == 0 || | |
| broken(error == 0xdeadbeef) || /* Win9x */ | |
| broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ | |
| "Last error: %u\n", error); | |
| if (handle != NULL) DestroyIcon(handle); | |
| /* Load as bitmap. Should succeed for correct bmp, fail for everything else */ | |
| SetLastError(0xdeadbeef); | |
| handle = LoadImageA(NULL, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); | |
| error = GetLastError(); | |
| ok(error == 0 || | |
| error == 0xdeadbeef, /* Win9x, WinMe */ | |
| "Last error: %u\n", error); | |
| if (expect_success) { | |
| ok(handle != NULL, "%s: IMAGE_BITMAP failed.\n", test_desc); | |
| if (handle != NULL) test_LoadImageBitmap(test_desc, handle); | |
| } | |
| else ok(handle == NULL, "%s: IMAGE_BITMAP succeeded incorrectly.\n", test_desc); | |
| if (handle != NULL) DeleteObject(handle); | |
| DeleteFileA(filename); | |
| } | |
| typedef struct { | |
| unsigned width; | |
| unsigned height; | |
| BOOL invalid_offset; | |
| } test_icon_entries_t; | |
| static void create_ico_file(const char *filename, const test_icon_entries_t *test_icon_entries, unsigned entry_cnt) | |
| { | |
| CURSORICONFILEDIRENTRY *icon_entry; | |
| BITMAPINFOHEADER *icon_header; | |
| CURSORICONFILEDIR *dir; | |
| BYTE *buf, *bitmap_ptr; | |
| DWORD bytes_written; | |
| size_t icon_size; | |
| HANDLE file; | |
| unsigned i; | |
| BOOL ret; | |
| const unsigned icon_bpp = 32; | |
| icon_size = FIELD_OFFSET(CURSORICONFILEDIR, idEntries[entry_cnt]) + sizeof(BITMAPINFOHEADER)*entry_cnt; | |
| for(i=0; i<entry_cnt; i++) | |
| icon_size += icon_bpp * test_icon_entries[i].width * test_icon_entries[i].height / 8; | |
| buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, icon_size); | |
| dir = (CURSORICONFILEDIR*)buf; | |
| dir->idReserved = 0; | |
| dir->idType = 1; | |
| dir->idCount = entry_cnt; | |
| bitmap_ptr = buf + FIELD_OFFSET(CURSORICONFILEDIR, idEntries[entry_cnt]); | |
| for(i=0; i<entry_cnt; i++) { | |
| icon_entry = dir->idEntries+i; | |
| icon_entry->bWidth = test_icon_entries[i].width; | |
| icon_entry->bHeight = test_icon_entries[i].height; | |
| icon_entry->bColorCount = 0; | |
| icon_entry->bReserved = 0; | |
| icon_entry->xHotspot = 1; | |
| icon_entry->yHotspot = 1; | |
| icon_entry->dwDIBSize = sizeof(BITMAPINFOHEADER) + icon_entry->bWidth * icon_entry->bHeight * icon_bpp / 8; | |
| icon_entry->dwDIBOffset = test_icon_entries[i].invalid_offset ? 0xffffffff : bitmap_ptr - buf; | |
| icon_header = (BITMAPINFOHEADER*)bitmap_ptr; | |
| bitmap_ptr += icon_entry->dwDIBSize; | |
| icon_header->biSize = sizeof(BITMAPINFOHEADER); | |
| icon_header->biWidth = icon_entry->bWidth; | |
| icon_header->biHeight = icon_entry->bHeight; | |
| icon_header->biPlanes = 1; | |
| icon_header->biBitCount = icon_bpp; | |
| icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ | |
| } | |
| memset(bitmap_ptr, 0xf0, buf+icon_size-bitmap_ptr); | |
| /* Create the icon. */ | |
| file = CreateFileA(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); | |
| ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); | |
| ret = WriteFile(file, buf, icon_size, &bytes_written, NULL); | |
| ok(ret && bytes_written == icon_size, "icon.ico created improperly.\n"); | |
| CloseHandle(file); | |
| HeapFree(GetProcessHeap(), 0, buf); | |
| } | |
| static void test_LoadImage(void) | |
| { | |
| HANDLE handle; | |
| BOOL ret; | |
| DWORD error; | |
| BITMAPINFOHEADER *bitmap_header; | |
| ICONINFO icon_info; | |
| int i; | |
| #define ICON_WIDTH 32 | |
| #define ICON_HEIGHT 32 | |
| #define ICON_AND_SIZE (ICON_WIDTH*ICON_HEIGHT/8) | |
| #define ICON_BPP 32 | |
| #define ICON_SIZE \ | |
| (sizeof(CURSORICONFILEDIR) + sizeof(BITMAPINFOHEADER) \ | |
| + ICON_AND_SIZE + ICON_AND_SIZE*ICON_BPP) | |
| static const test_icon_entries_t icon_desc = {32, 32}; | |
| create_ico_file("icon.ico", &icon_desc, 1); | |
| /* Test loading an icon as a cursor. */ | |
| SetLastError(0xdeadbeef); | |
| handle = LoadImageA(NULL, "icon.ico", IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); | |
| ok(handle != NULL, "LoadImage() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0 || | |
| broken(error == 0xdeadbeef) || /* Win9x */ | |
| broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ | |
| "Last error: %u\n", error); | |
| /* Test the icon information. */ | |
| SetLastError(0xdeadbeef); | |
| ret = GetIconInfo(handle, &icon_info); | |
| ok(ret, "GetIconInfo() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| if (ret) | |
| { | |
| ok(icon_info.fIcon == FALSE, "fIcon != FALSE.\n"); | |
| ok(icon_info.xHotspot == 1, "xHotspot is %u.\n", icon_info.xHotspot); | |
| ok(icon_info.yHotspot == 1, "yHotspot is %u.\n", icon_info.yHotspot); | |
| ok(icon_info.hbmColor != NULL || broken(!icon_info.hbmColor) /* no color cursor support */, | |
| "No hbmColor!\n"); | |
| ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); | |
| } | |
| if (pGetIconInfoExA) | |
| { | |
| ICONINFOEXA infoex; | |
| infoex.cbSize = sizeof(infoex); | |
| ret = pGetIconInfoExA( handle, &infoex ); | |
| ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); | |
| ok( infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID ); | |
| ok( infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName ); | |
| ok( infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName ); | |
| } | |
| else win_skip( "GetIconInfoEx not available\n" ); | |
| /* Clean up. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(handle); | |
| ok(ret, "DestroyCursor() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| DeleteFileA("icon.ico"); | |
| /* Test a system icon */ | |
| handle = LoadIconA( 0, (LPCSTR)IDI_HAND ); | |
| ok(handle != NULL, "LoadImage() failed.\n"); | |
| if (pGetIconInfoExA) | |
| { | |
| ICONINFOEXA infoexA; | |
| ICONINFOEXW infoexW; | |
| infoexA.cbSize = sizeof(infoexA); | |
| ret = pGetIconInfoExA( handle, &infoexA ); | |
| ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); | |
| ok( infoexA.wResID == (UINT_PTR)IDI_HAND, "GetIconInfoEx wrong resid %x\n", infoexA.wResID ); | |
| /* the A version is broken on 64-bit, it truncates the string after the first char */ | |
| if (is_win64 && infoexA.szModName[0] && infoexA.szModName[1] == 0) | |
| trace( "GetIconInfoExA broken on Win64\n" ); | |
| else | |
| ok( GetModuleHandleA(infoexA.szModName) == GetModuleHandleA("user32.dll"), | |
| "GetIconInfoEx wrong module %s\n", infoexA.szModName ); | |
| ok( infoexA.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoexA.szResName ); | |
| infoexW.cbSize = sizeof(infoexW); | |
| ret = pGetIconInfoExW( handle, &infoexW ); | |
| ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); | |
| ok( infoexW.wResID == (UINT_PTR)IDI_HAND, "GetIconInfoEx wrong resid %x\n", infoexW.wResID ); | |
| ok( GetModuleHandleW(infoexW.szModName) == GetModuleHandleA("user32.dll"), | |
| "GetIconInfoEx wrong module %s\n", wine_dbgstr_w(infoexW.szModName) ); | |
| ok( infoexW.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", wine_dbgstr_w(infoexW.szResName) ); | |
| } | |
| SetLastError(0xdeadbeef); | |
| DestroyIcon(handle); | |
| test_LoadImageFile("BMP", bmpimage, sizeof(bmpimage), "bmp", 1); | |
| test_LoadImageFile("BMP (coreinfo)", bmpcoreimage, sizeof(bmpcoreimage), "bmp", 1); | |
| test_LoadImageFile("GIF", gifimage, sizeof(gifimage), "gif", 0); | |
| test_LoadImageFile("GIF (2x2 pixel)", gif4pixel, sizeof(gif4pixel), "gif", 0); | |
| test_LoadImageFile("JPG", jpgimage, sizeof(jpgimage), "jpg", 0); | |
| test_LoadImageFile("PNG", pngimage, sizeof(pngimage), "png", 0); | |
| /* Check failure for broken BMP images */ | |
| bitmap_header = (BITMAPINFOHEADER *)(bmpimage + sizeof(BITMAPFILEHEADER)); | |
| bitmap_header->biHeight = 65536; | |
| test_LoadImageFile("BMP (too high)", bmpimage, sizeof(bmpimage), "bmp", 0); | |
| bitmap_header->biHeight = 1; | |
| bitmap_header->biWidth = 65536; | |
| test_LoadImageFile("BMP (too wide)", bmpimage, sizeof(bmpimage), "bmp", 0); | |
| bitmap_header->biWidth = 1; | |
| for (i = 0; i < ARRAY_SIZE(biSize_tests); i++) { | |
| bitmap_header->biSize = biSize_tests[i]; | |
| test_LoadImageFile("BMP (broken biSize)", bmpimage, sizeof(bmpimage), "bmp", 0); | |
| } | |
| bitmap_header->biSize = sizeof(BITMAPINFOHEADER); | |
| } | |
| #undef ARRAY_SIZE | |
| static void test_CreateIconFromResource(void) | |
| { | |
| HANDLE handle; | |
| BOOL ret; | |
| DWORD error; | |
| BITMAPINFOHEADER *icon_header; | |
| INT16 *hotspot; | |
| ICONINFO icon_info; | |
| #define ICON_RES_WIDTH 32 | |
| #define ICON_RES_HEIGHT 32 | |
| #define ICON_RES_AND_SIZE (ICON_WIDTH*ICON_HEIGHT/8) | |
| #define ICON_RES_BPP 32 | |
| #define ICON_RES_SIZE \ | |
| (sizeof(BITMAPINFOHEADER) + ICON_AND_SIZE + ICON_AND_SIZE*ICON_BPP) | |
| #define CRSR_RES_SIZE (2*sizeof(INT16) + ICON_RES_SIZE) | |
| /* Set icon data. */ | |
| hotspot = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CRSR_RES_SIZE); | |
| /* Cursor resources have an extra hotspot, icon resources not. */ | |
| hotspot[0] = 3; | |
| hotspot[1] = 3; | |
| icon_header = (BITMAPINFOHEADER *) (hotspot + 2); | |
| icon_header->biSize = sizeof(BITMAPINFOHEADER); | |
| icon_header->biWidth = ICON_WIDTH; | |
| icon_header->biHeight = ICON_HEIGHT*2; | |
| icon_header->biPlanes = 1; | |
| icon_header->biBitCount = ICON_BPP; | |
| icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ | |
| /* Test creating a cursor. */ | |
| SetLastError(0xdeadbeef); | |
| handle = CreateIconFromResource((PBYTE) hotspot, CRSR_RES_SIZE, FALSE, 0x00030000); | |
| ok(handle != NULL, "Create cursor failed.\n"); | |
| /* Test the icon information. */ | |
| SetLastError(0xdeadbeef); | |
| ret = GetIconInfo(handle, &icon_info); | |
| ok(ret, "GetIconInfo() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| if (ret) | |
| { | |
| ok(icon_info.fIcon == FALSE, "fIcon != FALSE.\n"); | |
| ok(icon_info.xHotspot == 3, "xHotspot is %u.\n", icon_info.xHotspot); | |
| ok(icon_info.yHotspot == 3, "yHotspot is %u.\n", icon_info.yHotspot); | |
| ok(icon_info.hbmColor != NULL || broken(!icon_info.hbmColor) /* no color cursor support */, | |
| "No hbmColor!\n"); | |
| ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); | |
| } | |
| if (pGetIconInfoExA) | |
| { | |
| ICONINFOEXA infoex; | |
| infoex.cbSize = sizeof(infoex); | |
| ret = pGetIconInfoExA( handle, &infoex ); | |
| ok( ret, "GetIconInfoEx failed err %d\n", GetLastError() ); | |
| ok( infoex.wResID == 0, "GetIconInfoEx wrong resid %x\n", infoex.wResID ); | |
| ok( infoex.szModName[0] == 0, "GetIconInfoEx wrong module %s\n", infoex.szModName ); | |
| ok( infoex.szResName[0] == 0, "GetIconInfoEx wrong name %s\n", infoex.szResName ); | |
| } | |
| /* Clean up. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(handle); | |
| ok(ret, "DestroyCursor() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| /* Test creating an icon. */ | |
| SetLastError(0xdeadbeef); | |
| handle = CreateIconFromResource((PBYTE) icon_header, ICON_RES_SIZE, TRUE, | |
| 0x00030000); | |
| ok(handle != NULL, "Create icon failed.\n"); | |
| /* Test the icon information. */ | |
| SetLastError(0xdeadbeef); | |
| ret = GetIconInfo(handle, &icon_info); | |
| ok(ret, "GetIconInfo() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| if (ret) | |
| { | |
| ok(icon_info.fIcon == TRUE, "fIcon != TRUE.\n"); | |
| /* Icons always have hotspot in the middle */ | |
| ok(icon_info.xHotspot == ICON_WIDTH/2, "xHotspot is %u.\n", icon_info.xHotspot); | |
| ok(icon_info.yHotspot == ICON_HEIGHT/2, "yHotspot is %u.\n", icon_info.yHotspot); | |
| ok(icon_info.hbmColor != NULL, "No hbmColor!\n"); | |
| ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); | |
| } | |
| /* Clean up. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(handle); | |
| ok(ret, "DestroyCursor() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| /* Rejection of NULL pointer crashes at least on WNT4WSSP6, W2KPROSP4, WXPPROSP3 | |
| * | |
| * handle = CreateIconFromResource(NULL, ICON_RES_SIZE, TRUE, 0x00030000); | |
| * ok(handle == NULL, "Invalid pointer accepted (%p)\n", handle); | |
| */ | |
| HeapFree(GetProcessHeap(), 0, hotspot); | |
| /* Test creating an animated cursor. */ | |
| empty_anicursor.frames[0].data.icon_info.idType = 2; /* type: cursor */ | |
| empty_anicursor.frames[0].data.icon_info.idEntries[0].xHotspot = 3; | |
| empty_anicursor.frames[0].data.icon_info.idEntries[0].yHotspot = 3; | |
| handle = CreateIconFromResource((PBYTE) &empty_anicursor, sizeof(empty_anicursor), FALSE, 0x00030000); | |
| ok(handle != NULL, "Create cursor failed.\n"); | |
| /* Test the animated cursor's information. */ | |
| SetLastError(0xdeadbeef); | |
| ret = GetIconInfo(handle, &icon_info); | |
| ok(ret, "GetIconInfo() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| if (ret) | |
| { | |
| ok(icon_info.fIcon == FALSE, "fIcon != FALSE.\n"); | |
| ok(icon_info.xHotspot == 3, "xHotspot is %u.\n", icon_info.xHotspot); | |
| ok(icon_info.yHotspot == 3, "yHotspot is %u.\n", icon_info.yHotspot); | |
| ok(icon_info.hbmColor != NULL || broken(!icon_info.hbmColor) /* no color cursor support */, | |
| "No hbmColor!\n"); | |
| ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); | |
| } | |
| /* Clean up. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(handle); | |
| ok(ret, "DestroyCursor() failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| } | |
| static int check_cursor_data( HDC hdc, HCURSOR hCursor, void *data, int length) | |
| { | |
| char *image = NULL; | |
| BITMAPINFO *info; | |
| ICONINFO iinfo; | |
| DWORD ret; | |
| int i; | |
| ret = GetIconInfo( hCursor, &iinfo ); | |
| ok(ret, "GetIconInfo() failed\n"); | |
| if (!ret) return 0; | |
| ret = 0; | |
| info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )); | |
| ok(info != NULL, "HeapAlloc() failed\n"); | |
| if (!info) return 0; | |
| info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| info->bmiHeader.biWidth = 32; | |
| info->bmiHeader.biHeight = 32; | |
| info->bmiHeader.biPlanes = 1; | |
| info->bmiHeader.biBitCount = 32; | |
| info->bmiHeader.biCompression = BI_RGB; | |
| info->bmiHeader.biSizeImage = 32 * 32 * 4; | |
| info->bmiHeader.biXPelsPerMeter = 0; | |
| info->bmiHeader.biYPelsPerMeter = 0; | |
| info->bmiHeader.biClrUsed = 0; | |
| info->bmiHeader.biClrImportant = 0; | |
| image = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ); | |
| ok(image != NULL, "HeapAlloc() failed\n"); | |
| if (!image) goto cleanup; | |
| ret = GetDIBits( hdc, iinfo.hbmColor, 0, 32, image, info, DIB_RGB_COLORS ); | |
| ok(ret, "GetDIBits() failed\n"); | |
| for (i = 0; ret && i < length / sizeof(COLORREF); i++) | |
| { | |
| ret = color_match( ((COLORREF *)data)[i], ((COLORREF *)image)[i] ); | |
| ok(ret, "%04x: Expected 0x%x, actually 0x%x\n", i, ((COLORREF *)data)[i], ((COLORREF *)image)[i] ); | |
| } | |
| cleanup: | |
| HeapFree( GetProcessHeap(), 0, image ); | |
| HeapFree( GetProcessHeap(), 0, info ); | |
| return ret; | |
| } | |
| static HCURSOR (WINAPI *pGetCursorFrameInfo)(HCURSOR hCursor, DWORD unk1, DWORD istep, DWORD *rate, DWORD *steps); | |
| static void test_GetCursorFrameInfo(void) | |
| { | |
| DWORD frame_identifier[] = { 0x10Ad, 0xc001, 0x1c05 }; | |
| HBITMAP bmp = NULL, bmpOld = NULL; | |
| DWORD rate, steps; | |
| BITMAPINFOHEADER *icon_header; | |
| BITMAPINFO bitmapInfo; | |
| HDC hdc = NULL; | |
| void *bits = 0; | |
| INT16 *hotspot; | |
| HANDLE h1, h2; | |
| BOOL ret; | |
| int i; | |
| if (!pGetCursorFrameInfo) | |
| { | |
| win_skip( "GetCursorFrameInfo not supported, skipping tests.\n" ); | |
| return; | |
| } | |
| hdc = CreateCompatibleDC(0); | |
| ok(hdc != 0, "CreateCompatibleDC(0) failed to return a valid DC\n"); | |
| if (!hdc) | |
| return; | |
| memset(&bitmapInfo, 0, sizeof(bitmapInfo)); | |
| bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| bitmapInfo.bmiHeader.biWidth = 3; | |
| bitmapInfo.bmiHeader.biHeight = 3; | |
| bitmapInfo.bmiHeader.biBitCount = 32; | |
| bitmapInfo.bmiHeader.biPlanes = 1; | |
| bitmapInfo.bmiHeader.biCompression = BI_RGB; | |
| bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32); | |
| bmp = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, &bits, NULL, 0); | |
| ok (bmp && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n"); | |
| if (!bmp || !bits) | |
| goto cleanup; | |
| bmpOld = SelectObject(hdc, bmp); | |
| #define ICON_RES_WIDTH 32 | |
| #define ICON_RES_HEIGHT 32 | |
| #define ICON_RES_AND_SIZE (ICON_WIDTH*ICON_HEIGHT/8) | |
| #define ICON_RES_BPP 32 | |
| #define ICON_RES_SIZE \ | |
| (sizeof(BITMAPINFOHEADER) + ICON_AND_SIZE + ICON_AND_SIZE*ICON_BPP) | |
| #define CRSR_RES_SIZE (2*sizeof(INT16) + ICON_RES_SIZE) | |
| /* Set icon data. */ | |
| hotspot = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CRSR_RES_SIZE); | |
| /* Cursor resources have an extra hotspot, icon resources not. */ | |
| hotspot[0] = 3; | |
| hotspot[1] = 3; | |
| icon_header = (BITMAPINFOHEADER *) (hotspot + 2); | |
| icon_header->biSize = sizeof(BITMAPINFOHEADER); | |
| icon_header->biWidth = ICON_WIDTH; | |
| icon_header->biHeight = ICON_HEIGHT*2; | |
| icon_header->biPlanes = 1; | |
| icon_header->biBitCount = ICON_BPP; | |
| icon_header->biSizeImage = 0; /* Uncompressed bitmap. */ | |
| /* Creating a static cursor. */ | |
| SetLastError(0xdeadbeef); | |
| h1 = CreateIconFromResource((PBYTE) hotspot, CRSR_RES_SIZE, FALSE, 0x00030000); | |
| ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); | |
| /* Check GetCursorFrameInfo behavior on a static cursor */ | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, 0xdead, &rate, &steps); | |
| ok(h1 == h2, "GetCursorFrameInfo() failed: (%p != %p).\n", h1, h2); | |
| ok(rate == 0, "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x0).\n", rate); | |
| ok(steps == 1, "GetCursorFrameInfo() unexpected param 5 value (%d != 1).\n", steps); | |
| /* Clean up static cursor. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(h1); | |
| ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); | |
| /* Creating a single-frame animated cursor. */ | |
| empty_anicursor.frames[0].data.icon_info.idType = 2; /* type: cursor */ | |
| empty_anicursor.frames[0].data.icon_info.idEntries[0].xHotspot = 3; | |
| empty_anicursor.frames[0].data.icon_info.idEntries[0].yHotspot = 3; | |
| memcpy( &empty_anicursor.frames[0].data.bmi_data.data[0], &frame_identifier[0], sizeof(DWORD) ); | |
| SetLastError(0xdeadbeef); | |
| h1 = CreateIconFromResource((PBYTE) &empty_anicursor, sizeof(empty_anicursor), FALSE, 0x00030000); | |
| ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); | |
| /* Check GetCursorFrameInfo behavior on a single-frame animated cursor */ | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, 0, &rate, &steps); | |
| ok(h1 == h2, "GetCursorFrameInfo() failed: (%p != %p).\n", h1, h2); | |
| ret = check_cursor_data( hdc, h2, &frame_identifier[0], sizeof(DWORD) ); | |
| ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame 0.\n"); | |
| ok(rate == 0x0, "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x0).\n", rate); | |
| ok(steps == empty_anicursor.header.header.num_steps, | |
| "GetCursorFrameInfo() unexpected param 5 value (%d != 1).\n", steps); | |
| /* Clean up single-frame animated cursor. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(h1); | |
| ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); | |
| /* Creating a multi-frame animated cursor. */ | |
| for (i=0; i<empty_anicursor3.header.header.num_frames; i++) | |
| { | |
| empty_anicursor3.frames[i].data.icon_info.idType = 2; /* type: cursor */ | |
| empty_anicursor3.frames[i].data.icon_info.idEntries[0].xHotspot = 3; | |
| empty_anicursor3.frames[i].data.icon_info.idEntries[0].yHotspot = 3; | |
| memcpy( &empty_anicursor3.frames[i].data.bmi_data.data[0], &frame_identifier[i], sizeof(DWORD) ); | |
| } | |
| SetLastError(0xdeadbeef); | |
| h1 = CreateIconFromResource((PBYTE) &empty_anicursor3, sizeof(empty_anicursor3), FALSE, 0x00030000); | |
| ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); | |
| /* Check number of steps in multi-frame animated cursor */ | |
| i=0; | |
| while (DrawIconEx(hdc, 0, 0, h1, 32, 32, i, NULL, DI_NORMAL)) | |
| i++; | |
| ok(i == empty_anicursor3.header.header.num_steps, | |
| "Unexpected number of steps in cursor (%d != %d)\n", | |
| i, empty_anicursor3.header.header.num_steps); | |
| /* Check GetCursorFrameInfo behavior on a multi-frame animated cursor */ | |
| for (i=0; i<empty_anicursor3.header.header.num_frames; i++) | |
| { | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, i, &rate, &steps); | |
| ok(h1 != h2 && h2 != 0, "GetCursorFrameInfo() failed for cursor %p: (%p, %p).\n", h1, h1, h2); | |
| ret = check_cursor_data( hdc, h2, &frame_identifier[i], sizeof(DWORD) ); | |
| ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame %d.\n", i); | |
| ok(rate == empty_anicursor3.header.header.display_rate, | |
| "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x%x).\n", | |
| rate, empty_anicursor3.header.header.display_rate); | |
| ok(steps == empty_anicursor3.header.header.num_steps, | |
| "GetCursorFrameInfo() unexpected param 5 value (%d != %d).\n", | |
| steps, empty_anicursor3.header.header.num_steps); | |
| } | |
| /* Check GetCursorFrameInfo behavior on rate 3 of a multi-frame animated cursor */ | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, 3, &rate, &steps); | |
| ok(h2 == 0, "GetCursorFrameInfo() failed for cursor %p: (%p != 0).\n", h1, h2); | |
| ok(rate == 0xdead || broken(rate == empty_anicursor3.header.header.display_rate) /*win2k*/ | |
| || broken(rate == ~0) /*win2k (sporadic)*/, | |
| "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0xdead).\n", rate); | |
| ok(steps == 0xdead || broken(steps == empty_anicursor3.header.header.num_steps) /*win2k*/ | |
| || broken(steps == 0) /*win2k (sporadic)*/, | |
| "GetCursorFrameInfo() unexpected param 5 value (0x%x != 0xdead).\n", steps); | |
| /* Clean up multi-frame animated cursor. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(h1); | |
| ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); | |
| /* Create a multi-frame animated cursor with num_steps == 1 */ | |
| empty_anicursor3.header.header.num_steps = 1; | |
| SetLastError(0xdeadbeef); | |
| h1 = CreateIconFromResource((PBYTE) &empty_anicursor3, sizeof(empty_anicursor3), FALSE, 0x00030000); | |
| ok(h1 != NULL, "Create cursor failed (error = %d).\n", GetLastError()); | |
| /* Check number of steps in multi-frame animated cursor (mismatch between steps and frames) */ | |
| i=0; | |
| while (DrawIconEx(hdc, 0, 0, h1, 32, 32, i, NULL, DI_NORMAL)) | |
| i++; | |
| ok(i == empty_anicursor3.header.header.num_steps, | |
| "Unexpected number of steps in cursor (%d != %d)\n", | |
| i, empty_anicursor3.header.header.num_steps); | |
| /* Check GetCursorFrameInfo behavior on rate 0 for a multi-frame animated cursor (with num_steps == 1) */ | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, 0, &rate, &steps); | |
| ok(h1 != h2 && h2 != 0, "GetCursorFrameInfo() failed for cursor %p: (%p, %p).\n", h1, h1, h2); | |
| ret = check_cursor_data( hdc, h2, &frame_identifier[0], sizeof(DWORD) ); | |
| ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame 0.\n"); | |
| ok(rate == empty_anicursor3.header.header.display_rate, | |
| "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x%x).\n", | |
| rate, empty_anicursor3.header.header.display_rate); | |
| ok(steps == ~0 || broken(steps == empty_anicursor3.header.header.num_steps) /*win2k*/, | |
| "GetCursorFrameInfo() unexpected param 5 value (%d != ~0).\n", steps); | |
| /* Check GetCursorFrameInfo behavior on rate 1 for a multi-frame animated cursor (with num_steps == 1) */ | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, 1, &rate, &steps); | |
| ok(h2 == 0, "GetCursorFrameInfo() failed for cursor %p: (%p != 0).\n", h1, h2); | |
| ok(rate == 0xdead || broken(rate == empty_anicursor3.header.header.display_rate) /*win2k*/ | |
| || broken(rate == ~0) /*win2k (sporadic)*/, | |
| "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0xdead).\n", rate); | |
| ok(steps == 0xdead || broken(steps == empty_anicursor3.header.header.num_steps) /*win2k*/ | |
| || broken(steps == 0) /*win2k (sporadic)*/, | |
| "GetCursorFrameInfo() unexpected param 5 value (%d != 0xdead).\n", steps); | |
| /* Clean up multi-frame animated cursor. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(h1); | |
| ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); | |
| /* Creating a multi-frame animated cursor with rate data. */ | |
| for (i=0; i<empty_anicursor3_seq.header.header.num_frames; i++) | |
| { | |
| empty_anicursor3_seq.frames[i].data.icon_info.idType = 2; /* type: cursor */ | |
| empty_anicursor3_seq.frames[i].data.icon_info.idEntries[0].xHotspot = 3; | |
| empty_anicursor3_seq.frames[i].data.icon_info.idEntries[0].yHotspot = 3; | |
| memcpy( &empty_anicursor3_seq.frames[i].data.bmi_data.data[0], &frame_identifier[i], sizeof(DWORD) ); | |
| } | |
| SetLastError(0xdeadbeef); | |
| h1 = CreateIconFromResource((PBYTE) &empty_anicursor3_seq, sizeof(empty_anicursor3_seq), FALSE, 0x00030000); | |
| ok(h1 != NULL, "Create cursor failed (error = %x).\n", GetLastError()); | |
| /* Check number of steps in multi-frame animated cursor with rate data */ | |
| i=0; | |
| while (DrawIconEx(hdc, 0, 0, h1, 32, 32, i, NULL, DI_NORMAL)) | |
| i++; | |
| ok(i == empty_anicursor3_seq.header.header.num_steps, | |
| "Unexpected number of steps in cursor (%d != %d)\n", | |
| i, empty_anicursor3_seq.header.header.num_steps); | |
| /* Check GetCursorFrameInfo behavior on a multi-frame animated cursor with rate data */ | |
| for (i=0; i<empty_anicursor3_seq.header.header.num_frames; i++) | |
| { | |
| int frame_id = empty_anicursor3_seq.seq.order[i]; | |
| rate = steps = 0xdead; | |
| h2 = pGetCursorFrameInfo(h1, 0xdead, i, &rate, &steps); | |
| ok(h1 != h2 && h2 != 0, "GetCursorFrameInfo() failed for cursor %p: (%p, %p).\n", h1, h1, h2); | |
| ret = check_cursor_data( hdc, h2, &frame_identifier[frame_id], sizeof(DWORD) ); | |
| ok(ret, "GetCursorFrameInfo() returned wrong cursor data for frame %d.\n", i); | |
| ok(rate == empty_anicursor3_seq.rates.rate[i], | |
| "GetCursorFrameInfo() unexpected param 4 value (0x%x != 0x%x).\n", | |
| rate, empty_anicursor3_seq.rates.rate[i]); | |
| ok(steps == empty_anicursor3_seq.header.header.num_steps, | |
| "GetCursorFrameInfo() unexpected param 5 value (%d != %d).\n", | |
| steps, empty_anicursor3_seq.header.header.num_steps); | |
| } | |
| /* Clean up multi-frame animated cursor with rate data. */ | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(h1); | |
| ok(ret, "DestroyCursor() failed (error = %d).\n", GetLastError()); | |
| HeapFree(GetProcessHeap(), 0, hotspot); | |
| cleanup: | |
| if(bmpOld) SelectObject(hdc, bmpOld); | |
| if(bmp) DeleteObject(bmp); | |
| if(hdc) DeleteDC(hdc); | |
| } | |
| static HICON create_test_icon(HDC hdc, int width, int height, int bpp, | |
| BOOL maskvalue, UINT32 *color, int colorSize) | |
| { | |
| ICONINFO iconInfo; | |
| BITMAPINFO bitmapInfo; | |
| void *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, &buffer, NULL, 0); | |
| if(!iconInfo.hbmColor || !buffer) | |
| { | |
| DeleteObject(iconInfo.hbmMask); | |
| return NULL; | |
| } | |
| memcpy(buffer, color, colorSize); | |
| return CreateIconIndirect(&iconInfo); | |
| } | |
| static void check_alpha_draw(HDC hdc, BOOL drawiconex, BOOL alpha, int bpp, int line) | |
| { | |
| HICON hicon; | |
| UINT32 color[2]; | |
| COLORREF modern_expected, legacy_expected, result; | |
| 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); | |
| SetPixelV(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1, background); | |
| SetPixelV(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 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); | |
| result = GetPixel(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1); | |
| 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); | |
| result = GetPixel(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); | |
| ok (color_match(result, background), | |
| "Overlaying Mask %d on Color %06X with DrawIcon. " | |
| "Expected unchanged background color %06X. Got %06X from line %d\n", | |
| maskvalue, color, background, result, line); | |
| } | |
| static void test_DrawIcon(void) | |
| { | |
| BITMAPINFO bitmapInfo; | |
| HDC hdcDst = NULL; | |
| HBITMAP bmpDst = NULL; | |
| HBITMAP bmpOld = NULL; | |
| void *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 palettizing.\n"); | |
| goto cleanup; | |
| } | |
| memset(&bitmapInfo, 0, sizeof(bitmapInfo)); | |
| bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| bitmapInfo.bmiHeader.biWidth = GetSystemMetrics(SM_CXICON)+1; | |
| bitmapInfo.bmiHeader.biHeight = GetSystemMetrics(SM_CYICON)+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, &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; | |
| void *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 palettizing.\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, &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, 0x00123456, 0x00000000, 0x00000000, __LINE__); | |
| check_DrawIconEx(hdcDst, TRUE, 0x80A0B0C0, 32, DI_MASK, 0x00123456, 0x00FFFFFF, 0x00FFFFFF, __LINE__); | |
| 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__); | |
| 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 check_DrawState_Size(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, UINT flags, int line) | |
| { | |
| COLORREF result, background; | |
| BOOL passed[2]; | |
| HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color)); | |
| background = 0x00FFFFFF; | |
| /* Set color of the 2 pixels that will be checked afterwards */ | |
| SetPixelV(hdc, 0, 0, background); | |
| SetPixelV(hdc, 2, 2, background); | |
| /* Let DrawState calculate the size of the icon (it's 1x1) */ | |
| DrawStateA(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, (DST_ICON | flags )); | |
| result = GetPixel(hdc, 0, 0); | |
| passed[0] = color_match(result, background); | |
| result = GetPixel(hdc, 2, 2); | |
| passed[0] = passed[0] & color_match(result, background); | |
| /* Check if manually specifying the icon size DOESN'T work */ | |
| /* IMPORTANT: For Icons, DrawState wants the size of the source image, not the | |
| * size in which it should be ultimately drawn. Therefore giving | |
| * width/height 2x2 if the icon is only 1x1 pixels in size should | |
| * result in drawing it with size 1x1. The size parameters must be | |
| * ignored if a Icon has to be drawn! */ | |
| DrawStateA(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 2, 2, (DST_ICON | flags )); | |
| result = GetPixel(hdc, 0, 0); | |
| passed[1] = color_match(result, background); | |
| result = GetPixel(hdc, 2, 2); | |
| passed[1] = passed[0] & color_match(result, background); | |
| if(!passed[0]&&!passed[1]) | |
| ok (passed[1], | |
| "DrawState failed to draw a 1x1 Icon in the correct size, independent of the " | |
| "width and height settings passed to it, for Icon with: Overlaying Mask %d on " | |
| "Color %06X with flags %08X. Line %d\n", | |
| maskvalue, color, (DST_ICON | flags), line); | |
| else if(!passed[1]) | |
| ok (passed[1], | |
| "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height " | |
| "parameters passed to it are bigger than the real Icon size, for Icon with: Overlaying " | |
| "Mask %d on Color %06X with flags %08X. Line %d\n", | |
| maskvalue, color, (DST_ICON | flags), line); | |
| else | |
| ok (passed[0], | |
| "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height " | |
| "parameters passed to it are 0, for Icon with: Overlaying Mask %d on " | |
| "Color %06X with flags %08X. Line %d\n", | |
| maskvalue, color, (DST_ICON | flags), line); | |
| } | |
| static void check_DrawState_Color(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, 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; | |
| /* Set color of the pixel that will be checked afterwards */ | |
| SetPixelV(hdc, 1, 1, background); | |
| DrawStateA(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, ( DST_ICON | flags )); | |
| /* Check the color of the pixel is correct */ | |
| result = GetPixel(hdc, 1, 1); | |
| ok (color_match(result, modern_expected) || /* Windows 2000 and up */ | |
| broken(color_match(result, legacy_expected)), /* Windows NT 4.0, 9X and below */ | |
| "DrawState drawing Icon with Overlaying Mask %d on Color %06X with flags %08X. " | |
| "Expected a close match to %06X (modern) or %06X (legacy). Got %06X from line %d\n", | |
| maskvalue, color, (DST_ICON | flags), modern_expected, legacy_expected, result, line); | |
| } | |
| static void test_DrawState(void) | |
| { | |
| BITMAPINFO bitmapInfo; | |
| HDC hdcDst = NULL; | |
| HBITMAP bmpDst = NULL; | |
| HBITMAP bmpOld = NULL; | |
| void *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 palettizing.\n"); | |
| goto cleanup; | |
| } | |
| memset(&bitmapInfo, 0, sizeof(bitmapInfo)); | |
| bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| bitmapInfo.bmiHeader.biWidth = 3; | |
| bitmapInfo.bmiHeader.biHeight = 3; | |
| 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, &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); | |
| /* potential flags to test with DrawState are: */ | |
| /* DSS_DISABLED embosses the icon */ | |
| /* DSS_MONO draw Icon using a brush as parameter 5 */ | |
| /* DSS_NORMAL draw Icon without any modifications */ | |
| /* DSS_UNION draw the Icon dithered */ | |
| check_DrawState_Size(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, __LINE__); | |
| check_DrawState_Color(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__); | |
| cleanup: | |
| if(bmpOld) | |
| SelectObject(hdcDst, bmpOld); | |
| if(bmpDst) | |
| DeleteObject(bmpDst); | |
| if(hdcDst) | |
| DeleteDC(hdcDst); | |
| } | |
| static DWORD parent_id; | |
| static DWORD CALLBACK set_cursor_thread( void *arg ) | |
| { | |
| HCURSOR ret; | |
| PeekMessageA( 0, 0, 0, 0, PM_NOREMOVE ); /* create a msg queue */ | |
| if (parent_id) | |
| { | |
| BOOL ret = AttachThreadInput( GetCurrentThreadId(), parent_id, TRUE ); | |
| ok( ret, "AttachThreadInput failed\n" ); | |
| } | |
| if (arg) ret = SetCursor( (HCURSOR)arg ); | |
| else ret = GetCursor(); | |
| return (DWORD_PTR)ret; | |
| } | |
| static void test_SetCursor(void) | |
| { | |
| static const BYTE bmp_bits[4096]; | |
| ICONINFO cursorInfo; | |
| HCURSOR cursor, old_cursor, global_cursor = 0; | |
| DWORD error, id, result; | |
| UINT display_bpp; | |
| HDC hdc; | |
| HANDLE thread; | |
| CURSORINFO info; | |
| if (pGetCursorInfo) | |
| { | |
| memset( &info, 0, sizeof(info) ); | |
| info.cbSize = sizeof(info); | |
| if (!pGetCursorInfo( &info )) | |
| { | |
| win_skip( "GetCursorInfo not working\n" ); | |
| pGetCursorInfo = NULL; | |
| } | |
| else global_cursor = info.hCursor; | |
| } | |
| cursor = GetCursor(); | |
| thread = CreateThread( NULL, 0, set_cursor_thread, 0, 0, &id ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == (DWORD_PTR)cursor, "wrong thread cursor %x/%p\n", result, cursor ); | |
| hdc = GetDC(0); | |
| display_bpp = GetDeviceCaps(hdc, BITSPIXEL); | |
| ReleaseDC(0, hdc); | |
| cursorInfo.fIcon = FALSE; | |
| cursorInfo.xHotspot = 0; | |
| cursorInfo.yHotspot = 0; | |
| cursorInfo.hbmMask = CreateBitmap(32, 32, 1, 1, bmp_bits); | |
| cursorInfo.hbmColor = CreateBitmap(32, 32, 1, display_bpp, bmp_bits); | |
| cursor = CreateIconIndirect(&cursorInfo); | |
| ok(cursor != NULL, "CreateIconIndirect returned %p\n", cursor); | |
| old_cursor = SetCursor( cursor ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| /* global cursor doesn't change since we don't have a window */ | |
| ok( info.hCursor == global_cursor || broken(info.hCursor != cursor), /* win9x */ | |
| "wrong info cursor %p/%p\n", info.hCursor, global_cursor ); | |
| } | |
| thread = CreateThread( NULL, 0, set_cursor_thread, 0, 0, &id ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == (DWORD_PTR)old_cursor, "wrong thread cursor %x/%p\n", result, old_cursor ); | |
| SetCursor( 0 ); | |
| ok( GetCursor() == 0, "wrong cursor %p\n", GetCursor() ); | |
| thread = CreateThread( NULL, 0, set_cursor_thread, 0, 0, &id ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == (DWORD_PTR)old_cursor, "wrong thread cursor %x/%p\n", result, old_cursor ); | |
| thread = CreateThread( NULL, 0, set_cursor_thread, cursor, 0, &id ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == (DWORD_PTR)old_cursor, "wrong thread cursor %x/%p\n", result, old_cursor ); | |
| ok( GetCursor() == 0, "wrong cursor %p/0\n", GetCursor() ); | |
| parent_id = GetCurrentThreadId(); | |
| thread = CreateThread( NULL, 0, set_cursor_thread, cursor, 0, &id ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == (DWORD_PTR)old_cursor, "wrong thread cursor %x/%p\n", result, old_cursor ); | |
| ok( GetCursor() == cursor, "wrong cursor %p/0\n", cursor ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| ok( info.hCursor == global_cursor || broken(info.hCursor != cursor), /* win9x */ | |
| "wrong info cursor %p/%p\n", info.hCursor, global_cursor ); | |
| } | |
| SetCursor( old_cursor ); | |
| DestroyCursor( cursor ); | |
| SetLastError( 0xdeadbeef ); | |
| cursor = SetCursor( (HCURSOR)0xbadbad ); | |
| error = GetLastError(); | |
| ok( cursor == 0, "wrong cursor %p/0\n", cursor ); | |
| ok( error == ERROR_INVALID_CURSOR_HANDLE || broken( error == 0xdeadbeef ), /* win9x */ | |
| "wrong error %u\n", error ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| ok( info.hCursor == global_cursor || broken(info.hCursor != cursor), /* win9x */ | |
| "wrong info cursor %p/%p\n", info.hCursor, global_cursor ); | |
| } | |
| } | |
| static HANDLE event_start, event_next; | |
| static DWORD CALLBACK show_cursor_thread( void *arg ) | |
| { | |
| DWORD count = (DWORD_PTR)arg; | |
| int ret; | |
| PeekMessageA( 0, 0, 0, 0, PM_NOREMOVE ); /* create a msg queue */ | |
| if (parent_id) | |
| { | |
| BOOL ret = AttachThreadInput( GetCurrentThreadId(), parent_id, TRUE ); | |
| ok( ret, "AttachThreadInput failed\n" ); | |
| } | |
| if (!count) ret = ShowCursor( FALSE ); | |
| else while (count--) ret = ShowCursor( TRUE ); | |
| SetEvent( event_start ); | |
| WaitForSingleObject( event_next, 2000 ); | |
| return ret; | |
| } | |
| static void test_ShowCursor(void) | |
| { | |
| int count; | |
| DWORD id, result; | |
| HANDLE thread; | |
| CURSORINFO info; | |
| if (pGetCursorInfo) | |
| { | |
| memset( &info, 0, sizeof(info) ); | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| ok( info.flags & CURSOR_SHOWING, "cursor not shown in info\n" ); | |
| } | |
| event_start = CreateEventW( NULL, FALSE, FALSE, NULL ); | |
| event_next = CreateEventW( NULL, FALSE, FALSE, NULL ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == 1, "wrong count %d\n", count ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == 2, "wrong count %d\n", count ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == 1, "wrong count %d\n", count ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == 0, "wrong count %d\n", count ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -1, "wrong count %d\n", count ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -2, "wrong count %d\n", count ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| /* global show count is not affected since we don't have a window */ | |
| ok( info.flags & CURSOR_SHOWING, "cursor not shown in info\n" ); | |
| } | |
| parent_id = 0; | |
| thread = CreateThread( NULL, 0, show_cursor_thread, NULL, 0, &id ); | |
| WaitForSingleObject( event_start, 1000 ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -3, "wrong count %d\n", count ); | |
| SetEvent( event_next ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == -1, "wrong thread count %d\n", result ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -4, "wrong count %d\n", count ); | |
| thread = CreateThread( NULL, 0, show_cursor_thread, (void *)1, 0, &id ); | |
| WaitForSingleObject( event_start, 1000 ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == -3, "wrong count %d\n", count ); | |
| SetEvent( event_next ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == 1, "wrong thread count %d\n", result ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == -2, "wrong count %d\n", count ); | |
| parent_id = GetCurrentThreadId(); | |
| thread = CreateThread( NULL, 0, show_cursor_thread, NULL, 0, &id ); | |
| WaitForSingleObject( event_start, 1000 ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == -2, "wrong count %d\n", count ); | |
| SetEvent( event_next ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == -3, "wrong thread count %d\n", result ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -2, "wrong count %d\n", count ); | |
| thread = CreateThread( NULL, 0, show_cursor_thread, (void *)3, 0, &id ); | |
| WaitForSingleObject( event_start, 1000 ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == 2, "wrong count %d\n", count ); | |
| SetEvent( event_next ); | |
| WaitForSingleObject( thread, 1000 ); | |
| GetExitCodeThread( thread, &result ); | |
| ok( result == 1, "wrong thread count %d\n", result ); | |
| count = ShowCursor( FALSE ); | |
| ok( count == -2, "wrong count %d\n", count ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| ok( info.flags & CURSOR_SHOWING, "cursor not shown in info\n" ); | |
| } | |
| count = ShowCursor( TRUE ); | |
| ok( count == -1, "wrong count %d\n", count ); | |
| count = ShowCursor( TRUE ); | |
| ok( count == 0, "wrong count %d\n", count ); | |
| if (pGetCursorInfo) | |
| { | |
| info.cbSize = sizeof(info); | |
| ok( pGetCursorInfo( &info ), "GetCursorInfo failed\n" ); | |
| ok( info.flags & CURSOR_SHOWING, "cursor not shown in info\n" ); | |
| } | |
| } | |
| static void test_DestroyCursor(void) | |
| { | |
| static const BYTE bmp_bits[4096]; | |
| ICONINFO cursorInfo, new_info; | |
| HCURSOR cursor, cursor2, new_cursor; | |
| BOOL ret; | |
| DWORD error; | |
| UINT display_bpp; | |
| HDC hdc; | |
| hdc = GetDC(0); | |
| display_bpp = GetDeviceCaps(hdc, BITSPIXEL); | |
| ReleaseDC(0, hdc); | |
| cursorInfo.fIcon = FALSE; | |
| cursorInfo.xHotspot = 0; | |
| cursorInfo.yHotspot = 0; | |
| cursorInfo.hbmMask = CreateBitmap(32, 32, 1, 1, bmp_bits); | |
| cursorInfo.hbmColor = CreateBitmap(32, 32, 1, display_bpp, bmp_bits); | |
| cursor = CreateIconIndirect(&cursorInfo); | |
| ok(cursor != NULL, "CreateIconIndirect returned %p\n", cursor); | |
| if(!cursor) { | |
| return; | |
| } | |
| SetCursor(cursor); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(cursor); | |
| ok(!ret || broken(ret) /* succeeds on win9x */, "DestroyCursor on the active cursor succeeded\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: %u\n", error); | |
| new_cursor = GetCursor(); | |
| if (ret) /* win9x replaces cursor by another one on destroy */ | |
| ok(new_cursor != cursor, "GetCursor returned %p/%p\n", new_cursor, cursor); | |
| else | |
| ok(new_cursor == cursor, "GetCursor returned %p/%p\n", new_cursor, cursor); | |
| SetLastError(0xdeadbeef); | |
| ret = GetIconInfo( cursor, &new_info ); | |
| ok( !ret || broken(ret), /* nt4 */ "GetIconInfo succeeded\n" ); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || | |
| broken(GetLastError() == 0xdeadbeef), /* win9x */ | |
| "wrong error %u\n", GetLastError() ); | |
| if (ret) /* nt4 delays destruction until cursor changes */ | |
| { | |
| DeleteObject( new_info.hbmColor ); | |
| DeleteObject( new_info.hbmMask ); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor( cursor ); | |
| ok( !ret, "DestroyCursor succeeded\n" ); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || GetLastError() == 0xdeadbeef, | |
| "wrong error %u\n", GetLastError() ); | |
| SetLastError(0xdeadbeef); | |
| cursor2 = SetCursor( cursor ); | |
| ok( cursor2 == cursor, "SetCursor returned %p/%p\n", cursor2, cursor); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || GetLastError() == 0xdeadbeef, | |
| "wrong error %u\n", GetLastError() ); | |
| } | |
| else | |
| { | |
| SetLastError(0xdeadbeef); | |
| cursor2 = CopyCursor( cursor ); | |
| ok(!cursor2, "CopyCursor succeeded\n" ); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || | |
| broken(GetLastError() == 0xdeadbeef), /* win9x */ | |
| "wrong error %u\n", GetLastError() ); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor( cursor ); | |
| if (new_cursor != cursor) /* win9x */ | |
| ok( ret, "DestroyCursor succeeded\n" ); | |
| else | |
| ok( !ret, "DestroyCursor succeeded\n" ); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || GetLastError() == 0xdeadbeef, | |
| "wrong error %u\n", GetLastError() ); | |
| SetLastError(0xdeadbeef); | |
| cursor2 = SetCursor( cursor ); | |
| ok(!cursor2, "SetCursor returned %p/%p\n", cursor2, cursor); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || GetLastError() == 0xdeadbeef, | |
| "wrong error %u\n", GetLastError() ); | |
| } | |
| cursor2 = GetCursor(); | |
| ok(cursor2 == new_cursor, "GetCursor returned %p/%p\n", cursor2, new_cursor); | |
| SetLastError(0xdeadbeef); | |
| cursor2 = SetCursor( 0 ); | |
| if (new_cursor != cursor) /* win9x */ | |
| ok(cursor2 == new_cursor, "SetCursor returned %p/%p\n", cursor2, cursor); | |
| else | |
| ok(!cursor2, "SetCursor returned %p/%p\n", cursor2, cursor); | |
| ok( GetLastError() == 0xdeadbeef, "wrong error %u\n", GetLastError() ); | |
| cursor2 = GetCursor(); | |
| ok(!cursor2, "GetCursor returned %p/%p\n", cursor2, cursor); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(cursor); | |
| if (new_cursor != cursor) /* win9x */ | |
| ok( ret, "DestroyCursor succeeded\n" ); | |
| else | |
| ok( !ret, "DestroyCursor succeeded\n" ); | |
| ok( GetLastError() == ERROR_INVALID_CURSOR_HANDLE || GetLastError() == 0xdeadbeef, | |
| "wrong error %u\n", GetLastError() ); | |
| DeleteObject(cursorInfo.hbmMask); | |
| DeleteObject(cursorInfo.hbmColor); | |
| /* Try testing DestroyCursor() now using LoadCursor() cursors. */ | |
| cursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); | |
| SetLastError(0xdeadbeef); | |
| ret = DestroyCursor(cursor); | |
| ok(ret || broken(!ret) /* fails on win9x */, "DestroyCursor on the active cursor failed.\n"); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); | |
| /* Try setting the cursor to a destroyed OEM cursor. */ | |
| SetLastError(0xdeadbeef); | |
| SetCursor(cursor); | |
| error = GetLastError(); | |
| ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); | |
| /* Check if LoadCursor() returns the same handle with the same icon. */ | |
| cursor2 = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); | |
| ok(cursor2 == cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2); | |
| /* Check if LoadCursor() returns the same handle with a different icon. */ | |
| cursor2 = LoadCursorA(NULL, (LPCSTR)IDC_WAIT); | |
| ok(cursor2 != cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2); | |
| } | |
| static void test_PrivateExtractIcons(void) | |
| { | |
| HICON icon; | |
| UINT ret; | |
| static const test_icon_entries_t icon_desc[] = {{0,0,TRUE}, {16,16,TRUE}, {32,32}, {64,64,TRUE}}; | |
| create_ico_file("extract.ico", icon_desc, sizeof(icon_desc)/sizeof(*icon_desc)); | |
| ret = PrivateExtractIconsA("extract.ico", 0, 32, 32, &icon, NULL, 1, 0); | |
| ok(ret == 1, "PrivateExtractIconsA returned %u\n", ret); | |
| ok(icon != NULL, "icon == NULL\n"); | |
| test_icon_info(icon, 32, 32, 32, 32); | |
| DestroyIcon(icon); | |
| DeleteFileA("extract.ico"); | |
| } | |
| static void test_monochrome_icon(void) | |
| { | |
| HANDLE handle; | |
| BOOL ret; | |
| DWORD bytes_written; | |
| CURSORICONFILEDIR *icon_data; | |
| CURSORICONFILEDIRENTRY *icon_entry; | |
| BITMAPINFO *bitmap_info; | |
| BITMAPCOREINFO *core_info; | |
| ICONINFO icon_info; | |
| ULONG icon_size; | |
| BOOL monochrome, use_core_info; | |
| icon_data = HeapAlloc(GetProcessHeap(), 0, sizeof(CURSORICONFILEDIR) + sizeof(BITMAPINFOHEADER) + | |
| 2 * sizeof(RGBQUAD) + sizeof(ULONG)); | |
| for (monochrome = FALSE; monochrome <= TRUE; monochrome++) | |
| for (use_core_info = FALSE; use_core_info <= TRUE; use_core_info++) | |
| { | |
| trace("%s, %s\n", | |
| monochrome ? "monochrome" : "colored", | |
| use_core_info ? "core info" : "bitmap info"); | |
| icon_size = sizeof(CURSORICONFILEDIR) + | |
| (use_core_info ? sizeof(BITMAPCOREHEADER) : sizeof(BITMAPINFOHEADER)) + | |
| /* 2 * sizeof(RGBTRIPLE) + padding comes out the same */ | |
| 2 * sizeof(RGBQUAD) + | |
| sizeof(ULONG); | |
| ZeroMemory(icon_data, icon_size); | |
| icon_data->idReserved = 0; | |
| icon_data->idType = 1; | |
| icon_data->idCount = 1; | |
| icon_entry = icon_data->idEntries; | |
| icon_entry->bWidth = 1; | |
| icon_entry->bHeight = 1; | |
| icon_entry->bColorCount = 0; | |
| icon_entry->bReserved = 0; | |
| icon_entry->xHotspot = 0; | |
| icon_entry->yHotspot = 0; | |
| icon_entry->dwDIBSize = icon_size - sizeof(CURSORICONFILEDIR); | |
| icon_entry->dwDIBOffset = sizeof(CURSORICONFILEDIR); | |
| if (use_core_info) | |
| { | |
| core_info = (BITMAPCOREINFO *) ((BYTE *) icon_data + icon_entry->dwDIBOffset); | |
| core_info->bmciHeader.bcSize = sizeof(BITMAPCOREHEADER); | |
| core_info->bmciHeader.bcWidth = 1; | |
| core_info->bmciHeader.bcHeight = 2; | |
| core_info->bmciHeader.bcPlanes = 1; | |
| core_info->bmciHeader.bcBitCount = 1; | |
| core_info->bmciColors[0].rgbtBlue = monochrome ? 0x00 : 0xff; | |
| core_info->bmciColors[0].rgbtGreen = 0x00; | |
| core_info->bmciColors[0].rgbtRed = 0x00; | |
| core_info->bmciColors[1].rgbtBlue = 0xff; | |
| core_info->bmciColors[1].rgbtGreen = 0xff; | |
| core_info->bmciColors[1].rgbtRed = 0xff; | |
| } | |
| else | |
| { | |
| bitmap_info = (BITMAPINFO *) ((BYTE *) icon_data + icon_entry->dwDIBOffset); | |
| bitmap_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| bitmap_info->bmiHeader.biWidth = 1; | |
| bitmap_info->bmiHeader.biHeight = 2; | |
| bitmap_info->bmiHeader.biPlanes = 1; | |
| bitmap_info->bmiHeader.biBitCount = 1; | |
| bitmap_info->bmiHeader.biSizeImage = 0; /* Uncompressed bitmap. */ | |
| bitmap_info->bmiColors[0].rgbBlue = monochrome ? 0x00 : 0xff; | |
| bitmap_info->bmiColors[0].rgbGreen = 0x00; | |
| bitmap_info->bmiColors[0].rgbRed = 0x00; | |
| bitmap_info->bmiColors[1].rgbBlue = 0xff; | |
| bitmap_info->bmiColors[1].rgbGreen = 0xff; | |
| bitmap_info->bmiColors[1].rgbRed = 0xff; | |
| } | |
| handle = CreateFileA("icon.ico", GENERIC_WRITE, 0, NULL, CREATE_NEW, | |
| FILE_ATTRIBUTE_NORMAL, NULL); | |
| ok(handle != INVALID_HANDLE_VALUE, "CreateFileA failed. %u\n", GetLastError()); | |
| ret = WriteFile(handle, icon_data, icon_size, &bytes_written, NULL); | |
| ok(ret && bytes_written == icon_size, "icon.ico created improperly.\n"); | |
| CloseHandle(handle); | |
| handle = LoadImageA(NULL, "icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE); | |
| ok(handle != NULL || | |
| broken(use_core_info && handle == NULL), /* Win 8, 10 */ | |
| "LoadImage() failed with %u.\n", GetLastError()); | |
| if (handle == NULL) | |
| { | |
| skip("Icon failed to load: %s, %s\n", | |
| monochrome ? "monochrome" : "colored", | |
| use_core_info ? "core info" : "bitmap info"); | |
| DeleteFileA("icon.ico"); | |
| continue; | |
| } | |
| ret = GetIconInfo(handle, &icon_info); | |
| ok(ret, "GetIconInfo() failed with %u.\n", GetLastError()); | |
| if (ret) | |
| { | |
| ok(icon_info.fIcon == TRUE, "fIcon is %u.\n", icon_info.fIcon); | |
| ok(icon_info.xHotspot == 0, "xHotspot is %u.\n", icon_info.xHotspot); | |
| ok(icon_info.yHotspot == 0, "yHotspot is %u.\n", icon_info.yHotspot); | |
| if (monochrome) | |
| ok(icon_info.hbmColor == NULL, "Got hbmColor %p!\n", icon_info.hbmColor); | |
| else | |
| ok(icon_info.hbmColor != NULL, "No hbmColor!\n"); | |
| ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); | |
| } | |
| ret = DestroyIcon(handle); | |
| ok(ret, "DestroyIcon() failed with %u.\n", GetLastError()); | |
| DeleteFileA("icon.ico"); | |
| } | |
| HeapFree(GetProcessHeap(), 0, icon_data); | |
| } | |
| START_TEST(cursoricon) | |
| { | |
| pGetCursorInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorInfo" ); | |
| pGetIconInfoExA = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetIconInfoExA" ); | |
| pGetIconInfoExW = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetIconInfoExW" ); | |
| pGetCursorFrameInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorFrameInfo" ); | |
| test_argc = winetest_get_mainargs(&test_argv); | |
| if (test_argc >= 3) | |
| { | |
| /* Child process. */ | |
| sscanf (test_argv[2], "%x", (unsigned int *) &parent); | |
| ok(parent != NULL, "Parent not found.\n"); | |
| if (parent == NULL) | |
| ExitProcess(1); | |
| do_child(); | |
| return; | |
| } | |
| test_CopyImage_Bitmap(1); | |
| test_CopyImage_Bitmap(4); | |
| test_CopyImage_Bitmap(8); | |
| test_CopyImage_Bitmap(16); | |
| test_CopyImage_Bitmap(24); | |
| test_CopyImage_Bitmap(32); | |
| test_initial_cursor(); | |
| test_CreateIcon(); | |
| test_LoadImage(); | |
| test_CreateIconFromResource(); | |
| test_GetCursorFrameInfo(); | |
| test_DrawIcon(); | |
| test_DrawIconEx(); | |
| test_DrawState(); | |
| test_SetCursor(); | |
| test_ShowCursor(); | |
| test_DestroyCursor(); | |
| test_PrivateExtractIcons(); | |
| test_monochrome_icon(); | |
| do_parent(); | |
| test_child_process(); | |
| finish_child_process(); | |
| } |