reactos/win32ss/user/ntuser/cursoricon_new.c
Amine Khaldi 23298ac30c * Sync up to trunk head (r64995).
svn path=/branches/shell-experiments/; revision=64996
2014-10-25 19:46:12 +00:00

1610 lines
42 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Cursor and icon functions
* FILE: win32ss/user/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
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserIcon);
SYSTEM_CURSORINFO gSysCursorInfo;
BOOL
InitCursorImpl(VOID)
{
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;
}
FORCEINLINE
BOOL
is_icon(PCURICON_OBJECT object)
{
return MAKEINTRESOURCE(object->rt) == RT_ICON;
}
/* 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;
}
if (UserObjectInDestroy(hCurIcon))
{
WARN("Requesting invalid/destroyed cursor.\n");
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
return NULL;
}
CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, TYPE_CURSOR);
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;
}
HANDLE
IntCreateCurIconHandle(BOOLEAN Animated)
{
PCURICON_OBJECT CurIcon;
HANDLE hCurIcon;
CurIcon = UserCreateObject(
gHandleTable,
NULL,
GetW32ThreadInfo(),
&hCurIcon,
TYPE_CURSOR,
Animated ? sizeof(ACON) : sizeof(CURICON_OBJECT));
if (!CurIcon)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
UserDereferenceObject(CurIcon);
return hCurIcon;
}
BOOLEAN
IntDestroyCurIconObject(
_In_ PVOID Object)
{
PCURICON_OBJECT CurIcon = Object;
/* Try finding it in its process cache */
if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
PPROCESSINFO ppi;
ppi = CurIcon->head.ppi;
if (ppi->pCursorCache == CurIcon)
{
ppi->pCursorCache = CurIcon->pcurNext;
UserDereferenceObject(CurIcon);
}
else
{
PCURICON_OBJECT CacheCurIcon = ppi->pCursorCache;
while (CacheCurIcon)
{
if (CacheCurIcon->pcurNext == CurIcon)
{
CacheCurIcon->pcurNext = CurIcon->pcurNext;
break;
}
CacheCurIcon = CacheCurIcon->pcurNext;
}
/* We must have found it! */
ASSERT(CacheCurIcon != NULL);
UserDereferenceObject(CurIcon);
}
}
/* We just mark the handle as being destroyed.
* Deleting all the stuff will be deferred to the actual struct free. */
UserDeleteObject(CurIcon->head.h, TYPE_CURSOR);
return TRUE;
}
void
FreeCurIconObject(
_In_ PVOID Object)
{
PCURICON_OBJECT CurIcon = Object;
if(!(CurIcon->CURSORF_flags & CURSORF_ACON))
{
HBITMAP bmpMask = CurIcon->hbmMask;
HBITMAP bmpColor = CurIcon->hbmColor;
HBITMAP bmpAlpha = CurIcon->hbmAlpha;
/* Delete bitmaps */
if (bmpMask)
{
GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpMask);
CurIcon->hbmMask = NULL;
}
if (bmpColor)
{
GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpColor);
CurIcon->hbmColor = NULL;
}
if (bmpAlpha)
{
GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpAlpha);
CurIcon->hbmAlpha = NULL;
}
}
else
{
PACON AniCurIcon = (PACON)CurIcon;
UINT i;
for(i = 0; i < AniCurIcon->cpcur; i++)
{
UserDereferenceObject(AniCurIcon->aspcur[i]);
IntDestroyCurIconObject(AniCurIcon->aspcur[i]);
}
ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR);
}
if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
if (!IS_INTRESOURCE(CurIcon->strName.Buffer))
ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
if (CurIcon->atomModName)
RtlDeleteAtomFromAtomTable(gAtomTable, CurIcon->atomModName);
CurIcon->strName.Buffer = NULL;
CurIcon->atomModName = 0;
}
/* Finally free the thing */
FreeProcMarkObject(CurIcon);
}
VOID FASTCALL
IntCleanupCurIconCache(PPROCESSINFO Win32Process)
{
PCURICON_OBJECT CurIcon;
/* Run through the list of icon objects */
while (Win32Process->pCursorCache)
{
CurIcon = Win32Process->pCursorCache;
Win32Process->pCursorCache = CurIcon->pcurNext;
UserDereferenceObject(CurIcon);
}
}
/*
* @implemented
*/
BOOL
APIENTRY
NtUserGetIconInfo(
_In_ HANDLE hCurIcon,
_Out_opt_ PICONINFO IconInfo,
_Out_opt_ PUNICODE_STRING lpModule, // Optional
_Out_opt_ PUNICODE_STRING lpResName, // Optional
_Out_opt_ LPDWORD pbpp, // Optional
_In_ BOOL bInternal)
{
ICONINFO ii;
PCURICON_OBJECT CurIcon;
NTSTATUS Status = STATUS_SUCCESS;
BOOL Ret = FALSE;
DWORD colorBpp = 0;
TRACE("Enter NtUserGetIconInfo\n");
/* Check if something was actually asked */
if (!IconInfo && !lpModule && !lpResName)
{
WARN("Nothing to fill.\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
UserEnterExclusive();
if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
{
WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon);
UserLeave();
return FALSE;
}
/* Give back the icon information */
if(IconInfo)
{
PCURICON_OBJECT FrameCurIcon = CurIcon;
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
/* Get information from first frame. */
FrameCurIcon = ((PACON)CurIcon)->aspcur[0];
}
/* Fill data */
ii.fIcon = is_icon(FrameCurIcon);
ii.xHotspot = FrameCurIcon->xHotspot;
ii.yHotspot = FrameCurIcon->yHotspot;
/* Copy bitmaps */
ii.hbmMask = BITMAP_CopyBitmap(FrameCurIcon->hbmMask);
GreSetObjectOwner(ii.hbmMask, GDI_OBJ_HMGR_POWNED);
ii.hbmColor = BITMAP_CopyBitmap(FrameCurIcon->hbmColor);
GreSetObjectOwner(ii.hbmColor, GDI_OBJ_HMGR_POWNED);
colorBpp = FrameCurIcon->bpp;
/* 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))
{
WARN("Status: 0x%08x.\n", Status);
SetLastNtError(Status);
goto leave;
}
}
/* Give back the module name */
if(lpModule)
{
ULONG BufLen = 0;
if (!CurIcon->atomModName)
goto leave;
RtlQueryAtomInAtomTable(gAtomTable, CurIcon->atomModName, NULL, NULL, NULL, &BufLen);
/* Get the module name from the atom table */
_SEH2_TRY
{
BufLen += sizeof(WCHAR);
if (BufLen > (lpModule->MaximumLength))
{
lpModule->Length = 0;
lpModule->MaximumLength = BufLen;
}
else
{
ProbeForWrite(lpModule->Buffer, lpModule->MaximumLength, 1);
BufLen = lpModule->MaximumLength;
RtlQueryAtomInAtomTable(gAtomTable, CurIcon->atomModName, NULL, NULL, lpModule->Buffer, &BufLen);
lpModule->Length = BufLen;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
goto leave;
}
}
if (lpResName)
{
if (!CurIcon->strName.Buffer)
goto leave;
/* Copy it */
_SEH2_TRY
{
ProbeForWrite(lpResName, sizeof(UNICODE_STRING), 1);
if (IS_INTRESOURCE(CurIcon->strName.Buffer))
{
lpResName->Buffer = CurIcon->strName.Buffer;
lpResName->Length = 0;
lpResName->MaximumLength = 0;
}
else if (lpResName->MaximumLength < CurIcon->strName.MaximumLength)
{
lpResName->Length = 0;
lpResName->MaximumLength = CurIcon->strName.MaximumLength;
}
else
{
ProbeForWrite(lpResName->Buffer, lpResName->MaximumLength, 1);
RtlCopyMemory(lpResName->Buffer, CurIcon->strName.Buffer, CurIcon->strName.Length);
lpResName->Length = CurIcon->strName.Length;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
}
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
goto leave;
}
Ret = TRUE;
leave:
UserDereferenceObject(CurIcon);
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;
}
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
/* Use first frame for animated cursors */
PACON AniCurIcon = (PACON)CurIcon;
CurIcon = AniCurIcon->aspcur[0];
UserDereferenceObject(AniCurIcon);
UserReferenceObject(CurIcon);
}
_SEH2_TRY
{
ProbeForWrite(plcx, sizeof(LONG), 1);
*plcx = CurIcon->cx;
ProbeForWrite(plcy, sizeof(LONG), 1);
*plcy = CurIcon->cy;
}
_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 ? CurIcon->head.h : NULL);
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)
{
BOOL ret;
PCURICON_OBJECT CurIcon = NULL;
TRACE("Enter NtUserDestroyCursorIcon (%p, %u)\n", hCurIcon, bForce);
UserEnterExclusive();
CurIcon = UserGetCurIconObject(hCurIcon);
if (!CurIcon)
{
ret = FALSE;
goto leave;
}
if (!bForce)
{
/* Maybe we have good reasons not to destroy this object */
if (CurIcon->head.ppi != PsGetCurrentProcessWin32Process())
{
/* No way, you're not touching my cursor */
ret = FALSE;
goto leave;
}
if (CurIcon->CURSORF_flags & CURSORF_CURRENT)
{
WARN("Trying to delete current cursor!\n");
ret = FALSE;
goto leave;
}
if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
WARN("Trying to delete shared cursor.\n");
/* This one is not an error */
ret = TRUE;
goto leave;
}
}
/* Destroy the handle */
ret = IntDestroyCurIconObject(CurIcon);
leave:
if (CurIcon)
UserDereferenceObject(CurIcon);
TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n", ret);
UserLeave();
return ret;
}
/*
* @implemented
*/
HICON
NTAPI
NtUserFindExistingCursorIcon(
_In_ PUNICODE_STRING pustrModule,
_In_ PUNICODE_STRING pustrRsrc,
_In_ FINDEXISTINGCURICONPARAM* param)
{
PCURICON_OBJECT CurIcon;
HICON Ret = NULL;
UNICODE_STRING ustrModuleSafe, ustrRsrcSafe;
FINDEXISTINGCURICONPARAM paramSafe;
NTSTATUS Status;
PPROCESSINFO pProcInfo = PsGetCurrentProcessWin32Process();
RTL_ATOM atomModName;
TRACE("Enter NtUserFindExistingCursorIcon\n");
_SEH2_TRY
{
ProbeForRead(param, sizeof(*param), 1);
RtlCopyMemory(&paramSafe, param, sizeof(paramSafe));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
/* Capture resource name (it can be an INTRESOURCE == ATOM) */
Status = ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe, pustrRsrc);
if(!NT_SUCCESS(Status))
return NULL;
Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
Status = RtlLookupAtomInAtomTable(gAtomTable, ustrModuleSafe.Buffer, &atomModName);
ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
if(!NT_SUCCESS(Status))
{
/* The module is not in the atom table. No chance to find the cursor */
goto done;
}
UserEnterExclusive();
CurIcon = pProcInfo->pCursorCache;
while(CurIcon)
{
/* Icon/cursor */
if (paramSafe.bIcon != is_icon(CurIcon))
{
CurIcon = CurIcon->pcurNext;
continue;
}
/* See if module names match */
if (atomModName == CurIcon->atomModName)
{
/* They do. Now see if this is the same resource */
if (IS_INTRESOURCE(CurIcon->strName.Buffer) != IS_INTRESOURCE(ustrRsrcSafe.Buffer))
{
/* One is an INT resource and the other is not -> no match */
CurIcon = CurIcon->pcurNext;
continue;
}
if (IS_INTRESOURCE(CurIcon->strName.Buffer))
{
if (CurIcon->strName.Buffer == ustrRsrcSafe.Buffer)
{
/* INT resources match */
break;
}
}
else if (RtlCompareUnicodeString(&ustrRsrcSafe, &CurIcon->strName, TRUE) == 0)
{
/* Resource name strings match */
break;
}
}
CurIcon = CurIcon->pcurNext;
}
if(CurIcon)
Ret = CurIcon->head.h;
UserLeave();
done:
if(!IS_INTRESOURCE(ustrRsrcSafe.Buffer))
ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING);
return Ret;
}
/*
* @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: %p\n", hCursor);
UserEnterExclusive();
if (hCursor)
{
pcurNew = UserGetCurIconObject(hCursor);
if (!pcurNew)
{
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
goto leave;
}
pcurNew->CURSORF_flags |= CURSORF_CURRENT;
}
else
{
pcurNew = NULL;
}
pcurOld = UserSetCursor(pcurNew, FALSE);
if (pcurOld)
{
hOldCursor = pcurOld->head.h;
/* See if it was destroyed in the meantime */
if (UserObjectInDestroy(hOldCursor))
hOldCursor = NULL;
pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
UserDereferenceObject(pcurOld);
}
leave:
UserLeave();
return hOldCursor;
}
/*
* @unimplemented
*/
BOOL
APIENTRY
NtUserSetCursorContents(
HANDLE hCurIcon,
PICONINFO UnsafeIconInfo)
{
FIXME(" is UNIMPLEMENTED.\n");
return FALSE;
}
/*
* @implemented
*/
BOOL
APIENTRY
NtUserSetCursorIconData(
_In_ HCURSOR Handle,
_In_opt_ PUNICODE_STRING pustrModule,
_In_opt_ PUNICODE_STRING pustrRsrc,
_In_ const CURSORDATA* pCursorData)
{
PCURICON_OBJECT CurIcon;
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN Ret = FALSE;
BOOLEAN IsShared = FALSE, IsAnim = FALSE;
DWORD numFrames;
UINT i = 0;
TRACE("Enter NtUserSetCursorIconData\n");
UserEnterExclusive();
if (!(CurIcon = UserGetCurIconObject(Handle)))
{
UserLeave();
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
_SEH2_TRY
{
ProbeForRead(pCursorData, sizeof(*pCursorData), 1);
if(pCursorData->CURSORF_flags & CURSORF_ACON)
{
/* This is an animated cursor */
PACON AniCurIcon = (PACON)CurIcon;
DWORD numSteps;
numFrames = AniCurIcon->cpcur = pCursorData->cpcur;
numSteps = AniCurIcon->cicur = pCursorData->cicur;
AniCurIcon->iicur = pCursorData->iicur;
AniCurIcon->rt = pCursorData->rt;
/* Calculate size: one cursor object for each frame, and a frame index and jiffies for each "step" */
AniCurIcon->aspcur = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, /* Let SEH catch allocation failures */
numFrames * sizeof(CURICON_OBJECT*) + numSteps * (sizeof(DWORD) + sizeof(INT)),
USERTAG_CURSOR);
AniCurIcon->aicur = (DWORD*)(AniCurIcon->aspcur + numFrames);
AniCurIcon->ajifRate = (INT*)(AniCurIcon->aicur + numSteps);
RtlZeroMemory(AniCurIcon->aspcur, numFrames * sizeof(CURICON_OBJECT*));
ProbeForRead(pCursorData->aicur, numSteps * sizeof(DWORD), 1);
RtlCopyMemory(AniCurIcon->aicur, pCursorData->aicur, numSteps * sizeof(DWORD));
ProbeForRead(pCursorData->ajifRate, numSteps * sizeof(INT), 1);
RtlCopyMemory(AniCurIcon->ajifRate, pCursorData->ajifRate, numSteps * sizeof(INT));
AniCurIcon->CURSORF_flags = pCursorData->CURSORF_flags;
pCursorData = pCursorData->aspcur;
IsAnim = TRUE;
}
else
{
CurIcon->xHotspot = pCursorData->xHotspot;
CurIcon->yHotspot = pCursorData->yHotspot;
CurIcon->cx = pCursorData->cx;
CurIcon->cy = pCursorData->cy;
CurIcon->rt = pCursorData->rt;
CurIcon->bpp = pCursorData->bpp;
CurIcon->hbmMask = pCursorData->hbmMask;
CurIcon->hbmColor = pCursorData->hbmColor;
CurIcon->hbmAlpha = pCursorData->hbmAlpha;
CurIcon->CURSORF_flags = pCursorData->CURSORF_flags;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
goto done;
}
if(IsAnim)
{
PACON AniCurIcon = (PACON)CurIcon;
/* This is an animated cursor. Create a cursor object for each frame and set up the data */
for(i = 0; i < numFrames; i++)
{
HANDLE hCurFrame = IntCreateCurIconHandle(FALSE);
if(!NtUserSetCursorIconData(hCurFrame, NULL, NULL, pCursorData))
goto done;
AniCurIcon->aspcur[i] = UserGetCurIconObject(hCurFrame);
if(!AniCurIcon->aspcur[i])
goto done;
pCursorData++;
}
}
if(CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
IsShared = TRUE;
if(pustrRsrc && pustrModule)
{
UNICODE_STRING ustrModuleSafe;
/* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
Status = ProbeAndCaptureUnicodeStringOrAtom(&CurIcon->strName, pustrRsrc);
if(!NT_SUCCESS(Status))
goto done;
Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
Status = RtlAddAtomToAtomTable(gAtomTable, ustrModuleSafe.Buffer, &CurIcon->atomModName);
ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
if(!NT_SUCCESS(Status))
goto done;
}
}
if(!CurIcon->hbmMask)
{
ERR("NtUserSetCursorIconData was got no hbmMask.\n");
EngSetLastError(ERROR_INVALID_PARAMETER);
goto done;
}
GreSetObjectOwner(CurIcon->hbmMask, GDI_OBJ_HMGR_PUBLIC);
if(CurIcon->hbmColor)
GreSetObjectOwner(CurIcon->hbmColor, GDI_OBJ_HMGR_PUBLIC);
if(CurIcon->hbmAlpha)
GreSetObjectOwner(CurIcon->hbmAlpha, GDI_OBJ_HMGR_PUBLIC);
if(IsShared)
{
/* Update process cache in case of shared cursor */
PPROCESSINFO ppi = CurIcon->head.ppi;
UserReferenceObject(CurIcon);
CurIcon->pcurNext = ppi->pCursorCache;
ppi->pCursorCache = CurIcon;
}
Ret = TRUE;
done:
if(!Ret && IsShared)
{
if(!IS_INTRESOURCE(CurIcon->strName.Buffer))
ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
}
if(!Ret && IsAnim)
{
PACON AniCurIcon = (PACON)CurIcon;
for(i = 0; i < numFrames; i++)
{
if(AniCurIcon->aspcur[i])
{
UserDereferenceObject(AniCurIcon->aspcur[i]);
IntDestroyCurIconObject(AniCurIcon->aspcur[i]);
}
}
AniCurIcon->cicur = 0;
AniCurIcon->cpcur = 0;
ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR);
AniCurIcon->aspcur = NULL;
AniCurIcon->aicur = NULL;
AniCurIcon->ajifRate = NULL;
}
UserDereferenceObject(CurIcon);
TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret);
UserLeave();
return Ret;
}
/* 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 = NULL;
PDC pdc = NULL;
BOOL Ret = FALSE;
HBITMAP hbmMask, hbmColor, hbmAlpha;
BOOL bOffScreen;
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;
}
if (pIcon->CURSORF_flags & CURSORF_ACON)
{
ACON* pAcon = (ACON*)pIcon;
if(istepIfAniCur >= pAcon->cicur)
{
ERR("NtUserDrawIconEx: istepIfAniCur too big!\n");
return FALSE;
}
pIcon = pAcon->aspcur[pAcon->aicur[istepIfAniCur]];
}
hbmMask = pIcon->hbmMask;
hbmColor = pIcon->hbmColor;
hbmAlpha = pIcon->hbmAlpha;
/*
* 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->cy);
psurfColor = NULL;
}
else if ((psurfColor = SURFACE_ShareLockSurface(hbmColor)) == NULL)
{
ERR("Unable to lock the color bitmap.\n");
SURFACE_ShareUnlockSurface(psurfMask);
return FALSE;
}
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, NULL);
/* 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;
}
/* Set source rect */
RECTL_vSetRect(&rcSrc, 0, 0, pIcon->cx, pIcon->cy);
/* Fix width parameter, if needed */
if (!cxWidth)
{
if(diFlags & DI_DEFAULTSIZE)
cxWidth = is_icon(pIcon) ?
UserGetSystemMetrics(SM_CXICON) : UserGetSystemMetrics(SM_CXCURSOR);
else
cxWidth = pIcon->cx;
}
/* Fix height parameter, if needed */
if (!cyHeight)
{
if(diFlags & DI_DEFAULTSIZE)
cyHeight = is_icon(pIcon) ?
UserGetSystemMetrics(SM_CYICON) : UserGetSystemMetrics(SM_CYCURSOR);
else
cyHeight = pIcon->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");
goto Cleanup;
}
#if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
psurfOffScreen = SURFACE_AllocSurface(STYPE_BITMAP,
cxWidth, cyHeight, psurfDest->SurfObj.iBitmapFormat,
0, 0, NULL);
if(!psurfOffScreen)
{
ERR("Failed to allocate the off-screen surface.\n");
BRUSH_ShareUnlockBrush(pbrush);
goto Cleanup;
}
/* 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");
goto Cleanup;
}
/* We now have our destination surface */
psurfDest = psurfOffScreen;
#else
pdcClipObj = &pdc->co.ClipObj;
/* Paint the brush */
EBRUSHOBJ_vInit(&eboFill, pbrush, psurfDest, 0x00FFFFFF, 0, NULL);
Ret = IntEngBitBlt(&psurfDest->SurfObj,
NULL,
NULL,
pdcClipObj,
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");
goto Cleanup;
}
#endif
}
else
{
/* We directly draw to the DC */
TRACE("Performing on screen rendering.\n");
pdcClipObj = &pdc->co.ClipObj;
// psurfOffScreen = NULL;
}
/* Now do the rendering */
if(hbmAlpha && ((diFlags & DI_NORMAL) == DI_NORMAL))
{
BLENDOBJ blendobj = { {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA } };
PSURFACE psurf = NULL;
psurf = SURFACE_ShareLockSurface(hbmAlpha);
if(!psurf)
{
ERR("SURFACE_LockSurface failed!\n");
goto NoAlpha;
}
/* 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);
SURFACE_ShareUnlockSurface(psurf);
if(Ret) goto done;
ERR("NtGdiAlphaBlend failed!\n");
}
NoAlpha:
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->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:
#if 0
/* We're done. Was it a double buffered draw ? */
if(bOffScreen)
{
/* Yes. Draw it back to our DC */
POINTL ptSrc = {0, 0};
/* 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);
/* Get the clip object */
pdcClipObj = pdc->rosdc.CombinedClip;
/* We now have our destination surface and rectangle */
psurfDest = pdc->dclevel.pSurface;
/* 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);
}
#endif
Cleanup:
if(pdc)
{
DC_vFinishBlit(pdc, NULL);
DC_UnlockDc(pdc);
}
#if 0
/* Delete off screen rendering surface */
if(psurfOffScreen)
GDIOBJ_vDeleteObject(&psurfOffScreen->BaseObject);
#endif
/* 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(0x%08x) failed!\n", hIcon);
UserLeave();
return FALSE;
}
Ret = UserDrawIconEx(hdc,
xLeft,
yTop,
pIcon,
cxWidth,
cyHeight,
istepIfAniCur,
hbrFlickerFreeDraw,
diFlags);
UserDereferenceObject(pIcon);
UserLeave();
return Ret;
}
/*
* @unimplemented
*/
HCURSOR
NTAPI
NtUserGetCursorFrameInfo(
HCURSOR hCursor,
DWORD istep,
INT* rate_jiffies,
DWORD* num_steps)
{
PCURICON_OBJECT CurIcon;
HCURSOR ret;
INT jiffies = 0;
DWORD steps = 1;
NTSTATUS Status = STATUS_SUCCESS;
TRACE("Enter NtUserGetCursorFrameInfo\n");
UserEnterExclusive();
if (!(CurIcon = UserGetCurIconObject(hCursor)))
{
UserLeave();
return NULL;
}
ret = CurIcon->head.h;
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
PACON AniCurIcon = (PACON)CurIcon;
if(istep >= AniCurIcon->cicur)
{
UserDereferenceObject(CurIcon);
UserLeave();
return NULL;
}
jiffies = AniCurIcon->ajifRate[istep];
steps = AniCurIcon->cicur;
ret = AniCurIcon->aspcur[AniCurIcon->aicur[istep]]->head.h;
}
_SEH2_TRY
{
ProbeForWrite(rate_jiffies, sizeof(INT), 1);
ProbeForWrite(num_steps, sizeof(DWORD), 1);
*rate_jiffies = jiffies;
*num_steps = steps;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
WARN("Status: 0x%08x.\n", Status);
SetLastNtError(Status);
ret = NULL;
}
UserDereferenceObject(CurIcon);
UserLeave();
TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%08x\n", ret);
return ret;
}
/* EOF */