mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 19:02:32 +00:00
1511 lines
38 KiB
C
1511 lines
38 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* PURPOSE: Cursor and icon functions
|
|
* FILE: subsystems/win32/win32k/ntuser/cursoricon.c
|
|
* PROGRAMER: ReactOS Team
|
|
*/
|
|
/*
|
|
* We handle two types of cursors/icons:
|
|
* - Private
|
|
* Loaded without LR_SHARED flag
|
|
* Private to a process
|
|
* Can be deleted by calling NtDestroyCursorIcon()
|
|
* CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc set to NULL
|
|
* - Shared
|
|
* Loaded with LR_SHARED flag
|
|
* Possibly shared by multiple processes
|
|
* Immune to NtDestroyCursorIcon()
|
|
* CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid
|
|
* There's a M:N relationship between processes and (shared) cursor/icons.
|
|
* A process can have multiple cursor/icons and a cursor/icon can be used
|
|
* by multiple processes. To keep track of this we keep a list of all
|
|
* cursor/icons (CurIconList) and per cursor/icon we keep a list of
|
|
* CURICON_PROCESS structs starting at CurIcon->ProcessList.
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
DBG_DEFAULT_CHANNEL(UserIcon);
|
|
|
|
static PPAGED_LOOKASIDE_LIST pgProcessLookasideList;
|
|
static LIST_ENTRY gCurIconList;
|
|
|
|
SYSTEM_CURSORINFO gSysCursorInfo;
|
|
|
|
BOOL
|
|
InitCursorImpl()
|
|
{
|
|
pgProcessLookasideList = ExAllocatePool(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST));
|
|
if(!pgProcessLookasideList)
|
|
return FALSE;
|
|
|
|
ExInitializePagedLookasideList(pgProcessLookasideList,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof(CURICON_PROCESS),
|
|
TAG_DIB,
|
|
128);
|
|
InitializeListHead(&gCurIconList);
|
|
|
|
gSysCursorInfo.Enabled = FALSE;
|
|
gSysCursorInfo.ButtonsDown = 0;
|
|
gSysCursorInfo.bClipped = FALSE;
|
|
gSysCursorInfo.LastBtnDown = 0;
|
|
gSysCursorInfo.CurrentCursorObject = NULL;
|
|
gSysCursorInfo.ShowingCursor = -1;
|
|
gSysCursorInfo.ClickLockActive = FALSE;
|
|
gSysCursorInfo.ClickLockTime = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PSYSTEM_CURSORINFO
|
|
IntGetSysCursorInfo()
|
|
{
|
|
return &gSysCursorInfo;
|
|
}
|
|
|
|
/* This function creates a reference for the object! */
|
|
PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
|
|
if (!hCurIcon)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, otCursorIcon);
|
|
if (!CurIcon)
|
|
{
|
|
/* We never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(CurIcon->head.cLockObj >= 1);
|
|
return CurIcon;
|
|
}
|
|
|
|
BOOL UserSetCursorPos( INT x, INT y, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook)
|
|
{
|
|
PWND DesktopWindow;
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
MSG Msg;
|
|
RECTL rcClip;
|
|
POINT pt;
|
|
|
|
if(!(DesktopWindow = UserGetDesktopWindow()))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
|
|
/* Clip cursor position */
|
|
if (!CurInfo->bClipped)
|
|
rcClip = DesktopWindow->rcClient;
|
|
else
|
|
rcClip = CurInfo->rcClip;
|
|
|
|
if(x >= rcClip.right) x = rcClip.right - 1;
|
|
if(x < rcClip.left) x = rcClip.left;
|
|
if(y >= rcClip.bottom) y = rcClip.bottom - 1;
|
|
if(y < rcClip.top) y = rcClip.top;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
/* 1. Generate a mouse move message, this sets the htEx and Track Window too. */
|
|
Msg.message = WM_MOUSEMOVE;
|
|
Msg.wParam = UserGetMouseButtonsState();
|
|
Msg.lParam = MAKELPARAM(x, y);
|
|
Msg.pt = pt;
|
|
co_MsqInsertMouseMessage(&Msg, flags, dwExtraInfo, Hook);
|
|
|
|
/* 2. Store the new cursor position */
|
|
gpsi->ptCursor = pt;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* We have to register that this object is in use by the current
|
|
* process. The only way to do that seems to be to walk the list
|
|
* of cursor/icon objects starting at W32Process->CursorIconListHead.
|
|
* If the object is already present in the list, we don't have to do
|
|
* anything, if it's not present we add it and inc the ProcessCount
|
|
* in the object. Having to walk the list kind of sucks, but that's
|
|
* life...
|
|
*/
|
|
static BOOLEAN FASTCALL
|
|
ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
|
|
{
|
|
PPROCESSINFO Win32Process;
|
|
PCURICON_PROCESS Current;
|
|
|
|
Win32Process = PsGetCurrentProcessWin32Process();
|
|
|
|
LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
|
|
{
|
|
if (Current->Process == Win32Process)
|
|
{
|
|
/* Already registered for this process */
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Not registered yet */
|
|
Current = ExAllocateFromPagedLookasideList(pgProcessLookasideList);
|
|
if (NULL == Current)
|
|
{
|
|
return FALSE;
|
|
}
|
|
InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
|
|
Current->Process = Win32Process;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCURICON_OBJECT FASTCALL
|
|
IntFindExistingCurIconObject(HMODULE hModule,
|
|
HRSRC hRsrc, LONG cx, LONG cy)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
|
|
LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
|
|
{
|
|
|
|
// if (NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon))) // <- huh????
|
|
// UserReferenceObject( CurIcon);
|
|
// {
|
|
if ((CurIcon->hModule == hModule) && (CurIcon->hRsrc == hRsrc))
|
|
{
|
|
if (cx && ((cx != CurIcon->Size.cx) || (cy != CurIcon->Size.cy)))
|
|
{
|
|
// UserDereferenceObject(CurIcon);
|
|
continue;
|
|
}
|
|
if (! ReferenceCurIconByProcess(CurIcon))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return CurIcon;
|
|
}
|
|
// }
|
|
// UserDereferenceObject(CurIcon);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PCURICON_OBJECT
|
|
IntCreateCurIconHandle(DWORD dwNumber)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
HANDLE hCurIcon;
|
|
|
|
CurIcon = UserCreateObject(gHandleTable, NULL, NULL, &hCurIcon, otCursorIcon, sizeof(CURICON_OBJECT));
|
|
|
|
if (!CurIcon)
|
|
{
|
|
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
CurIcon->Self = hCurIcon;
|
|
InitializeListHead(&CurIcon->ProcessList);
|
|
|
|
if (! ReferenceCurIconByProcess(CurIcon))
|
|
{
|
|
ERR("Failed to add process\n");
|
|
UserDeleteObject(hCurIcon, otCursorIcon);
|
|
UserDereferenceObject(CurIcon);
|
|
return NULL;
|
|
}
|
|
|
|
InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
|
|
|
|
return CurIcon;
|
|
}
|
|
|
|
BOOLEAN FASTCALL
|
|
IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, PPROCESSINFO ppi)
|
|
{
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
HBITMAP bmpMask, bmpColor;
|
|
BOOLEAN Ret, bListEmpty, bFound = FALSE;
|
|
PCURICON_PROCESS Current = NULL;
|
|
|
|
/* For handles created without any data (error handling) */
|
|
if(IsListEmpty(&CurIcon->ProcessList))
|
|
goto emptyList;
|
|
|
|
/* Now find this process in the list of processes referencing this object and
|
|
remove it from that list */
|
|
LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
|
|
{
|
|
if (Current->Process == ppi)
|
|
{
|
|
bFound = TRUE;
|
|
bListEmpty = RemoveEntryList(&Current->ListEntry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!bFound)
|
|
{
|
|
/* This object doesn't belong to this process */
|
|
EngSetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
ExFreeToPagedLookasideList(pgProcessLookasideList, Current);
|
|
|
|
/* If there are still processes referencing this object we can't destroy it yet */
|
|
if (!bListEmpty)
|
|
{
|
|
if(CurIcon->head.ppi == ppi)
|
|
{
|
|
/* Set the first process of the list as owner */
|
|
Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry);
|
|
UserSetObjectOwner(CurIcon, otCursorIcon, Current->Process);
|
|
}
|
|
UserDereferenceObject(CurIcon);
|
|
return TRUE;
|
|
}
|
|
|
|
emptyList:
|
|
/* Remove it from the list */
|
|
RemoveEntryList(&CurIcon->ListEntry);
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
|
|
if (CurInfo->CurrentCursorObject == CurIcon)
|
|
{
|
|
/* Hide the cursor if we're destroying the current cursor */
|
|
UserSetCursor(NULL, TRUE);
|
|
}
|
|
|
|
bmpMask = CurIcon->IconInfo.hbmMask;
|
|
bmpColor = CurIcon->IconInfo.hbmColor;
|
|
|
|
/* Delete bitmaps */
|
|
if (bmpMask)
|
|
{
|
|
GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
|
|
GreDeleteObject(bmpMask);
|
|
CurIcon->IconInfo.hbmMask = NULL;
|
|
}
|
|
if (bmpColor)
|
|
{
|
|
GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
|
|
GreDeleteObject(bmpColor);
|
|
CurIcon->IconInfo.hbmColor = NULL;
|
|
}
|
|
|
|
/* We were given a pointer, no need to keep the reference anylonger! */
|
|
UserDereferenceObject(CurIcon);
|
|
Ret = UserDeleteObject(CurIcon->Self, otCursorIcon);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
VOID FASTCALL
|
|
IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
|
|
{
|
|
PCURICON_OBJECT CurIcon, tmp;
|
|
|
|
/* Run through the list of icon objects */
|
|
LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry)
|
|
{
|
|
UserReferenceObject(CurIcon);
|
|
IntDestroyCurIconObject(CurIcon, Win32Process);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetIconInfo(
|
|
HANDLE hCurIcon,
|
|
PICONINFO IconInfo,
|
|
PUNICODE_STRING lpInstName, // Optional
|
|
PUNICODE_STRING lpResName, // Optional
|
|
LPDWORD pbpp, // Optional
|
|
BOOL bInternal)
|
|
{
|
|
ICONINFO ii;
|
|
PCURICON_OBJECT CurIcon;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOL Ret = FALSE;
|
|
DWORD colorBpp = 0;
|
|
|
|
TRACE("Enter NtUserGetIconInfo\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!IconInfo)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
goto leave;
|
|
}
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
|
|
{
|
|
goto leave;
|
|
}
|
|
|
|
RtlCopyMemory(&ii, &CurIcon->IconInfo, sizeof(ICONINFO));
|
|
|
|
/* Copy bitmaps */
|
|
ii.hbmMask = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmMask);
|
|
ii.hbmColor = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmColor);
|
|
|
|
if (pbpp)
|
|
{
|
|
PSURFACE psurfBmp;
|
|
|
|
psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmColor);
|
|
if (psurfBmp)
|
|
{
|
|
colorBpp = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
|
|
SURFACE_ShareUnlockSurface(psurfBmp);
|
|
}
|
|
}
|
|
|
|
/* Copy fields */
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(IconInfo, sizeof(ICONINFO), 1);
|
|
RtlCopyMemory(IconInfo, &ii, sizeof(ICONINFO));
|
|
|
|
if (pbpp)
|
|
{
|
|
ProbeForWrite(pbpp, sizeof(DWORD), 1);
|
|
*pbpp = colorBpp;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (NT_SUCCESS(Status))
|
|
Ret = TRUE;
|
|
else
|
|
SetLastNtError(Status);
|
|
|
|
UserDereferenceObject(CurIcon);
|
|
|
|
leave:
|
|
TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret);
|
|
UserLeave();
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetIconSize(
|
|
HANDLE hCurIcon,
|
|
UINT istepIfAniCur,
|
|
PLONG plcx, // &size.cx
|
|
PLONG plcy) // &size.cy
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("Enter NtUserGetIconSize\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForWrite(plcx, sizeof(LONG), 1);
|
|
RtlCopyMemory(plcx, &CurIcon->Size.cx, sizeof(LONG));
|
|
ProbeForWrite(plcy, sizeof(LONG), 1);
|
|
RtlCopyMemory(plcy, &CurIcon->Size.cy, sizeof(LONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (NT_SUCCESS(Status))
|
|
bRet = TRUE;
|
|
else
|
|
SetLastNtError(Status); // Maybe not, test this
|
|
|
|
UserDereferenceObject(CurIcon);
|
|
|
|
cleanup:
|
|
TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet);
|
|
UserLeave();
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetCursorInfo(
|
|
PCURSORINFO pci)
|
|
{
|
|
CURSORINFO SafeCi;
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PCURICON_OBJECT CurIcon;
|
|
BOOL Ret = FALSE;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserGetCursorInfo\n");
|
|
UserEnterExclusive();
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
|
|
|
|
SafeCi.cbSize = sizeof(CURSORINFO);
|
|
SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0);
|
|
SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
|
|
|
|
SafeCi.ptScreenPos = gpsi->ptCursor;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
if (pci->cbSize == sizeof(CURSORINFO))
|
|
{
|
|
ProbeForWrite(pci, sizeof(CURSORINFO), 1);
|
|
RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
|
|
Ret = TRUE;
|
|
}
|
|
else
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
}
|
|
|
|
RETURN(Ret);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
UserClipCursor(
|
|
RECTL *prcl)
|
|
{
|
|
/* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
PWND DesktopWindow = NULL;
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
|
|
DesktopWindow = UserGetDesktopWindow();
|
|
|
|
if (prcl != NULL && DesktopWindow != NULL)
|
|
{
|
|
if (prcl->right < prcl->left || prcl->bottom < prcl->top)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
CurInfo->bClipped = TRUE;
|
|
|
|
/* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
|
|
it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
|
|
CurInfo->rcClip.left = max(prcl->left, DesktopWindow->rcWindow.left);
|
|
CurInfo->rcClip.right = min(prcl->right, DesktopWindow->rcWindow.right);
|
|
if (CurInfo->rcClip.right < CurInfo->rcClip.left)
|
|
CurInfo->rcClip.right = CurInfo->rcClip.left;
|
|
|
|
CurInfo->rcClip.top = max(prcl->top, DesktopWindow->rcWindow.top);
|
|
CurInfo->rcClip.bottom = min(prcl->bottom, DesktopWindow->rcWindow.bottom);
|
|
if (CurInfo->rcClip.bottom < CurInfo->rcClip.top)
|
|
CurInfo->rcClip.bottom = CurInfo->rcClip.top;
|
|
|
|
/* Make sure cursor is in clipping region */
|
|
UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, 0, 0, FALSE);
|
|
}
|
|
else
|
|
{
|
|
CurInfo->bClipped = FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserClipCursor(
|
|
RECTL *prcl)
|
|
{
|
|
RECTL rclLocal;
|
|
BOOL bResult;
|
|
|
|
if (prcl)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe and copy rect */
|
|
ProbeForRead(prcl, sizeof(RECTL), 1);
|
|
rclLocal = *prcl;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
_SEH2_YIELD(return FALSE;)
|
|
}
|
|
_SEH2_END
|
|
|
|
prcl = &rclLocal;
|
|
}
|
|
|
|
UserEnterExclusive();
|
|
|
|
/* Call the internal function */
|
|
bResult = UserClipCursor(prcl);
|
|
|
|
UserLeave();
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserDestroyCursor(
|
|
_In_ HANDLE hCurIcon,
|
|
_In_ BOOL bForce)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
BOOL ret;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserDestroyCursorIcon\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process());
|
|
/* Note: IntDestroyCurIconObject will remove our reference for us! */
|
|
|
|
RETURN(ret);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
HICON
|
|
APIENTRY
|
|
NtUserFindExistingCursorIcon(
|
|
HMODULE hModule,
|
|
HRSRC hRsrc,
|
|
LONG cx,
|
|
LONG cy)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
HANDLE Ret = (HANDLE)0;
|
|
DECLARE_RETURN(HICON);
|
|
|
|
TRACE("Enter NtUserFindExistingCursorIcon\n");
|
|
UserEnterExclusive();
|
|
|
|
CurIcon = IntFindExistingCurIconObject(hModule, hRsrc, cx, cy);
|
|
if (CurIcon)
|
|
{
|
|
Ret = CurIcon->Self;
|
|
|
|
// IntReleaseCurIconObject(CurIcon); // FIXME: Is this correct? Does IntFindExistingCurIconObject add a ref?
|
|
RETURN(Ret);
|
|
}
|
|
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
RETURN((HANDLE)0);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserFindExistingCursorIcon, ret=%p\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetClipCursor(
|
|
RECTL *lpRect)
|
|
{
|
|
/* FIXME: Check if process has WINSTA_READATTRIBUTES */
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
RECTL Rect;
|
|
NTSTATUS Status;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserGetClipCursor\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!lpRect)
|
|
RETURN(FALSE);
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
if (CurInfo->bClipped)
|
|
{
|
|
Rect = CurInfo->rcClip;
|
|
}
|
|
else
|
|
{
|
|
Rect.left = 0;
|
|
Rect.top = 0;
|
|
Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
|
|
Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
|
|
Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
RETURN(TRUE);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
HCURSOR
|
|
APIENTRY
|
|
NtUserSetCursor(
|
|
HCURSOR hCursor)
|
|
{
|
|
PCURICON_OBJECT pcurOld, pcurNew;
|
|
HCURSOR hOldCursor = NULL;
|
|
|
|
TRACE("Enter NtUserSetCursor\n");
|
|
UserEnterExclusive();
|
|
|
|
if (hCursor)
|
|
{
|
|
pcurNew = UserGetCurIconObject(hCursor);
|
|
if (!pcurNew)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
goto leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pcurNew = NULL;
|
|
}
|
|
|
|
pcurOld = UserSetCursor(pcurNew, FALSE);
|
|
if (pcurOld)
|
|
{
|
|
hOldCursor = (HCURSOR)pcurOld->Self;
|
|
UserDereferenceObject(pcurOld);
|
|
}
|
|
|
|
leave:
|
|
UserLeave();
|
|
return hOldCursor;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserSetCursorContents(
|
|
HANDLE hCurIcon,
|
|
PICONINFO UnsafeIconInfo)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
ICONINFO IconInfo;
|
|
PSURFACE psurfBmp;
|
|
NTSTATUS Status;
|
|
BOOL Ret = FALSE;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserSetCursorContents\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
/* Copy fields */
|
|
Status = MmCopyFromCaller(&IconInfo, UnsafeIconInfo, sizeof(ICONINFO));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Delete old bitmaps */
|
|
if ((CurIcon->IconInfo.hbmColor)
|
|
&& (CurIcon->IconInfo.hbmColor != IconInfo.hbmColor))
|
|
{
|
|
GreDeleteObject(CurIcon->IconInfo.hbmColor);
|
|
}
|
|
if ((CurIcon->IconInfo.hbmMask)
|
|
&& CurIcon->IconInfo.hbmMask != IconInfo.hbmMask)
|
|
{
|
|
GreDeleteObject(CurIcon->IconInfo.hbmMask);
|
|
}
|
|
|
|
/* Copy new IconInfo field */
|
|
CurIcon->IconInfo = IconInfo;
|
|
|
|
if (CurIcon->IconInfo.hbmColor)
|
|
{
|
|
psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmColor);
|
|
if (!psurfBmp)
|
|
goto done;
|
|
|
|
CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
|
|
CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
|
|
SURFACE_ShareUnlockSurface(psurfBmp);
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmColor, GDI_OBJ_HMGR_PUBLIC);
|
|
}
|
|
else
|
|
{
|
|
psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmMask);
|
|
if (!psurfBmp)
|
|
goto done;
|
|
|
|
CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
|
|
CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy / 2;
|
|
|
|
SURFACE_ShareUnlockSurface(psurfBmp);
|
|
}
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
|
|
|
|
Ret = TRUE;
|
|
|
|
done:
|
|
|
|
if (CurIcon)
|
|
{
|
|
UserDereferenceObject(CurIcon);
|
|
}
|
|
RETURN(Ret);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserSetCursorContents, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
#if 0
|
|
BOOL
|
|
APIENTRY
|
|
NtUserSetCursorIconData(
|
|
HANDLE Handle,
|
|
HMODULE hModule,
|
|
PUNICODE_STRING pstrResName,
|
|
PICONINFO pIconInfo)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
PSURFACE psurfBmp;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOL Ret = FALSE;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserSetCursorIconData\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(Handle)))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
CurIcon->hModule = hModule;
|
|
CurIcon->hRsrc = NULL; //hRsrc;
|
|
CurIcon->hGroupRsrc = NULL; //hGroupRsrc;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(pIconInfo, sizeof(ICONINFO), 1);
|
|
RtlCopyMemory(&CurIcon->IconInfo, pIconInfo, sizeof(ICONINFO));
|
|
|
|
CurIcon->IconInfo.hbmMask = BITMAP_CopyBitmap(pIconInfo->hbmMask);
|
|
CurIcon->IconInfo.hbmColor = BITMAP_CopyBitmap(pIconInfo->hbmColor);
|
|
|
|
if (CurIcon->IconInfo.hbmColor)
|
|
{
|
|
if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor)))
|
|
{
|
|
CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
|
|
CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
|
|
SURFACE_UnlockSurface(psurfBmp);
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
|
|
}
|
|
}
|
|
if (CurIcon->IconInfo.hbmMask)
|
|
{
|
|
if (CurIcon->IconInfo.hbmColor == NULL)
|
|
{
|
|
if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask)))
|
|
{
|
|
CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
|
|
CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
|
|
SURFACE_UnlockSurface(psurfBmp);
|
|
}
|
|
}
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
SetLastNtError(Status);
|
|
else
|
|
Ret = TRUE;
|
|
|
|
UserDereferenceObject(CurIcon);
|
|
RETURN(Ret);
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
#else
|
|
BOOL
|
|
APIENTRY
|
|
NtUserSetCursorIconData(
|
|
HANDLE hCurIcon,
|
|
PBOOL fIcon,
|
|
POINT *Hotspot,
|
|
HMODULE hModule,
|
|
HRSRC hRsrc,
|
|
HRSRC hGroupRsrc)
|
|
{
|
|
PCURICON_OBJECT CurIcon;
|
|
NTSTATUS Status;
|
|
POINT SafeHotspot;
|
|
BOOL Ret = FALSE;
|
|
DECLARE_RETURN(BOOL);
|
|
|
|
TRACE("Enter NtUserSetCursorIconData\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
|
|
{
|
|
RETURN(FALSE);
|
|
}
|
|
|
|
CurIcon->hModule = hModule;
|
|
CurIcon->hRsrc = hRsrc;
|
|
CurIcon->hGroupRsrc = hGroupRsrc;
|
|
|
|
/* Copy fields */
|
|
if (fIcon)
|
|
{
|
|
Status = MmCopyFromCaller(&CurIcon->IconInfo.fIcon, fIcon, sizeof(BOOL));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastNtError(Status);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!Hotspot)
|
|
Ret = TRUE;
|
|
}
|
|
|
|
if (Hotspot)
|
|
{
|
|
Status = MmCopyFromCaller(&SafeHotspot, Hotspot, sizeof(POINT));
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
CurIcon->IconInfo.xHotspot = SafeHotspot.x;
|
|
CurIcon->IconInfo.yHotspot = SafeHotspot.y;
|
|
|
|
Ret = TRUE;
|
|
}
|
|
else
|
|
SetLastNtError(Status);
|
|
}
|
|
|
|
if (!fIcon && !Hotspot)
|
|
{
|
|
Ret = TRUE;
|
|
}
|
|
|
|
done:
|
|
if(Ret)
|
|
{
|
|
/* This icon is shared now */
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
|
|
if(CurIcon->IconInfo.hbmColor)
|
|
{
|
|
GreSetObjectOwner(CurIcon->IconInfo.hbmColor, GDI_OBJ_HMGR_PUBLIC);
|
|
}
|
|
}
|
|
UserDereferenceObject(CurIcon);
|
|
RETURN(Ret);
|
|
|
|
|
|
CLEANUP:
|
|
TRACE("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
|
|
UserLeave();
|
|
END_CLEANUP;
|
|
}
|
|
#endif
|
|
|
|
/* Mostly inspired from wine code.
|
|
* We use low level functions because:
|
|
* - at this point, the icon bitmap could have a different bit depth than the DC,
|
|
* making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
|
|
* This happens after a mode setting change.
|
|
* - it avoids massive GDI objects locking when only the destination surface needs it.
|
|
* - It makes (small) performance gains.
|
|
*/
|
|
BOOL
|
|
UserDrawIconEx(
|
|
HDC hDc,
|
|
INT xLeft,
|
|
INT yTop,
|
|
PCURICON_OBJECT pIcon,
|
|
INT cxWidth,
|
|
INT cyHeight,
|
|
UINT istepIfAniCur,
|
|
HBRUSH hbrFlickerFreeDraw,
|
|
UINT diFlags)
|
|
{
|
|
PSURFACE psurfDest, psurfMask, psurfColor, psurfOffScreen;
|
|
PDC pdc = NULL;
|
|
BOOL Ret = FALSE;
|
|
HBITMAP hbmMask, hbmColor;
|
|
BOOL bOffScreen, bAlpha = FALSE;
|
|
RECTL rcDest, rcSrc;
|
|
CLIPOBJ* pdcClipObj = NULL;
|
|
EXLATEOBJ exlo;
|
|
|
|
/* Stupid case */
|
|
if((diFlags & DI_NORMAL) == 0)
|
|
{
|
|
ERR("DrawIconEx called without mask or color bitmap to draw.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
hbmMask = pIcon->IconInfo.hbmMask;
|
|
hbmColor = pIcon->IconInfo.hbmColor;
|
|
|
|
if (istepIfAniCur)
|
|
ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
|
|
|
|
/*
|
|
* Get our objects.
|
|
* Shared locks are enough, we are only reading those bitmaps
|
|
*/
|
|
psurfMask = SURFACE_ShareLockSurface(hbmMask);
|
|
if(psurfMask == NULL)
|
|
{
|
|
ERR("Unable to lock the mask surface.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Color bitmap is not mandatory */
|
|
if(hbmColor == NULL)
|
|
{
|
|
/* But then the mask bitmap must have the information in it's bottom half */
|
|
ASSERT(psurfMask->SurfObj.sizlBitmap.cy == 2*pIcon->Size.cy);
|
|
psurfColor = NULL;
|
|
}
|
|
else if ((psurfColor = SURFACE_ShareLockSurface(hbmColor)) == NULL)
|
|
{
|
|
ERR("Unable to lock the color bitmap.\n");
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set source rect */
|
|
RECTL_vSetRect(&rcSrc, 0, 0, pIcon->Size.cx, pIcon->Size.cy);
|
|
|
|
/* Check for alpha */
|
|
if (psurfColor &&
|
|
(psurfColor->SurfObj.iBitmapFormat == BMF_32BPP) &&
|
|
(diFlags & DI_IMAGE))
|
|
{
|
|
PFN_DIB_GetPixel fnSource_GetPixel = NULL;
|
|
INT i, j;
|
|
|
|
/* In order to correctly display 32 bit icons Windows first scans the image,
|
|
because information about transparency is not stored in any image's headers */
|
|
fnSource_GetPixel = DibFunctionsForBitmapFormat[BMF_32BPP].DIB_GetPixel;
|
|
for (i = 0; i < psurfColor->SurfObj.sizlBitmap.cx; i++)
|
|
{
|
|
for (j = 0; j < psurfColor->SurfObj.sizlBitmap.cy; j++)
|
|
{
|
|
bAlpha = ((BYTE)(fnSource_GetPixel(&psurfColor->SurfObj, i, j) >> 24) & 0xff);
|
|
if (bAlpha)
|
|
break;
|
|
}
|
|
if (bAlpha)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Fix width parameter, if needed */
|
|
if (!cxWidth)
|
|
{
|
|
if(diFlags & DI_DEFAULTSIZE)
|
|
cxWidth = pIcon->IconInfo.fIcon ?
|
|
UserGetSystemMetrics(SM_CXICON) : UserGetSystemMetrics(SM_CXCURSOR);
|
|
else
|
|
cxWidth = pIcon->Size.cx;
|
|
}
|
|
|
|
/* Fix height parameter, if needed */
|
|
if (!cyHeight)
|
|
{
|
|
if(diFlags & DI_DEFAULTSIZE)
|
|
cyHeight = pIcon->IconInfo.fIcon ?
|
|
UserGetSystemMetrics(SM_CYICON) : UserGetSystemMetrics(SM_CYCURSOR);
|
|
else
|
|
cyHeight = pIcon->Size.cy;
|
|
}
|
|
|
|
/* Should we render off-screen? */
|
|
bOffScreen = hbrFlickerFreeDraw && (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH);
|
|
|
|
if (bOffScreen)
|
|
{
|
|
/* Yes: Allocate and paint the offscreen surface */
|
|
EBRUSHOBJ eboFill;
|
|
PBRUSH pbrush = BRUSH_ShareLockBrush(hbrFlickerFreeDraw);
|
|
|
|
TRACE("Performing off-screen rendering.\n");
|
|
|
|
if(!pbrush)
|
|
{
|
|
ERR("Failed to get brush object.\n");
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!psurfColor)
|
|
{
|
|
ERR("Attention HAX FIX API DrawIconEx TEST Line 37: psurfColor is NULL going with Mask!\n");
|
|
psurfColor = SURFACE_ShareLockSurface(hbmMask);
|
|
}
|
|
|
|
psurfOffScreen = SURFACE_AllocSurface(STYPE_BITMAP,
|
|
cxWidth, cyHeight, psurfColor->SurfObj.iBitmapFormat,
|
|
0, 0, NULL);
|
|
if(!psurfOffScreen)
|
|
{
|
|
ERR("Failed to allocate the off-screen surface.\n");
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
BRUSH_ShareUnlockBrush(pbrush);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Paint the brush */
|
|
EBRUSHOBJ_vInit(&eboFill, pbrush, psurfOffScreen, 0x00FFFFFF, 0, NULL);
|
|
RECTL_vSetRect(&rcDest, 0, 0, cxWidth, cyHeight);
|
|
|
|
Ret = IntEngBitBlt(&psurfOffScreen->SurfObj,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&rcDest,
|
|
NULL,
|
|
NULL,
|
|
&eboFill.BrushObject,
|
|
&pbrush->ptOrigin,
|
|
ROP4_PATCOPY);
|
|
|
|
/* Clean up everything */
|
|
EBRUSHOBJ_vCleanup(&eboFill);
|
|
BRUSH_ShareUnlockBrush(pbrush);
|
|
|
|
if(!Ret)
|
|
{
|
|
ERR("Failed to paint the off-screen surface.\n");
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
GDIOBJ_vDeleteObject(&psurfOffScreen->BaseObject);
|
|
return FALSE;
|
|
}
|
|
|
|
/* We now have our destination surface */
|
|
psurfDest = psurfOffScreen;
|
|
}
|
|
else
|
|
{
|
|
/* We directly draw to the DC */
|
|
TRACE("Performing on screen rendering.\n");
|
|
|
|
psurfOffScreen = NULL;
|
|
pdc = DC_LockDc(hDc);
|
|
if(!pdc)
|
|
{
|
|
ERR("Could not lock the destination DC.\n");
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
return FALSE;
|
|
}
|
|
/* Calculate destination rectangle */
|
|
RECTL_vSetRect(&rcDest, xLeft, yTop, xLeft + cxWidth, yTop + cyHeight);
|
|
IntLPtoDP(pdc, (LPPOINT)&rcDest, 2);
|
|
RECTL_vOffsetRect(&rcDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
|
|
|
|
/* Prepare the underlying surface */
|
|
DC_vPrepareDCsForBlit(pdc, rcDest, NULL, rcDest);
|
|
|
|
/* Get the clip object */
|
|
pdcClipObj = pdc->rosdc.CombinedClip;
|
|
|
|
/* We now have our destination surface and rectangle */
|
|
psurfDest = pdc->dclevel.pSurface;
|
|
|
|
if(psurfDest == NULL)
|
|
{
|
|
/* Empty DC */
|
|
DC_vFinishBlit(pdc, NULL);
|
|
DC_UnlockDc(pdc);
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Now do the rendering */
|
|
if(bAlpha && (diFlags & DI_IMAGE))
|
|
{
|
|
BLENDOBJ blendobj = { {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA } };
|
|
BYTE Alpha;
|
|
INT i, j;
|
|
PSURFACE psurf = NULL;
|
|
PBYTE ptr ;
|
|
HBITMAP hsurfCopy = NULL;
|
|
|
|
hsurfCopy = BITMAP_CopyBitmap(hbmColor);
|
|
if(!hsurfCopy)
|
|
{
|
|
ERR("BITMAP_CopyBitmap failed!");
|
|
goto CleanupAlpha;
|
|
}
|
|
|
|
psurf = SURFACE_ShareLockSurface(hsurfCopy);
|
|
if(!psurf)
|
|
{
|
|
ERR("SURFACE_LockSurface failed!\n");
|
|
goto CleanupAlpha;
|
|
}
|
|
|
|
/* Premultiply with the alpha channel value */
|
|
for (i = 0; i < psurf->SurfObj.sizlBitmap.cy; i++)
|
|
{
|
|
ptr = (PBYTE)psurf->SurfObj.pvScan0 + i*psurf->SurfObj.lDelta;
|
|
for (j = 0; j < psurf->SurfObj.sizlBitmap.cx; j++)
|
|
{
|
|
Alpha = ptr[3];
|
|
ptr[0] = (ptr[0] * Alpha) / 0xff;
|
|
ptr[1] = (ptr[1] * Alpha) / 0xff;
|
|
ptr[2] = (ptr[2] * Alpha) / 0xff;
|
|
|
|
ptr += 4;
|
|
}
|
|
}
|
|
|
|
/* Initialize color translation object */
|
|
EXLATEOBJ_vInitialize(&exlo, psurf->ppal, psurfDest->ppal, 0xFFFFFFFF, 0xFFFFFFFF, 0);
|
|
|
|
/* Now do it */
|
|
Ret = IntEngAlphaBlend(&psurfDest->SurfObj,
|
|
&psurf->SurfObj,
|
|
pdcClipObj,
|
|
&exlo.xlo,
|
|
&rcDest,
|
|
&rcSrc,
|
|
&blendobj);
|
|
|
|
EXLATEOBJ_vCleanup(&exlo);
|
|
|
|
CleanupAlpha:
|
|
if(psurf) SURFACE_ShareUnlockSurface(psurf);
|
|
if(hsurfCopy) NtGdiDeleteObjectApp(hsurfCopy);
|
|
if(Ret) goto done;
|
|
ERR("NtGdiAlphaBlend failed!\n");
|
|
}
|
|
|
|
if (diFlags & DI_MASK)
|
|
{
|
|
DWORD rop4 = (diFlags & DI_IMAGE) ? ROP4_SRCAND : ROP4_SRCCOPY;
|
|
|
|
EXLATEOBJ_vInitSrcMonoXlate(&exlo, psurfDest->ppal, 0x00FFFFFF, 0);
|
|
|
|
Ret = IntEngStretchBlt(&psurfDest->SurfObj,
|
|
&psurfMask->SurfObj,
|
|
NULL,
|
|
pdcClipObj,
|
|
&exlo.xlo,
|
|
NULL,
|
|
&rcDest,
|
|
&rcSrc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rop4);
|
|
|
|
EXLATEOBJ_vCleanup(&exlo);
|
|
|
|
if(!Ret)
|
|
{
|
|
ERR("Failed to mask the bitmap data.\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if(diFlags & DI_IMAGE)
|
|
{
|
|
if (psurfColor)
|
|
{
|
|
DWORD rop4 = (diFlags & DI_MASK) ? ROP4_SRCINVERT : ROP4_SRCCOPY ;
|
|
|
|
EXLATEOBJ_vInitialize(&exlo, psurfColor->ppal, psurfDest->ppal, 0x00FFFFFF, 0x00FFFFFF, 0);
|
|
|
|
Ret = IntEngStretchBlt(&psurfDest->SurfObj,
|
|
&psurfColor->SurfObj,
|
|
NULL,
|
|
pdcClipObj,
|
|
&exlo.xlo,
|
|
NULL,
|
|
&rcDest,
|
|
&rcSrc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rop4);
|
|
|
|
EXLATEOBJ_vCleanup(&exlo);
|
|
|
|
if(!Ret)
|
|
{
|
|
ERR("Failed to render the icon bitmap.\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Mask bitmap holds the information in its bottom half */
|
|
DWORD rop4 = (diFlags & DI_MASK) ? ROP4_SRCINVERT : ROP4_SRCCOPY;
|
|
RECTL_vOffsetRect(&rcSrc, 0, pIcon->Size.cy);
|
|
|
|
EXLATEOBJ_vInitSrcMonoXlate(&exlo, psurfDest->ppal, 0x00FFFFFF, 0);
|
|
|
|
Ret = IntEngStretchBlt(&psurfDest->SurfObj,
|
|
&psurfMask->SurfObj,
|
|
NULL,
|
|
pdcClipObj,
|
|
&exlo.xlo,
|
|
NULL,
|
|
&rcDest,
|
|
&rcSrc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rop4);
|
|
|
|
EXLATEOBJ_vCleanup(&exlo);
|
|
|
|
if(!Ret)
|
|
{
|
|
ERR("Failed to render the icon bitmap.\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
/* We're done. Was it a double buffered draw ? */
|
|
if(bOffScreen)
|
|
{
|
|
/* Yes. Draw it back to our DC */
|
|
POINTL ptSrc = {0, 0};
|
|
pdc = DC_LockDc(hDc);
|
|
if(!pdc)
|
|
{
|
|
ERR("Could not lock the destination DC.\n");
|
|
return FALSE;
|
|
}
|
|
/* Calculate destination rectangle */
|
|
RECTL_vSetRect(&rcDest, xLeft, yTop, xLeft + cxWidth, yTop + cyHeight);
|
|
IntLPtoDP(pdc, (LPPOINT)&rcDest, 2);
|
|
RECTL_vOffsetRect(&rcDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
|
|
|
|
/* Prepare the underlying surface */
|
|
DC_vPrepareDCsForBlit(pdc, rcDest, NULL, rcDest);
|
|
|
|
/* Get the clip object */
|
|
pdcClipObj = pdc->rosdc.CombinedClip;
|
|
|
|
/* We now have our destination surface and rectangle */
|
|
psurfDest = pdc->dclevel.pSurface;
|
|
if(!psurfDest)
|
|
{
|
|
/* So, you did all of this for an empty DC. */
|
|
DC_UnlockDc(pdc);
|
|
goto Cleanup2;
|
|
}
|
|
|
|
/* Color translation */
|
|
EXLATEOBJ_vInitialize(&exlo, psurfOffScreen->ppal, psurfDest->ppal, 0x00FFFFFF, 0x00FFFFFF, 0);
|
|
|
|
/* Blt it! */
|
|
Ret = IntEngBitBlt(&psurfDest->SurfObj,
|
|
&psurfOffScreen->SurfObj,
|
|
NULL,
|
|
pdcClipObj,
|
|
&exlo.xlo,
|
|
&rcDest,
|
|
&ptSrc,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
ROP4_SRCCOPY);
|
|
|
|
EXLATEOBJ_vCleanup(&exlo);
|
|
}
|
|
Cleanup:
|
|
if(pdc)
|
|
{
|
|
DC_vFinishBlit(pdc, NULL);
|
|
DC_UnlockDc(pdc);
|
|
}
|
|
|
|
Cleanup2:
|
|
/* Delete off screen rendering surface */
|
|
if(psurfOffScreen)
|
|
GDIOBJ_vDeleteObject(&psurfOffScreen->BaseObject);
|
|
|
|
/* Unlock other surfaces */
|
|
SURFACE_ShareUnlockSurface(psurfMask);
|
|
if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserDrawIconEx(
|
|
HDC hdc,
|
|
int xLeft,
|
|
int yTop,
|
|
HICON hIcon,
|
|
int cxWidth,
|
|
int cyHeight,
|
|
UINT istepIfAniCur,
|
|
HBRUSH hbrFlickerFreeDraw,
|
|
UINT diFlags,
|
|
BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
|
|
PVOID pDIXData)
|
|
{
|
|
PCURICON_OBJECT pIcon;
|
|
BOOL Ret;
|
|
|
|
TRACE("Enter NtUserDrawIconEx\n");
|
|
UserEnterExclusive();
|
|
|
|
if (!(pIcon = UserGetCurIconObject(hIcon)))
|
|
{
|
|
ERR("UserGetCurIconObject() failed!\n");
|
|
UserLeave();
|
|
return FALSE;
|
|
}
|
|
|
|
Ret = UserDrawIconEx(hdc,
|
|
xLeft,
|
|
yTop,
|
|
pIcon,
|
|
cxWidth,
|
|
cyHeight,
|
|
istepIfAniCur,
|
|
hbrFlickerFreeDraw,
|
|
diFlags);
|
|
|
|
UserDereferenceObject(pIcon);
|
|
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/* EOF */
|