mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
fd331f1494
Addendum to 76290a6
(0.4.15-dev-7889).
2256 lines
59 KiB
C
2256 lines
59 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;
|
|
|
|
PCURICON_OBJECT gcurFirst = NULL; // After all is done, this should be WINLOGO!
|
|
|
|
//
|
|
// System Cursors
|
|
//
|
|
SYSTEMCURICO gasyscur[] = {
|
|
{OCR_NORMAL, NULL},
|
|
{OCR_IBEAM, NULL},
|
|
{OCR_WAIT, NULL},
|
|
{OCR_CROSS, NULL},
|
|
{OCR_UP, NULL},
|
|
{OCR_ICON, NULL},
|
|
{OCR_SIZE, NULL},
|
|
{OCR_SIZENWSE, NULL},
|
|
{OCR_SIZENESW, NULL},
|
|
{OCR_SIZEWE, NULL},
|
|
{OCR_SIZENS, NULL},
|
|
{OCR_SIZEALL, NULL},
|
|
{OCR_NO, NULL},
|
|
{OCR_HAND, NULL},
|
|
{OCR_APPSTARTING,NULL},
|
|
{OCR_HELP, NULL},
|
|
};
|
|
|
|
//
|
|
// System Icons
|
|
//
|
|
SYSTEMCURICO gasysico[] = {
|
|
{OIC_SAMPLE, NULL},
|
|
{OIC_HAND, NULL},
|
|
{OIC_QUES, NULL},
|
|
{OIC_BANG, NULL},
|
|
{OIC_NOTE, NULL},
|
|
{OIC_WINLOGO,NULL},
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
IntInsertCursorIntoList(
|
|
_Inout_ PCURICON_OBJECT pcur)
|
|
{
|
|
PPROCESSINFO ppi = pcur->head.ppi;
|
|
PCURICON_OBJECT *ppcurHead;
|
|
NT_ASSERT((pcur->CURSORF_flags & (CURSORF_GLOBAL|CURSORF_LRSHARED)) != 0);
|
|
NT_ASSERT((pcur->CURSORF_flags & CURSORF_LINKED) == 0);
|
|
|
|
/* Get the right list head */
|
|
ppcurHead = (pcur->CURSORF_flags & CURSORF_GLOBAL) ?
|
|
&gcurFirst : &ppi->pCursorCache;
|
|
|
|
UserReferenceObject(pcur);
|
|
pcur->pcurNext = *ppcurHead;
|
|
*ppcurHead = pcur;
|
|
pcur->CURSORF_flags |= CURSORF_LINKED;
|
|
}
|
|
|
|
// FIXME: should think about using a LIST_ENTRY!
|
|
static
|
|
VOID
|
|
IntRemoveCursorFromList(
|
|
_Inout_ PCURICON_OBJECT pcur)
|
|
{
|
|
PPROCESSINFO ppi = pcur->head.ppi;
|
|
PCURICON_OBJECT *ppcurHead;
|
|
PCURICON_OBJECT *ppcur;
|
|
NT_ASSERT((pcur->CURSORF_flags & (CURSORF_GLOBAL|CURSORF_LRSHARED)) != 0);
|
|
NT_ASSERT((pcur->CURSORF_flags & CURSORF_LINKED) != 0);
|
|
|
|
/* Get the right list head */
|
|
ppcurHead = (pcur->CURSORF_flags & CURSORF_GLOBAL) ?
|
|
&gcurFirst : &ppi->pCursorCache;
|
|
|
|
/* Loop all cursors in the cache */
|
|
for (ppcur = ppcurHead;
|
|
(*ppcur) != NULL;
|
|
ppcur = &(*ppcur)->pcurNext)
|
|
{
|
|
/* Check if this is the one we are looking for */
|
|
if ((*ppcur) == pcur)
|
|
{
|
|
/* Remove it from the list */
|
|
(*ppcur) = pcur->pcurNext;
|
|
|
|
/* Dereference it */
|
|
UserDereferenceObject(pcur);
|
|
pcur->CURSORF_flags &= ~CURSORF_LINKED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* We did not find it, this must not happen */
|
|
NT_ASSERT(FALSE);
|
|
}
|
|
|
|
VOID
|
|
IntLoadSystenIcons(HICON hcur, DWORD id)
|
|
{
|
|
PCURICON_OBJECT pcur;
|
|
int i;
|
|
PPROCESSINFO ppi;
|
|
|
|
if (hcur)
|
|
{
|
|
pcur = UserGetCurIconObject(hcur);
|
|
if (!pcur)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
return;
|
|
}
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
|
|
if (!(ppi->W32PF_flags & W32PF_CREATEDWINORDC))
|
|
return;
|
|
|
|
// Set Small Window Icon and do not link.
|
|
if ( id == OIC_WINLOGO+1 )
|
|
{
|
|
pcur->CURSORF_flags |= CURSORF_GLOBAL;
|
|
UserReferenceObject(pcur);
|
|
pcur->head.ppi = NULL;
|
|
return;
|
|
}
|
|
|
|
for (i = 0 ; i < 6; i++)
|
|
{
|
|
if (gasysico[i].type == id)
|
|
{
|
|
gasysico[i].handle = pcur;
|
|
pcur->CURSORF_flags |= CURSORF_GLOBAL;
|
|
|
|
//
|
|
// The active switch between LR shared and Global public.
|
|
// This is hacked around to support this while at the initial system start up.
|
|
//
|
|
pcur->head.ppi = NULL;
|
|
|
|
IntInsertCursorIntoList(pcur);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PSYSTEM_CURSORINFO
|
|
IntGetSysCursorInfo(VOID)
|
|
{
|
|
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;
|
|
}
|
|
|
|
PCURICON_OBJECT
|
|
IntSystemSetCursor(PCURICON_OBJECT pcurNew)
|
|
{
|
|
PCURICON_OBJECT pcurOld = UserSetCursor(pcurNew, FALSE);
|
|
if (pcurNew) UserReferenceObject(pcurNew);
|
|
if (pcurOld) UserDereferenceObject(pcurOld);
|
|
return pcurOld;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Nothing to do if position did not actually change */
|
|
if (x == gpsi->ptCursor.x && y == gpsi->ptCursor.y)
|
|
return TRUE;
|
|
|
|
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;
|
|
}
|
|
|
|
if (Animated)
|
|
{
|
|
/* We MUST set this flag, to track whether this is an ACON! */
|
|
CurIcon->CURSORF_flags |= CURSORF_ACON;
|
|
}
|
|
|
|
NT_ASSERT(CurIcon->pcurNext == NULL);
|
|
UserDereferenceObject(CurIcon);
|
|
|
|
return hCurIcon;
|
|
}
|
|
|
|
BOOLEAN
|
|
IntDestroyCurIconObject(
|
|
_In_ PVOID Object)
|
|
{
|
|
PCURICON_OBJECT CurIcon = Object;
|
|
|
|
/* Check if the cursor is in a list */
|
|
if (CurIcon->CURSORF_flags & CURSORF_LINKED)
|
|
{
|
|
/* Remove the cursor from it's list */
|
|
IntRemoveCursorFromList(CurIcon);
|
|
}
|
|
|
|
/* We just mark the handle as being destroyed.
|
|
* Deleting all the stuff will be deferred to the actual struct free. */
|
|
UserDeleteObject(UserHMGetHandle(CurIcon), 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);
|
|
NT_VERIFY(GreDeleteObject(bmpMask) == TRUE);
|
|
CurIcon->hbmMask = NULL;
|
|
}
|
|
if (bmpColor)
|
|
{
|
|
GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
|
|
NT_VERIFY(GreDeleteObject(bmpColor) == TRUE);
|
|
CurIcon->hbmColor = NULL;
|
|
}
|
|
if (bmpAlpha)
|
|
{
|
|
GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED);
|
|
NT_VERIFY(GreDeleteObject(bmpAlpha) == TRUE);
|
|
CurIcon->hbmAlpha = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PACON AniCurIcon = (PACON)CurIcon;
|
|
UINT i;
|
|
|
|
for (i = 0; i < AniCurIcon->cpcur; i++)
|
|
{
|
|
UserDereferenceObject(AniCurIcon->aspcur[i]);
|
|
NT_VERIFY(IntDestroyCurIconObject(AniCurIcon->aspcur[i]) == TRUE);
|
|
}
|
|
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
|
|
*/
|
|
_Success_(return != FALSE)
|
|
BOOL
|
|
NTAPI
|
|
NtUserGetIconInfo(
|
|
_In_ HANDLE hCurIcon,
|
|
_Out_opt_ PICONINFO IconInfo,
|
|
_Inout_opt_ PUNICODE_STRING lpModule,
|
|
_Inout_opt_ PUNICODE_STRING lpResName,
|
|
_Out_opt_ LPDWORD pbpp,
|
|
_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%p) 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%08lx\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");
|
|
UserEnterShared();
|
|
|
|
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;
|
|
|
|
TRACE("Enter NtUserGetCursorInfo\n");
|
|
UserEnterShared();
|
|
|
|
CurInfo = IntGetSysCursorInfo();
|
|
CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
|
|
|
|
SafeCi.cbSize = sizeof(CURSORINFO);
|
|
SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0);
|
|
SafeCi.hCursor = (CurIcon ? UserHMGetHandle(CurIcon) : 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);
|
|
}
|
|
|
|
TRACE("Leave NtUserGetCursorInfo, ret=%i\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
UserClipCursor(
|
|
RECTL *prcl)
|
|
{
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
PWND DesktopWindow = NULL;
|
|
|
|
if (!CheckWinstaAttributeAccess(WINSTA_WRITEATTRIBUTES))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
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, %i)\n", hCurIcon, bForce);
|
|
UserEnterExclusive();
|
|
|
|
CurIcon = UserGetCurIconObject(hCurIcon);
|
|
if (!CurIcon)
|
|
{
|
|
ret = FALSE;
|
|
goto leave;
|
|
}
|
|
|
|
if (!bForce)
|
|
{
|
|
/* Can not destroy global objects */
|
|
if (CurIcon->head.ppi == NULL)
|
|
{
|
|
ERR("Trying to delete global cursor!\n");
|
|
ret = TRUE;
|
|
goto leave;
|
|
}
|
|
|
|
/* 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(¶mSafe, 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;
|
|
}
|
|
|
|
UserEnterShared();
|
|
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;
|
|
}
|
|
|
|
/* Now search Global Cursors or Icons. */
|
|
if (CurIcon == NULL)
|
|
{
|
|
CurIcon = gcurFirst;
|
|
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 = UserHMGetHandle(CurIcon);
|
|
UserLeave();
|
|
|
|
done:
|
|
if (!IS_INTRESOURCE(ustrRsrcSafe.Buffer))
|
|
ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserGetClipCursor(
|
|
RECTL *lpRect)
|
|
{
|
|
PSYSTEM_CURSORINFO CurInfo;
|
|
RECTL Rect;
|
|
NTSTATUS Status;
|
|
BOOL Ret = FALSE;
|
|
|
|
TRACE("Enter NtUserGetClipCursor\n");
|
|
UserEnterShared();
|
|
|
|
if (!CheckWinstaAttributeAccess(WINSTA_READATTRIBUTES))
|
|
{
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
if (!lpRect)
|
|
goto Exit; // 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);
|
|
goto Exit; // Return FALSE
|
|
}
|
|
|
|
Ret = TRUE;
|
|
|
|
Exit:
|
|
TRACE("Leave NtUserGetClipCursor, ret=%i\n", Ret);
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @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 returning an old cursor than validate it, Justin Case!
|
|
if ( pcurOld &&
|
|
(pcurOld = UserGetObjectNoErr(gHandleTable, UserHMGetHandle(pcurOld), TYPE_CURSOR)))
|
|
{
|
|
hOldCursor = UserHMGetHandle(pcurOld);
|
|
/*
|
|
Problem:
|
|
|
|
System Global Cursors start out having at least 2 lock counts. If a system
|
|
cursor is the default cursor and is returned to the caller twice in its
|
|
life, the count will reach zero. Causing an assert to occur in objects.
|
|
|
|
This fixes a SeaMonkey crash while the mouse crosses a boundary.
|
|
*/
|
|
if (pcurOld->CURSORF_flags & CURSORF_GLOBAL)
|
|
{
|
|
TRACE("Returning Global Cursor hcur %p\n",hOldCursor);
|
|
|
|
/*if (pcurOld->head.cLockObj > 2) // Throttle down to 2.
|
|
{
|
|
UserDereferenceObject(pcurOld);
|
|
}
|
|
|
|
goto leave;*/
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
static
|
|
BOOL
|
|
IntSetCursorData(
|
|
_Inout_ PCURICON_OBJECT pcur,
|
|
_In_opt_ PUNICODE_STRING pustrName,
|
|
_In_ ATOM atomModName,
|
|
_In_ const CURSORDATA* pcursordata)
|
|
{
|
|
/* Check if the CURSORF_ACON is also set in the cursor data */
|
|
if (pcursordata->CURSORF_flags & CURSORF_ACON)
|
|
{
|
|
ERR("Mismatch in CURSORF_flags! cursor: 0x%08lx, data: 0x%08lx\n",
|
|
pcur->CURSORF_flags, pcursordata->CURSORF_flags);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if this cursor was already set */
|
|
if (pcur->hbmMask != NULL)
|
|
{
|
|
ERR("Cursor data already set!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* We need a mask */
|
|
if (pcursordata->hbmMask == NULL)
|
|
{
|
|
ERR("NtUserSetCursorIconData was got no hbmMask.\n");
|
|
EngSetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Take ownership of the mask bitmap */
|
|
if (!GreSetBitmapOwner(pcursordata->hbmMask, GDI_OBJ_HMGR_PUBLIC))
|
|
{
|
|
ERR("Failed to set ownership of hbmMask %p.\n", pcursordata->hbmMask);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if we have a color bitmap */
|
|
if (pcursordata->hbmColor)
|
|
{
|
|
/* Take ownership of the color bitmap */
|
|
if (!GreSetBitmapOwner(pcursordata->hbmColor, GDI_OBJ_HMGR_PUBLIC))
|
|
{
|
|
ERR("Failed to set ownership of hbmColor %p.\n", pcursordata->hbmColor);
|
|
GreSetBitmapOwner(pcursordata->hbmMask, GDI_OBJ_HMGR_POWNED);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check if we have an alpha bitmap */
|
|
if (pcursordata->hbmAlpha)
|
|
{
|
|
/* Take ownership of the alpha bitmap */
|
|
if (!GreSetBitmapOwner(pcursordata->hbmAlpha, GDI_OBJ_HMGR_PUBLIC))
|
|
{
|
|
ERR("Failed to set ownership of hbmAlpha %p.\n", pcursordata->hbmAlpha);
|
|
GreSetBitmapOwner(pcursordata->hbmMask, GDI_OBJ_HMGR_POWNED);
|
|
if (pcursordata->hbmColor)
|
|
{
|
|
GreSetBitmapOwner(pcursordata->hbmColor, GDI_OBJ_HMGR_POWNED);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Free the old name (Must be NULL atm, but later we might allow this) */
|
|
NT_ASSERT(pcur->strName.Buffer == NULL);
|
|
if (pcur->strName.Buffer != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(pcur->strName.Buffer))
|
|
{
|
|
ExFreePoolWithTag(pcur->strName.Buffer, TAG_STRING);
|
|
}
|
|
RtlInitEmptyUnicodeString(&pcur->strName, NULL, 0);
|
|
}
|
|
|
|
/* Free the module atom */
|
|
if (pcur->atomModName != 0)
|
|
{
|
|
NT_VERIFY(NT_SUCCESS(RtlDeleteAtomFromAtomTable(gAtomTable, pcur->atomModName)));
|
|
}
|
|
|
|
/* Now set the new cursor data */
|
|
pcur->atomModName = atomModName;
|
|
pcur->rt = pcursordata->rt;
|
|
pcur->CURSORF_flags = pcursordata->CURSORF_flags & CURSORF_USER_MASK;
|
|
pcur->xHotspot = pcursordata->xHotspot;
|
|
pcur->yHotspot = pcursordata->yHotspot;
|
|
pcur->hbmMask = pcursordata->hbmMask;
|
|
pcur->hbmColor = pcursordata->hbmColor;
|
|
pcur->hbmAlpha = pcursordata->hbmAlpha;
|
|
pcur->rcBounds.left = 0;
|
|
pcur->rcBounds.top = 0;
|
|
pcur->rcBounds.right = pcursordata->cx;
|
|
pcur->rcBounds.bottom = pcursordata->cy;
|
|
pcur->hbmUserAlpha = pcursordata->hbmUserAlpha;
|
|
pcur->bpp = pcursordata->bpp;
|
|
pcur->cx = pcursordata->cx;
|
|
pcur->cy = pcursordata->cy;
|
|
if (pustrName != NULL)
|
|
{
|
|
pcur->strName = *pustrName;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
IntSetAconData(
|
|
_Inout_ PACON pacon,
|
|
_In_opt_ PUNICODE_STRING pustrName,
|
|
_In_ ATOM atomModName,
|
|
_In_ const CURSORDATA *pcursordata)
|
|
{
|
|
PCURICON_OBJECT *aspcur;
|
|
DWORD *aicur;
|
|
INT *ajifRate;
|
|
PCURSORDATA pcdFrame;
|
|
HCURSOR hcurFrame;
|
|
UINT cjSize, i;
|
|
|
|
NT_ASSERT((pacon->CURSORF_flags & CURSORF_ACON) != 0);
|
|
NT_ASSERT((pacon->CURSORF_flags & CURSORF_ACONFRAME) == 0);
|
|
NT_ASSERT((ULONG_PTR)pcursordata->aspcur > MmUserProbeAddress);
|
|
NT_ASSERT((ULONG_PTR)pcursordata->aicur > MmUserProbeAddress);
|
|
NT_ASSERT((ULONG_PTR)pcursordata->ajifRate > MmUserProbeAddress);
|
|
NT_ASSERT((pcursordata->CURSORF_flags & ~CURSORF_USER_MASK) == 0);
|
|
NT_ASSERT(pcursordata->cpcur > 0);
|
|
NT_ASSERT(pcursordata->cicur > 0);
|
|
|
|
/* Check if the CURSORF_ACON is also set in the cursor data */
|
|
if (!(pcursordata->CURSORF_flags & CURSORF_ACON))
|
|
{
|
|
ERR("Mismatch in CURSORF_flags! acon: 0x%08lx, data: 0x%08lx\n",
|
|
pacon->CURSORF_flags, pcursordata->CURSORF_flags);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if this acon was already set */
|
|
if (pacon->aspcur != NULL)
|
|
{
|
|
ERR("Acon data already set!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Loop all frames indexes */
|
|
for (i = 0; i < pcursordata->cicur; i++)
|
|
{
|
|
/* Check if the index is within the range of the frames */
|
|
if (pcursordata->aicur[i] >= pcursordata->cpcur)
|
|
{
|
|
ERR("aicur[%lu] is out or range. Got %lu, cpcur = %u\n",
|
|
i, pcursordata->aicur[i], pcursordata->cpcur);
|
|
return FALSE;
|
|
}
|
|
|
|
/* FIXME: check the JIF rates? */
|
|
}
|
|
|
|
/* Calculate size: one cursor object for each frame, and a frame
|
|
index and jiffies for each "step" */
|
|
cjSize = (pcursordata->cpcur * sizeof(CURICON_OBJECT*)) +
|
|
(pcursordata->cicur * sizeof(DWORD)) +
|
|
(pcursordata->cicur * sizeof(INT));
|
|
|
|
/* Allocate a buffer */
|
|
aspcur = ExAllocatePoolWithTag(PagedPool, cjSize, USERTAG_CURSOR);
|
|
if (aspcur == NULL)
|
|
{
|
|
ERR("Failed to allocate memory (cpcur = %u, cicur = %u)\n",
|
|
pcursordata->cpcur, pcursordata->cicur);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the pointers */
|
|
aicur = (DWORD*)&aspcur[pcursordata->cpcur];
|
|
ajifRate = (INT*)&aicur[pcursordata->cicur];
|
|
|
|
/* Copy the values */
|
|
RtlCopyMemory(aicur, pcursordata->aicur, pcursordata->cicur * sizeof(DWORD));
|
|
RtlCopyMemory(ajifRate, pcursordata->ajifRate, pcursordata->cicur * sizeof(INT));
|
|
|
|
/* Zero out the array, so we can handle cleanup */
|
|
RtlZeroMemory(aspcur, pcursordata->cpcur * sizeof(PCURICON_OBJECT));
|
|
|
|
/* Get a pointer to the cursor data for each frame */
|
|
pcdFrame = pcursordata->aspcur;
|
|
|
|
/* Create the cursors */
|
|
for (i = 0; i < pcursordata->cpcur; i++)
|
|
{
|
|
/* Create a cursor for this frame */
|
|
hcurFrame = IntCreateCurIconHandle(FALSE);
|
|
if (hcurFrame == NULL)
|
|
{
|
|
ERR("Failed to create a cursor for frame %u\n", i);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Get a pointer to the frame cursor */
|
|
aspcur[i] = UserGetCurIconObject(hcurFrame);
|
|
_PRAGMA_WARNING_SUPPRESS(__WARNING_READ_OVERRUN);
|
|
NT_ASSERT(aspcur[i] != NULL);
|
|
|
|
/* Check if the flags are valid */
|
|
if (pcdFrame->CURSORF_flags & ~(CURSORF_USER_MASK|CURSORF_ACONFRAME))
|
|
{
|
|
ERR("Invalid flags for acon frame %u: 0x%08lx\n",
|
|
i, pcdFrame->CURSORF_flags);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Set the cursor data for this frame */
|
|
if (!IntSetCursorData(aspcur[i], NULL, 0, &pcdFrame[i]))
|
|
{
|
|
ERR("Failed to set cursor data for frame %u\n", i);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Mark this cursor as an acon frame */
|
|
aspcur[i]->CURSORF_flags |= CURSORF_ACONFRAME;
|
|
}
|
|
|
|
/* Free the old name (Must be NULL atm.) */
|
|
NT_ASSERT(pacon->strName.Buffer == NULL);
|
|
if (pacon->strName.Buffer != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(pacon->strName.Buffer))
|
|
{
|
|
ExFreePoolWithTag(pacon->strName.Buffer, TAG_STRING);
|
|
}
|
|
RtlInitEmptyUnicodeString(&pacon->strName, NULL, 0);
|
|
}
|
|
|
|
/* Free the module atom */
|
|
if (pacon->atomModName != 0)
|
|
{
|
|
NT_VERIFY(NT_SUCCESS(RtlDeleteAtomFromAtomTable(gAtomTable, pacon->atomModName)));
|
|
}
|
|
|
|
/* Free the previous frames */
|
|
if (pacon->aspcur != NULL)
|
|
{
|
|
for (i = 0; i < pacon->cpcur; i++)
|
|
{
|
|
UserDereferenceObject(pacon->aspcur[i]);
|
|
NT_VERIFY(IntDestroyCurIconObject(pacon->aspcur[i]) == TRUE);
|
|
}
|
|
ExFreePoolWithTag(pacon->aspcur, USERTAG_CURSOR);
|
|
}
|
|
|
|
/* Finally set the data in the acon */
|
|
pacon->atomModName = atomModName;
|
|
pacon->rt = pcursordata->rt;
|
|
pacon->CURSORF_flags = pcursordata->CURSORF_flags & CURSORF_USER_MASK;
|
|
pacon->cpcur = pcursordata->cpcur;
|
|
pacon->cicur = pcursordata->cicur;
|
|
pacon->aspcur = aspcur;
|
|
pacon->aicur = aicur;
|
|
pacon->ajifRate = ajifRate;
|
|
pacon->iicur = 0;
|
|
if (pustrName != NULL)
|
|
{
|
|
pacon->strName = *pustrName;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
Cleanup:
|
|
|
|
/* Clean up the cursors we created */
|
|
for (i = 0; i < pcursordata->cpcur; i++)
|
|
{
|
|
if (aspcur[i] == NULL)
|
|
break;
|
|
|
|
/* Destroy this cursor */
|
|
UserDereferenceObject(aspcur[i]);
|
|
NT_VERIFY(IntDestroyCurIconObject(aspcur[i]) == TRUE);
|
|
}
|
|
|
|
/* Delete the allocated structure */
|
|
ExFreePoolWithTag(aspcur, USERTAG_CURSOR);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
UserSetCursorIconData(
|
|
_In_ HCURSOR hcursor,
|
|
_In_opt_ PUNICODE_STRING pustrModule,
|
|
_In_opt_ PUNICODE_STRING pustrRsrc,
|
|
_In_ PCURSORDATA pcursordata)
|
|
{
|
|
PCURICON_OBJECT pcur;
|
|
ATOM atomModName;
|
|
NTSTATUS status;
|
|
BOOL bResult;
|
|
|
|
/* Do we have a module name? */
|
|
if (pustrModule != NULL)
|
|
{
|
|
/* Create an atom for the module name */
|
|
status = RtlAddAtomToAtomTable(gAtomTable,
|
|
pustrModule->Buffer,
|
|
&atomModName);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERR("Failed to create atom from module name '%wZ': 0x%08lx\n",
|
|
pustrModule, status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No module name atom */
|
|
atomModName = 0;
|
|
}
|
|
|
|
/* Reference the cursor */
|
|
pcur = UserGetCurIconObject(hcursor);
|
|
if (pcur == NULL)
|
|
{
|
|
ERR("Failed to reference cursor %p\n", hcursor);
|
|
bResult = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
/* Check if this is an acon */
|
|
if (pcur->CURSORF_flags & CURSORF_ACON)
|
|
{
|
|
bResult = IntSetAconData((PACON)pcur,
|
|
pustrRsrc,
|
|
atomModName,
|
|
pcursordata);
|
|
}
|
|
else
|
|
{
|
|
bResult = IntSetCursorData(pcur,
|
|
pustrRsrc,
|
|
atomModName,
|
|
pcursordata);
|
|
}
|
|
|
|
Exit:
|
|
|
|
/* Check if we had success */
|
|
if (bResult != FALSE)
|
|
{
|
|
/* Check if this is an LRSHARED cursor now */
|
|
if (pcur->CURSORF_flags & CURSORF_LRSHARED)
|
|
{
|
|
/* Insert the cursor into the list. */
|
|
IntInsertCursorIntoList(pcur);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Cleanup on failure */
|
|
if (atomModName != 0)
|
|
{
|
|
NT_VERIFY(NT_SUCCESS(RtlDeleteAtomFromAtomTable(gAtomTable, atomModName)));
|
|
}
|
|
}
|
|
|
|
/* Dereference the cursor and return the result */
|
|
if (pcur)
|
|
UserDereferenceObject(pcur);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
__kernel_entry
|
|
BOOL
|
|
APIENTRY
|
|
NtUserSetCursorIconData(
|
|
_In_ HCURSOR hcursor,
|
|
_In_opt_ PUNICODE_STRING pustrModule,
|
|
_In_opt_ PUNICODE_STRING pustrRsrc,
|
|
_In_ const CURSORDATA* pCursorData)
|
|
{
|
|
CURSORDATA cursordata;
|
|
UNICODE_STRING ustrModule, ustrRsrc;
|
|
_SEH2_VOLATILE PVOID pvBuffer;
|
|
CURSORDATA* aspcur;
|
|
DWORD* aicur;
|
|
PINT ajifRate;
|
|
UINT cjSize;
|
|
NTSTATUS status;
|
|
BOOL bResult = FALSE;
|
|
|
|
TRACE("Enter NtUserSetCursorIconData\n");
|
|
|
|
/* Initialize buffer, so we can handle cleanup */
|
|
ustrRsrc.Buffer = NULL;
|
|
ustrModule.Buffer = NULL;
|
|
pvBuffer = NULL;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe and capture the cursor data structure */
|
|
ProbeForRead(pCursorData, sizeof(*pCursorData), 1);
|
|
cursordata = *pCursorData;
|
|
|
|
/* Check if this is an animated cursor */
|
|
if (cursordata.CURSORF_flags & CURSORF_ACON)
|
|
{
|
|
/* Check of the range is ok */
|
|
if ((cursordata.cpcur == 0) || (cursordata.cicur == 0) ||
|
|
(cursordata.cpcur > 1000) || (cursordata.cicur > 1000))
|
|
{
|
|
ERR("Range error (cpcur = %u, cicur = %u)\n",
|
|
cursordata.cpcur, cursordata.cicur);
|
|
goto Exit;
|
|
}
|
|
|
|
/* Calculate size: one cursor data structure for each frame,
|
|
and a frame index and jiffies for each "step" */
|
|
cjSize = (cursordata.cpcur * sizeof(CURSORDATA)) +
|
|
(cursordata.cicur * sizeof(DWORD)) +
|
|
(cursordata.cicur * sizeof(INT));
|
|
|
|
/* Allocate a buffer */
|
|
pvBuffer = ExAllocatePoolWithTag(PagedPool, cjSize, USERTAG_CURSOR);
|
|
if (pvBuffer == NULL)
|
|
{
|
|
ERR("Failed to allocate memory (cpcur = %u, cicur = %u)\n",
|
|
cursordata.cpcur, cursordata.cicur);
|
|
goto Exit;
|
|
}
|
|
|
|
/* Calculate the kernel mode pointers */
|
|
aspcur = (CURSORDATA*)pvBuffer;
|
|
aicur = (DWORD*)&aspcur[cursordata.cpcur];
|
|
ajifRate = (INT*)&aicur[cursordata.cicur];
|
|
|
|
/* Probe and copy aspcur */
|
|
ProbeForRead(cursordata.aspcur, cursordata.cpcur * sizeof(CURSORDATA), 1);
|
|
RtlCopyMemory(aspcur,
|
|
cursordata.aspcur,
|
|
cursordata.cpcur * sizeof(CURSORDATA));
|
|
|
|
/* Probe and copy aicur */
|
|
ProbeForRead(cursordata.aicur, cursordata.cicur * sizeof(DWORD), 1);
|
|
RtlCopyMemory(aicur,
|
|
cursordata.aicur,
|
|
cursordata.cicur * sizeof(DWORD));
|
|
|
|
/* Probe and copy ajifRate */
|
|
ProbeForRead(cursordata.ajifRate, cursordata.cicur * sizeof(INT), 1);
|
|
RtlCopyMemory(ajifRate,
|
|
cursordata.ajifRate,
|
|
cursordata.cicur * sizeof(INT));
|
|
|
|
/* Set the new pointers */
|
|
cursordata.aspcur = aspcur;
|
|
cursordata.aicur = aicur;
|
|
cursordata.ajifRate = ajifRate;
|
|
}
|
|
else
|
|
{
|
|
/* This is a standard cursor, we don't use the pointers */
|
|
cursordata.aspcur = NULL;
|
|
cursordata.aicur = NULL;
|
|
cursordata.ajifRate = NULL;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SetLastNtError(_SEH2_GetExceptionCode());
|
|
goto Exit;
|
|
}
|
|
_SEH2_END
|
|
|
|
/* Check if we got a module name */
|
|
if (pustrModule != NULL)
|
|
{
|
|
/* Capture the name */
|
|
status = ProbeAndCaptureUnicodeString(&ustrModule, UserMode, pustrModule);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERR("Failed to copy pustrModule: status 0x%08lx\n", status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/* Check if we got a resource name */
|
|
if (pustrRsrc != NULL)
|
|
{
|
|
/* We use this function, because INTRESOURCEs and ATOMs are the same */
|
|
status = ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrc, pustrRsrc);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ERR("Failed to copy pustrRsrc: status 0x%08lx\n", status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
/* Make sure the caller doesn't give us invalid flags */
|
|
if (cursordata.CURSORF_flags & ~CURSORF_USER_MASK)
|
|
{
|
|
ERR("Invalid cursor flags: 0x%08lx\n", cursordata.CURSORF_flags);
|
|
goto Exit;
|
|
}
|
|
|
|
/* Acquire the global user lock */
|
|
UserEnterExclusive();
|
|
|
|
/* Call the internal function */
|
|
bResult = UserSetCursorIconData(hcursor,
|
|
pustrModule ? &ustrModule : NULL,
|
|
pustrRsrc ? &ustrRsrc : NULL,
|
|
&cursordata);
|
|
|
|
/* Release the global user lock */
|
|
UserLeave();
|
|
|
|
Exit:
|
|
|
|
/* Free the captured module name */
|
|
if ((ustrModule.Buffer != NULL) && !IS_INTRESOURCE(ustrModule.Buffer))
|
|
{
|
|
ReleaseCapturedUnicodeString(&ustrModule, UserMode);
|
|
}
|
|
|
|
if (pvBuffer != NULL)
|
|
{
|
|
ExFreePoolWithTag(pvBuffer, USERTAG_CURSOR);
|
|
}
|
|
|
|
/* Additional cleanup on failure */
|
|
if (bResult == FALSE)
|
|
{
|
|
if ((ustrRsrc.Buffer != NULL) &&
|
|
!IS_INTRESOURCE(ustrRsrc.Buffer))
|
|
{
|
|
ExFreePoolWithTag(ustrRsrc.Buffer, TAG_STRING);
|
|
}
|
|
}
|
|
|
|
TRACE("Leave NtUserSetCursorIconData, bResult = %i\n", bResult);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* 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 = (CLIPOBJ *)&pdc->co;
|
|
/* 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 = (CLIPOBJ *)&pdc->co;
|
|
// 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%p) 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");
|
|
UserEnterShared();
|
|
|
|
if (!(CurIcon = UserGetCurIconObject(hCursor)))
|
|
{
|
|
UserLeave();
|
|
return NULL;
|
|
}
|
|
|
|
ret = UserHMGetHandle(CurIcon);
|
|
|
|
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 = UserHMGetHandle(AniCurIcon->aspcur[AniCurIcon->aicur[istep]]);
|
|
}
|
|
|
|
_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%08lx\n", Status);
|
|
SetLastNtError(Status);
|
|
ret = NULL;
|
|
}
|
|
|
|
UserDereferenceObject(CurIcon);
|
|
UserLeave();
|
|
|
|
TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%p\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
APIENTRY
|
|
NtUserSetSystemCursor(
|
|
HCURSOR hcur,
|
|
DWORD id)
|
|
{
|
|
PCURICON_OBJECT pcur, pcurOrig = NULL;
|
|
int i;
|
|
PPROCESSINFO ppi;
|
|
BOOL Ret = FALSE;
|
|
UserEnterExclusive();
|
|
|
|
if (!CheckWinstaAttributeAccess(WINSTA_WRITEATTRIBUTES))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (hcur)
|
|
{
|
|
pcur = UserGetCurIconObject(hcur);
|
|
if (!pcur)
|
|
{
|
|
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
|
|
goto Exit;
|
|
}
|
|
|
|
ppi = PsGetCurrentProcessWin32Process();
|
|
|
|
for (i = 0 ; i < 16; i++)
|
|
{
|
|
if (gasyscur[i].type == id)
|
|
{
|
|
pcurOrig = gasyscur[i].handle;
|
|
|
|
if (pcurOrig) break;
|
|
|
|
if (ppi->W32PF_flags & W32PF_CREATEDWINORDC)
|
|
{
|
|
gasyscur[i].handle = pcur;
|
|
pcur->CURSORF_flags |= CURSORF_GLOBAL;
|
|
pcur->head.ppi = NULL;
|
|
IntInsertCursorIntoList(pcur);
|
|
Ret = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (pcurOrig)
|
|
{
|
|
FIXME("Need to copy cursor data or do something! pcurOrig %p new pcur %p\n",pcurOrig,pcur);
|
|
}
|
|
}
|
|
Exit:
|
|
UserLeave();
|
|
return Ret;
|
|
}
|
|
|
|
/* EOF */
|