blob: 781282c279a06b9edbe6c50b889911a74f19495e [file] [log] [blame]
/*
* Unit test suite for cursors and icons.
*
* Copyright 2006 Michael Kaufmann
* Copyright 2007 Dmitry Timoshkov
* Copyright 2007 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 <assert.h>
#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"
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 = (HBITMAP) CopyImage(bitmap, IMAGE_BITMAP, copyWidth, copyHeight, flags);
ok(copy != NULL, "CopyImage() failed\n");
if (copy != NULL)
{
GetObject(bitmap, sizeof(origBitmap), &origBitmap);
GetObject(copy, sizeof(copyBitmap), &copyBitmap);
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 = LoadCursor(NULL, 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_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 = GetObject(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 = GetObject(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_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_cy * 2, "bmMask.bmHeight = %d\n", bmMask.bmHeight);
}
}
#define test_icon_info(a,b,c,d) test_icon_info_dbg((a),(b),(c),(d),__LINE__)
static void test_CreateIcon(void)
{
static const BYTE bmp_bits[1024];
HICON hIcon;
HBITMAP hbmMask, hbmColor;
ICONINFO info;
HDC hdc;
UINT display_bpp;
hdc = GetDC(0);
display_bpp = GetDeviceCaps(hdc, BITSPIXEL);
ReleaseDC(0, hdc);
/* 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, 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, 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, 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, 1);
DestroyIcon(hIcon);
DeleteObject(hbmMask);
DeleteObject(hbmColor);
}
static void test_DestroyCursor(void)
{
static const BYTE bmp_bits[4096];
ICONINFO cursorInfo;
HCURSOR cursor, cursor2;
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, "DestroyCursor on the active cursor succeeded\n");
error = GetLastError();
ok(error == 0xdeadbeef, "Last error: %u\n", error);
cursor2 = GetCursor();
ok(cursor2 == cursor, "Active was set to %p when trying to destroy it\n", cursor2);
SetCursor(NULL);
/* Trying to destroy the cursor properly fails now with
* ERROR_INVALID_CURSOR_HANDLE. This happens because we called
* DestroyCursor() 2+ times after calling SetCursor(). The calls to
* GetCursor() and SetCursor(NULL) in between make no difference. */
ret = DestroyCursor(cursor);
todo_wine {
ok(!ret, "DestroyCursor succeeded.\n");
error = GetLastError();
ok(error == ERROR_INVALID_CURSOR_HANDLE, "Last error: 0x%08x\n", error);
}
DeleteObject(cursorInfo.hbmMask);
DeleteObject(cursorInfo.hbmColor);
/* Try testing DestroyCursor() now using LoadCursor() cursors. */
cursor = LoadCursor(NULL, IDC_ARROW);
SetLastError(0xdeadbeef);
ret = DestroyCursor(cursor);
ok(ret, "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();
todo_wine {
ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error);
}
/* Check if LoadCursor() returns the same handle with the same icon. */
cursor2 = LoadCursor(NULL, IDC_ARROW);
ok(cursor2 == cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2);
/* Check if LoadCursor() returns the same handle with a different icon. */
cursor2 = LoadCursor(NULL, IDC_WAIT);
ok(cursor2 != cursor, "cursor == %p, cursor2 == %p\n", cursor, cursor2);
}
START_TEST(cursoricon)
{
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_DestroyCursor();
}