diff --git a/dll/win32/kernel32/k32.h b/dll/win32/kernel32/k32.h index 77df40d5507..745fc78e90b 100644 --- a/dll/win32/kernel32/k32.h +++ b/dll/win32/kernel32/k32.h @@ -41,7 +41,6 @@ #include #include #include -//#include // FIXME: data header. /* C Headers */ #include diff --git a/win32ss/CMakeLists.txt b/win32ss/CMakeLists.txt index 0abce0e42c6..164a16118d9 100644 --- a/win32ss/CMakeLists.txt +++ b/win32ss/CMakeLists.txt @@ -1,5 +1,6 @@ set(USE_DIBLIB FALSE) +set(USE_NEW_CURSORICON FALSE) # Give WIN32 subsystem its own project. PROJECT(WIN32SS) @@ -103,7 +104,6 @@ list(APPEND SOURCE user/ntuser/class.c user/ntuser/clipboard.c user/ntuser/csr.c - user/ntuser/cursoricon.c user/ntuser/defwnd.c user/ntuser/desktop.c user/ntuser/display.c @@ -190,6 +190,13 @@ else() ${GENDIB_FILES}) endif() +if(USE_NEW_CURSORICON) + add_definitions(-DNEW_CURSORICON) + list(APPEND SOURCE user/ntuser/cursoricon_new.c) +else() + list(APPEND SOURCE user/ntuser/cursoricon.c) +endif() + if(ARCH STREQUAL "i386") list(APPEND SOURCE gdi/dib/i386/dib24bpp_hline.s diff --git a/win32ss/gdi/ntgdi/misc.h b/win32ss/gdi/ntgdi/misc.h index addd2591806..22297f05bf7 100644 --- a/win32ss/gdi/ntgdi/misc.h +++ b/win32ss/gdi/ntgdi/misc.h @@ -163,6 +163,6 @@ BOOL NTAPI W32kDosPathNameToNtPathName(PCWSTR, PUNICODE_STRING); #define LIST_FOR_EACH_SAFE(cursor, cursor2, list, type, field) \ for ((cursor) = CONTAINING_RECORD((list)->Flink, type, field), \ (cursor2) = CONTAINING_RECORD((cursor)->field.Flink, type, field); \ - &(cursor)->field != (list) && ((&((cursor)->field)) != NULL); \ + &(cursor)->field != (list); \ (cursor) = (cursor2), \ (cursor2) = CONTAINING_RECORD((cursor)->field.Flink, type, field)) diff --git a/win32ss/include/ntuser.h b/win32ss/include/ntuser.h index 3a0e9630d87..7c6d5a40f98 100644 --- a/win32ss/include/ntuser.h +++ b/win32ss/include/ntuser.h @@ -200,7 +200,6 @@ typedef struct tagHOOK struct _DESKTOP *rpdesk; /* ReactOS */ LIST_ENTRY Chain; /* Hook chain entry */ - struct _ETHREAD* Thread; /* Thread owning the hook */ HOOKPROC Proc; /* Hook function */ BOOLEAN Ansi; /* Is it an Ansi hook? */ UNICODE_STRING ModuleName; /* Module name for global hooks */ @@ -2739,14 +2738,24 @@ NtUserSetCursorContents( HANDLE Handle, PICONINFO IconInfo); -#if 0 // Correct type. +#ifdef NEW_CURSORICON BOOL NTAPI NtUserSetCursorIconData( - HCURSOR hCursor, - PUNICODE_STRING ModuleName, - PUNICODE_STRING ResourceName, - PCURSORDATA pCursorData); + _In_ HCURSOR hCursor, + _In_ HINSTANCE hinst, + _In_ HRSRC hrsrc, + _In_ PICONINFO pii); +#else +BOOL +NTAPI +NtUserSetCursorIconData( + HANDLE Handle, + PBOOL fIcon, + POINT *Hotspot, + HMODULE hModule, + HRSRC hRsrc, + HRSRC hGroupRsrc); #endif DWORD @@ -3361,16 +3370,6 @@ NtUserMonitorFromWindow( IN HWND hWnd, IN DWORD dwFlags); -BOOL -NTAPI -NtUserSetCursorIconData( - HANDLE Handle, - PBOOL fIcon, - POINT *Hotspot, - HMODULE hModule, - HRSRC hRsrc, - HRSRC hGroupRsrc); - typedef struct _SETSCROLLBARINFO { int nTrackPos; diff --git a/win32ss/user/ntuser/callproc.c b/win32ss/user/ntuser/callproc.c index 3d0f2fc998c..815cee50d81 100644 --- a/win32ss/user/ntuser/callproc.c +++ b/win32ss/user/ntuser/callproc.c @@ -46,6 +46,9 @@ CreateCallProc(IN PDESKTOP Desktop, NewCallProc->spcpdNext = NULL; } + /* Release the extra reference (UserCreateObject added 2 references) */ + UserDereferenceObject(NewCallProc); + return NewCallProc; } diff --git a/win32ss/user/ntuser/clipboard.c b/win32ss/user/ntuser/clipboard.c index e238e54a6be..72bac911b0f 100644 --- a/win32ss/user/ntuser/clipboard.c +++ b/win32ss/user/ntuser/clipboard.c @@ -211,6 +211,9 @@ IntSynthesizeDib( /* Add the clipboard data */ IntAddFormatedData(pWinStaObj, CF_DIB, hMem, TRUE, TRUE); + /* Release the extra reference (UserCreateObject added 2 references) */ + UserDereferenceObject(pClipboardData); + cleanup: UserReleaseDC(NULL, hdc, FALSE); } @@ -302,6 +305,9 @@ IntAddSynthesizedFormats(PWINSTATION_OBJECT pWinStaObj) *((LCID*)pMemObj->Data) = NtCurrentTeb()->CurrentLocale; IntAddFormatedData(pWinStaObj, CF_LOCALE, hMem, TRUE, TRUE); } + + /* Release the extra reference (UserCreateObject added 2 references) */ + UserDereferenceObject(pMemObj); } /* Add CF_TEXT. Note: it is synthesized in user32.dll */ @@ -1099,6 +1105,9 @@ NtUserConvertMemHandle( } _SEH2_END; + /* Release the extra reference (UserCreateObject added 2 references) */ + UserDereferenceObject(pMemObj); + /* If we failed to copy data, remove handle */ if (!pMemObj) { diff --git a/win32ss/user/ntuser/cursoricon.c b/win32ss/user/ntuser/cursoricon.c index 593e6ae7874..7ba56db0700 100644 --- a/win32ss/user/ntuser/cursoricon.c +++ b/win32ss/user/ntuser/cursoricon.c @@ -234,55 +234,54 @@ IntCreateCurIconHandle() } BOOLEAN FASTCALL -IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOL ProcessCleanup) +IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, PPROCESSINFO ppi) { PSYSTEM_CURSORINFO CurInfo; HBITMAP bmpMask, bmpColor; - BOOLEAN Ret; + BOOLEAN Ret, bListEmpty, bFound = FALSE; PCURICON_PROCESS Current = NULL; - PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process(); - - /* Private objects can only be destroyed by their own process */ - if (NULL == CurIcon->hModule) - { - ASSERT(CurIcon->ProcessList.Flink->Flink == &CurIcon->ProcessList); - Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry); - if (Current->Process != W32Process) - { - ERR("Trying to destroy private icon/cursor of another process\n"); - return FALSE; - } - } - else if (! ProcessCleanup) - { - TRACE("Trying to destroy shared icon/cursor\n"); - return FALSE; - } + + /* 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 == W32Process) + if (Current->Process == ppi) { - RemoveEntryList(&Current->ListEntry); + 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 (! IsListEmpty(&CurIcon->ProcessList)) + 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; } - - if (! ProcessCleanup) - { - RemoveEntryList(&CurIcon->ListEntry); - } +emptyList: + /* Remove it from the list */ + RemoveEntryList(&CurIcon->ListEntry); CurInfo = IntGetSysCursorInfo(); @@ -320,33 +319,13 @@ VOID FASTCALL IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process) { PCURICON_OBJECT CurIcon, tmp; - PCURICON_PROCESS ProcessData; + /* Run through the list of icon objects */ LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry) { UserReferenceObject(CurIcon); - // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon))) - { - LIST_FOR_EACH(ProcessData, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry) - { - if (Win32Process == ProcessData->Process) - { - RemoveEntryList(&CurIcon->ListEntry); - IntDestroyCurIconObject(CurIcon, TRUE); - CurIcon = NULL; - break; - } - } - -// UserDereferenceObject(Object); - } - - if (CurIcon) - { - UserDereferenceObject(CurIcon); - } + IntDestroyCurIconObject(CurIcon, Win32Process); } - } @@ -649,7 +628,7 @@ NtUserDestroyCursor( RETURN(FALSE); } - ret = IntDestroyCurIconObject(CurIcon, FALSE); + ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process()); /* Note: IntDestroyCurIconObject will remove our reference for us! */ RETURN(ret); diff --git a/win32ss/user/ntuser/cursoricon.h b/win32ss/user/ntuser/cursoricon.h index ca48c934f02..3bf5e19c4be 100644 --- a/win32ss/user/ntuser/cursoricon.h +++ b/win32ss/user/ntuser/cursoricon.h @@ -8,6 +8,31 @@ typedef struct tagCURICON_PROCESS PPROCESSINFO Process; } CURICON_PROCESS, *PCURICON_PROCESS; +#ifdef NEW_CURSORICON +typedef struct _CURICON_FRAME +{ + HBITMAP hbmMask; + HBITMAP hbmColor; + HBITMAP hbmAlpha; +} CURICON_FRAME, *PCURICON_FRAME; + +typedef struct _CURICON_OBJECT +{ + PROCMARKHEAD head; + LIST_ENTRY ListEntry; + HANDLE Self; + LIST_ENTRY ProcessList; + HMODULE hModule; + HRSRC hRsrc; + SIZE Size; + BYTE Shadow; + BOOL bIcon; + POINTL ptlHotspot; + CURICON_FRAME aFrame[1]; +} CURICON_OBJECT, *PCURICON_OBJECT; + +#else + typedef struct _CURICON_OBJECT { PROCMARKHEAD head; @@ -21,6 +46,7 @@ typedef struct _CURICON_OBJECT BYTE Shadow; ICONINFO IconInfo; } CURICON_OBJECT, *PCURICON_OBJECT; +#endif typedef struct _CURSORACCELERATION_INFO { diff --git a/win32ss/user/ntuser/cursoricon_new.c b/win32ss/user/ntuser/cursoricon_new.c new file mode 100644 index 00000000000..0c772a2155d --- /dev/null +++ b/win32ss/user/ntuser/cursoricon_new.c @@ -0,0 +1,1578 @@ +/* + * 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 +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() +{ + 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, bmpAlpha; + 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->aFrame[0].hbmMask; + bmpColor = CurIcon->aFrame[0].hbmColor; + bmpAlpha = CurIcon->aFrame[0].hbmAlpha; + + /* Delete bitmaps */ + if (bmpMask) + { + GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(bmpMask); + CurIcon->aFrame[0].hbmMask = NULL; + } + if (bmpColor) + { + GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(bmpColor); + CurIcon->aFrame[0].hbmColor = NULL; + } + if (bmpAlpha) + { + GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(bmpAlpha); + CurIcon->aFrame[0].hbmAlpha = 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; + } + + /* Fill data */ + ii.fIcon = CurIcon->bIcon; + ii.xHotspot = CurIcon->ptlHotspot.x; + ii.yHotspot = CurIcon->ptlHotspot.y; + + /* Copy bitmaps */ + ii.hbmMask = BITMAP_CopyBitmap(CurIcon->aFrame[0].hbmMask); + ii.hbmColor = BITMAP_CopyBitmap(CurIcon->aFrame[0].hbmColor); + + if (pbpp) + { + PSURFACE psurfBmp; + + psurfBmp = SURFACE_ShareLockSurface(CurIcon->aFrame[0].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( + HANDLE hCurIcon, + DWORD Unknown) +{ + 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; + } + +#if 0 + /* Check if we get valid information */ + if(IconInfo.fIcon != CurInfo->bIcon) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + goto done; + } +#endif + + /* Delete old bitmaps */ + if (CurIcon->aFrame[0].hbmColor) + GreDeleteObject(CurIcon->aFrame[0].hbmColor); + if (CurIcon->aFrame[0].hbmMask) + GreDeleteObject(CurIcon->aFrame[0].hbmMask); + if(CurIcon->aFrame[0].hbmAlpha) + GreDeleteObject(CurIcon->aFrame[0].hbmAlpha); + + /* Set fields */ + CurIcon->bIcon = IconInfo.fIcon; + CurIcon->ptlHotspot.x = IconInfo.xHotspot; + CurIcon->ptlHotspot.y = IconInfo.yHotspot; + CurIcon->aFrame[0].hbmMask = IconInfo.hbmMask; + CurIcon->aFrame[0].hbmColor = IconInfo.hbmColor; + CurIcon->aFrame[0].hbmAlpha = NULL; + + if (IconInfo.hbmColor) + { + BOOLEAN bAlpha = FALSE; + psurfBmp = SURFACE_ShareLockSurface(IconInfo.hbmColor); + if (!psurfBmp) + goto done; + CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx; + CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy; + + /* 32bpp bitmap is likely to have an alpha channel */ + if(psurfBmp->SurfObj.iBitmapFormat == BMF_32BPP) + { + PFN_DIB_GetPixel fn_GetPixel = DibFunctionsForBitmapFormat[BMF_32BPP].DIB_GetPixel; + INT i, j; + + fn_GetPixel = DibFunctionsForBitmapFormat[BMF_32BPP].DIB_GetPixel; + for (i = 0; i < psurfBmp->SurfObj.sizlBitmap.cx; i++) + { + for (j = 0; j < psurfBmp->SurfObj.sizlBitmap.cy; j++) + { + bAlpha = ((BYTE)(fn_GetPixel(&psurfBmp->SurfObj, i, j) >> 24)) != 0; + if (bAlpha) + break; + } + if (bAlpha) + break; + } + } + /* We're done with this one */ + SURFACE_ShareUnlockSurface(psurfBmp); + GreSetObjectOwner(IconInfo.hbmColor, GDI_OBJ_HMGR_PUBLIC); + + if(bAlpha) + { + UCHAR Alpha; + PUCHAR ptr; + INT i, j; + /* Copy the bitmap */ + CurIcon->aFrame[0].hbmAlpha = BITMAP_CopyBitmap(IconInfo.hbmColor); + if(!CurIcon->aFrame[0].hbmAlpha) + { + ERR("BITMAP_CopyBitmap failed!"); + goto done; + } + + psurfBmp = SURFACE_ShareLockSurface(CurIcon->aFrame[0].hbmAlpha); + if(!psurfBmp) + { + ERR("SURFACE_LockSurface failed!\n"); + goto done; + } + + /* Premultiply with the alpha channel value */ + for (i = 0; i < psurfBmp->SurfObj.sizlBitmap.cy; i++) + { + ptr = (PBYTE)psurfBmp->SurfObj.pvScan0 + i*psurfBmp->SurfObj.lDelta; + for (j = 0; j < psurfBmp->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; + } + } + SURFACE_ShareUnlockSurface(psurfBmp); + GreSetObjectOwner(CurIcon->aFrame[0].hbmAlpha, GDI_OBJ_HMGR_PUBLIC); + } + } + else + { + psurfBmp = SURFACE_ShareLockSurface(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(IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC); + + Ret = TRUE; + +done: + + if(!Ret) + { + IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process()); + CurIcon = NULL; + } + + if (CurIcon) + { + UserDereferenceObject(CurIcon); + } + RETURN(Ret); + +CLEANUP: + TRACE("Leave NtUserSetCursorContents, ret=%i\n",_ret_); + UserLeave(); + END_CLEANUP; +} + + +/* + * @implemented + */ +#ifdef NEW_CURSORICON +BOOL +APIENTRY +NtUserSetCursorIconData( + _In_ HCURSOR Handle, + _In_ HINSTANCE hinst, + _In_ HRSRC hrsrc, + _In_ PICONINFO pIconInfo) +{ + PCURICON_OBJECT CurIcon; + PSURFACE psurfBmp; + NTSTATUS Status = STATUS_SUCCESS; + BOOL Ret = FALSE; + DECLARE_RETURN(BOOL); + ICONINFO ii; + + TRACE("Enter NtUserSetCursorIconData\n"); + UserEnterExclusive(); + + if (!(CurIcon = UserGetCurIconObject(Handle))) + { + RETURN(FALSE); + } + + CurIcon->hModule = hinst; + CurIcon->hRsrc =hrsrc; + + _SEH2_TRY + { + ProbeForRead(pIconInfo, sizeof(ICONINFO), 1); + ii = *pIconInfo; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END + + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + goto done; + } + + /* This is probably not what windows does, but consistency checks can't hurt */ + if(CurIcon->bIcon != ii.fIcon) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + CurIcon->ptlHotspot.x = ii.xHotspot; + CurIcon->ptlHotspot.y = ii.yHotspot; + + if(!ii.hbmMask) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + CurIcon->aFrame[0].hbmMask = BITMAP_CopyBitmap(ii.hbmMask); + if(!CurIcon->aFrame[0].hbmMask) + goto done; + + if(ii.hbmColor) + { + CurIcon->aFrame[0].hbmColor = BITMAP_CopyBitmap(ii.hbmColor); + if(!CurIcon->aFrame[0].hbmColor) + goto done; + } + + if (CurIcon->aFrame[0].hbmColor) + { + if ((psurfBmp = SURFACE_ShareLockSurface(CurIcon->aFrame[0].hbmColor))) + { + CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx; + CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy; + SURFACE_ShareUnlockSurface(psurfBmp); + GreSetObjectOwner(CurIcon->aFrame[0].hbmMask, GDI_OBJ_HMGR_PUBLIC); + } + else + goto done; + } + else + { + if ((psurfBmp = SURFACE_ShareLockSurface(CurIcon->aFrame[0].hbmMask))) + { + CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx; + CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy/2; + SURFACE_ShareUnlockSurface(psurfBmp); + } + else + goto done; + } + GreSetObjectOwner(CurIcon->aFrame[0].hbmMask, GDI_OBJ_HMGR_PUBLIC); + + Ret = TRUE; + +done: + UserDereferenceObject(CurIcon); + if(!Ret) + { + if (CurIcon->aFrame[0].hbmMask) + { + GreSetObjectOwner(CurIcon->aFrame[0].hbmMask, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(CurIcon->aFrame[0].hbmMask); + CurIcon->aFrame[0].hbmMask = NULL; + } + if (CurIcon->aFrame[0].hbmColor) + { + GreSetObjectOwner(CurIcon->aFrame[0].hbmColor, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(CurIcon->aFrame[0].hbmColor); + CurIcon->aFrame[0].hbmColor = NULL; + } + } + 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; + 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->bIcon, fIcon, sizeof(BOOL)); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + goto done; + } + } + else + { + if (!Hotspot) + Ret = TRUE; + } + + if (Hotspot) + { + Status = MmCopyFromCaller(&CurIcon->ptlHotspot, Hotspot, sizeof(POINT)); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + goto done; + } + } + + if (!fIcon && !Hotspot) + { + Ret = TRUE; + } + +done: + if(Ret) + { + /* This icon is shared now */ + GreSetObjectOwner(CurIcon->aFrame[0].hbmMask, GDI_OBJ_HMGR_PUBLIC); + if(CurIcon->aFrame[0].hbmColor) + { + GreSetObjectOwner(CurIcon->aFrame[0].hbmColor, GDI_OBJ_HMGR_PUBLIC); + } + if(CurIcon->aFrame[0].hbmAlpha) + { + GreSetObjectOwner(CurIcon->aFrame[0].hbmAlpha, 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, 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; + } + + hbmMask = pIcon->aFrame[0].hbmMask; + hbmColor = pIcon->aFrame[0].hbmColor; + hbmAlpha = pIcon->aFrame[0].hbmAlpha; + + 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); + + /* Fix width parameter, if needed */ + if (!cxWidth) + { + if(diFlags & DI_DEFAULTSIZE) + cxWidth = pIcon->bIcon ? + UserGetSystemMetrics(SM_CXICON) : UserGetSystemMetrics(SM_CXCURSOR); + else + cxWidth = pIcon->Size.cx; + } + + /* Fix height parameter, if needed */ + if (!cyHeight) + { + if(diFlags & DI_DEFAULTSIZE) + cyHeight = pIcon->bIcon ? + 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; + } + + 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(hbmAlpha && (diFlags & DI_IMAGE)) + { + 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->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 */ diff --git a/win32ss/user/ntuser/hook.c b/win32ss/user/ntuser/hook.c index 31ebcbd486a..2c436918d68 100644 --- a/win32ss/user/ntuser/hook.c +++ b/win32ss/user/ntuser/hook.c @@ -250,8 +250,8 @@ co_IntCallLowLevelHook(PHOOK Hook, BOOL Block = FALSE; ULONG_PTR uResult = 0; - if (Hook->Thread) - pti = Hook->Thread->Tcb.Win32Thread; + if (Hook->ptiHooked) + pti = Hook->ptiHooked; else pti = Hook->head.pti; @@ -990,9 +990,9 @@ IntGetNextHook(PHOOK Hook) PLIST_ENTRY pLastHead, pElem; PTHREADINFO pti; - if (Hook->Thread) + if (Hook->ptiHooked) { - pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); + pti = Hook->ptiHooked; pLastHead = &pti->aphkStart[HOOKID_TO_INDEX(HookId)]; } else @@ -1035,9 +1035,9 @@ IntRemoveHook(PHOOK Hook) HookId = Hook->HookId; - if (Hook->Thread) // Local + if (Hook->ptiHooked) // Local { - pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); + pti = Hook->ptiHooked; IntFreeHook( Hook); @@ -1496,8 +1496,6 @@ NtUserSetWindowsHookEx( HINSTANCE Mod, ptiHook = Thread->Tcb.Win32Thread; - ObDereferenceObject(Thread); - if ( ptiHook->rpdesk != pti->rpdesk) // gptiCurrent->rpdesk) { ERR("Local hook wrong desktop HookId: %d\n",HookId); @@ -1577,7 +1575,6 @@ NtUserSetWindowsHookEx( HINSTANCE Mod, } Hook->ihmod = (INT)Mod; // Module Index from atom table, Do this for now. - Hook->Thread = Thread; /* Set Thread, Null is Global. */ Hook->HookId = HookId; Hook->rpdesk = ptiHook->rpdesk; Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */ @@ -1684,6 +1681,7 @@ NtUserSetWindowsHookEx( HINSTANCE Mod, CLEANUP: TRACE("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_); + if (Thread) ObDereferenceObject(Thread); UserLeave(); END_CLEANUP; } diff --git a/win32ss/user/ntuser/msgqueue.c b/win32ss/user/ntuser/msgqueue.c index 084f04ddfe7..0ae86125932 100644 --- a/win32ss/user/ntuser/msgqueue.c +++ b/win32ss/user/ntuser/msgqueue.c @@ -145,6 +145,15 @@ UserSetCursor( if (NewCursor) { /* Call GDI to set the new screen cursor */ +#ifdef NEW_CURSORICON + GreSetPointerShape(hdcScreen, + NewCursor->aFrame[0].hbmMask, + NewCursor->aFrame[0].hbmColor, + NewCursor->ptlHotspot.x, + NewCursor->ptlHotspot.y, + gpsi->ptCursor.x, + gpsi->ptCursor.y); +#else GreSetPointerShape(hdcScreen, NewCursor->IconInfo.hbmMask, NewCursor->IconInfo.hbmColor, @@ -152,6 +161,7 @@ UserSetCursor( NewCursor->IconInfo.yHotspot, gpsi->ptCursor.x, gpsi->ptCursor.y); +#endif } else /* Note: OldCursor != NewCursor so we have to hide cursor */ { @@ -571,13 +581,23 @@ co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook if(CurInfo->CurrentCursorObject != MessageQueue->CursorObject) { /* Call GDI to set the new screen cursor */ +#ifdef NEW_CURSORICON GreSetPointerShape(hdcScreen, + MessageQueue->CursorObject->aFrame[0].hbmMask, + MessageQueue->CursorObject->aFrame[0].hbmColor, + MessageQueue->CursorObject->ptlHotspot.x, + MessageQueue->CursorObject->ptlHotspot.y, + gpsi->ptCursor.x, + gpsi->ptCursor.y); +#else + GreSetPointerShape(hdcScreen, MessageQueue->CursorObject->IconInfo.hbmMask, MessageQueue->CursorObject->IconInfo.hbmColor, MessageQueue->CursorObject->IconInfo.xHotspot, MessageQueue->CursorObject->IconInfo.yHotspot, gpsi->ptCursor.x, gpsi->ptCursor.y); +#endif } else GreMovePointer(hdcScreen, Msg->pt.x, Msg->pt.y); } diff --git a/win32ss/user/ntuser/object.c b/win32ss/user/ntuser/object.c index 94242ef83fe..d7785754073 100644 --- a/win32ss/user/ntuser/object.c +++ b/win32ss/user/ntuser/object.c @@ -516,6 +516,38 @@ UserReferenceObjectByHandle(HANDLE handle, USER_OBJECT_TYPE type) return object; } +VOID +FASTCALL +UserSetObjectOwner(PVOID obj, USER_OBJECT_TYPE type, PVOID owner) +{ + PUSER_HANDLE_ENTRY entry = handle_to_entry(gHandleTable, ((PHEAD)obj)->h ); + PPROCESSINFO ppi, oldppi; + + /* This must be called with a valid object */ + ASSERT(entry); + + /* For now, only supported for CursorIcon object */ + switch(type) + { + case otCursorIcon: + ppi = (PPROCESSINFO)owner; + entry->pi = ppi; + oldppi = ((PPROCMARKHEAD)obj)->ppi; + ((PPROCMARKHEAD)obj)->ppi = ppi; + break; + default: + ASSERT(FALSE); + return; + } + + oldppi->UserHandleCount--; + ppi->UserHandleCount++; +#if DBG + oldppi->DbgHandleCount[type]--; + ppi->DbgHandleCount[type]++; +#endif +} + /* * NtUserValidateHandleSecure * diff --git a/win32ss/user/ntuser/object.h b/win32ss/user/ntuser/object.h index 976fd463af6..08ae96ebcb2 100644 --- a/win32ss/user/ntuser/object.h +++ b/win32ss/user/ntuser/object.h @@ -40,6 +40,7 @@ PVOID UserGetObjectNoErr(PUSER_HANDLE_TABLE, HANDLE, USER_OBJECT_TYPE); BOOL FASTCALL UserCreateHandleTable(VOID); BOOL FASTCALL UserObjectInDestroy(HANDLE); void DbgUserDumpHandleTable(); +VOID FASTCALL UserSetObjectOwner(PVOID obj, USER_OBJECT_TYPE type, PVOID owner); static __inline VOID UserRefObjectCo(PVOID obj, PUSER_REFERENCE_ENTRY UserReferenceEntry) diff --git a/win32ss/user/ntuser/painting.c b/win32ss/user/ntuser/painting.c index e7145fd0826..f85adff9b7c 100644 --- a/win32ss/user/ntuser/painting.c +++ b/win32ss/user/ntuser/painting.c @@ -2003,6 +2003,7 @@ BOOL UserDrawCaption( LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does LONG y = (Rect.top + Rect.bottom)/2 - cy/2; // center UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL); + UserDereferenceObject(pIcon); } } diff --git a/win32ss/user/ntuser/winsta.c b/win32ss/user/ntuser/winsta.c index 64039cbfbce..13adc556057 100644 --- a/win32ss/user/ntuser/winsta.c +++ b/win32ss/user/ntuser/winsta.c @@ -526,7 +526,7 @@ NtUserCloseWindowStation( if (hWinSta == UserGetProcessWindowStation()) { - ERR("Attempted to close process window station"); + ERR("Attempted to close process window station\n"); return FALSE; } @@ -612,19 +612,13 @@ NtUserGetObjectInformation( /* try windowstation */ TRACE("Trying to open window station 0x%x\n", hObject); - Status = IntValidateWindowStationHandle( + Status = ObReferenceObjectByHandle( hObject, - UserMode, 0, - &WinStaObject); - - - if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_TYPE_MISMATCH) - { - TRACE("Failed: 0x%x\n", Status); - SetLastNtError(Status); - return FALSE; - } + ExWindowStationObjectType, + UserMode, + (PVOID*)&WinStaObject, + NULL); if (Status == STATUS_OBJECT_TYPE_MISMATCH) { @@ -635,13 +629,15 @@ NtUserGetObjectInformation( UserMode, 0, &DesktopObject); - if (!NT_SUCCESS(Status)) - { - TRACE("Failed: 0x%x\n", Status); - SetLastNtError(Status); - return FALSE; - } } + + if (!NT_SUCCESS(Status)) + { + ERR("Failed: 0x%x\n", Status); + SetLastNtError(Status); + return FALSE; + } + TRACE("WinSta or Desktop opened!!\n"); /* get data */ @@ -713,8 +709,13 @@ NtUserGetObjectInformation( if (DesktopObject != NULL) ObDereferenceObject(DesktopObject); - SetLastNtError(Status); - return NT_SUCCESS(Status); + if (!NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + + return TRUE; } /* diff --git a/win32ss/user/user32/CMakeLists.txt b/win32ss/user/user32/CMakeLists.txt index b67b30d33e7..8c5930d14a9 100644 --- a/win32ss/user/user32/CMakeLists.txt +++ b/win32ss/user/user32/CMakeLists.txt @@ -41,7 +41,7 @@ list(APPEND SOURCE windows/caret.c windows/class.c windows/clipboard.c - windows/cursoricon.c +# windows/cursoricon.c windows/dc.c windows/defwnd.c windows/dialog.c @@ -64,6 +64,12 @@ list(APPEND SOURCE user32.rc ${CMAKE_CURRENT_BINARY_DIR}/user32.def) +if(USE_NEW_CURSORICON) + list(APPEND SOURCE windows/cursoricon_new.c) +else() + list(APPEND SOURCE windows/cursoricon.c) +endif() + add_library(user32 SHARED ${SOURCE}) set_module_type(user32 win32dll UNICODE) diff --git a/win32ss/user/user32/misc/exticon.c b/win32ss/user/user32/misc/exticon.c index 00b3b3e87da..7455f22cd83 100644 --- a/win32ss/user/user32/misc/exticon.c +++ b/win32ss/user/user32/misc/exticon.c @@ -30,30 +30,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(icon); -#include "pshpack1.h" - -typedef struct -{ - BYTE bWidth; /* Width, in pixels, of the image */ - BYTE bHeight; /* Height, in pixels, of the image */ - BYTE bColorCount; /* Number of colors in image (0 if >=8bpp) */ - BYTE bReserved; /* Reserved ( must be 0) */ - WORD wPlanes; /* Color Planes */ - WORD wBitCount; /* Bits per pixel */ - DWORD dwBytesInRes; /* How many bytes in this resource? */ - DWORD dwImageOffset; /* Where in the file is this image? */ -} icoICONDIRENTRY, *LPicoICONDIRENTRY; - -typedef struct -{ - WORD idReserved; /* Reserved (must be 0) */ - WORD idType; /* Resource Type (RES_ICON or RES_CURSOR) */ - WORD idCount; /* How many images */ - icoICONDIRENTRY idEntries[1]; /* An entry for each image (idCount of 'em) */ -} icoICONDIR, *LPicoICONDIR; - -#include "poppack.h" - #if 0 static void dumpIcoDirEnty ( LPicoICONDIRENTRY entry ) { diff --git a/win32ss/user/user32/misc/object.c b/win32ss/user/user32/misc/object.c index 5c97e0a521c..a40eef27c83 100644 --- a/win32ss/user/user32/misc/object.c +++ b/win32ss/user/user32/misc/object.c @@ -48,7 +48,8 @@ GetUserObjectInformationA( LPDWORD lpnLengthNeeded) { LPWSTR buffer; - BOOL ret = TRUE; + BOOL ret = FALSE; + DWORD LengthNeeded; TRACE("GetUserObjectInformationA(%x %d %x %d %x)\n", hObj, nIndex, pvInfo, nLength, lpnLengthNeeded); @@ -65,17 +66,16 @@ GetUserObjectInformationA( } /* get unicode string */ - if (!GetUserObjectInformationW(hObj, nIndex, buffer, nLength*2, lpnLengthNeeded)) - ret = FALSE; - *lpnLengthNeeded /= 2; - - if (ret) + if (GetUserObjectInformationW(hObj, nIndex, buffer, nLength*2, lpnLengthNeeded)) { /* convert string */ - if (WideCharToMultiByte(CP_THREAD_ACP, 0, buffer, -1, - pvInfo, nLength, NULL, NULL) == 0) + LengthNeeded = WideCharToMultiByte(CP_THREAD_ACP, 0, buffer, -1, + pvInfo, nLength, NULL, NULL); + + if (LengthNeeded != 0) { - ret = FALSE; + *lpnLengthNeeded = LengthNeeded; + ret = TRUE; } } diff --git a/win32ss/user/user32/windows/cursoricon_new.c b/win32ss/user/user32/windows/cursoricon_new.c new file mode 100644 index 00000000000..d1276192ad6 --- /dev/null +++ b/win32ss/user/user32/windows/cursoricon_new.c @@ -0,0 +1,756 @@ +/* + * PROJECT: ReactOS user32.dll + * COPYRIGHT: GPL - See COPYING in the top level directory + * FILE: dll/win32/user32/windows/class.c + * PURPOSE: Window classes + * PROGRAMMER: Jérôme Gardou (jerome.gardou@reactos.org) + */ + +#include + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(cursor); +//WINE_DECLARE_DEBUG_CHANNEL(icon); +//WINE_DECLARE_DEBUG_CHANNEL(resource); + +/************* USER32 INTERNAL FUNCTIONS **********/ + +/* This callback routine is called directly after switching to gui mode */ +NTSTATUS +WINAPI +User32SetupDefaultCursors(PVOID Arguments, + ULONG ArgumentLength) +{ + BOOL *DefaultCursor = (BOOL*)Arguments; + HCURSOR hCursor; + + if(*DefaultCursor) + { + /* set default cursor */ + hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); + SetCursor(hCursor); + } + else + { + /* FIXME load system cursor scheme */ + SetCursor(0); + hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); + SetCursor(hCursor); + } + + return(ZwCallbackReturn(&hCursor, sizeof(HCURSOR), STATUS_SUCCESS)); +} + +BOOL get_icon_size(HICON hIcon, SIZE *size) +{ + return NtUserGetIconSize(hIcon, 0, &size->cx, &size->cy); +} + +HCURSOR CursorIconToCursor(HICON hIcon, BOOL SemiTransparent) +{ + UNIMPLEMENTED; + return NULL; +} + +/************* IMPLEMENTATION HELPERS ******************/ + +static int get_dib_image_size( int width, int height, int depth ) +{ + return (((width * depth + 31) / 8) & ~3) * abs( height ); +} + +static BOOL is_dib_monochrome( const BITMAPINFO* info ) +{ + if (info->bmiHeader.biBitCount != 1) return FALSE; + + if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) + { + const RGBTRIPLE *rgb = ((const BITMAPCOREINFO*)info)->bmciColors; + + /* Check if the first color is black */ + if ((rgb->rgbtRed == 0) && (rgb->rgbtGreen == 0) && (rgb->rgbtBlue == 0)) + { + rgb++; + + /* Check if the second color is white */ + return ((rgb->rgbtRed == 0xff) && (rgb->rgbtGreen == 0xff) + && (rgb->rgbtBlue == 0xff)); + } + else return FALSE; + } + else /* assume BITMAPINFOHEADER */ + { + const RGBQUAD *rgb = info->bmiColors; + + /* Check if the first color is black */ + if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && + (rgb->rgbBlue == 0) && (rgb->rgbReserved == 0)) + { + rgb++; + + /* Check if the second color is white */ + return ((rgb->rgbRed == 0xff) && (rgb->rgbGreen == 0xff) + && (rgb->rgbBlue == 0xff) && (rgb->rgbReserved == 0)); + } + else return FALSE; + } +} + +static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) +{ + unsigned int colors, size, masks = 0; + + if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER)) + { + const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)info; + colors = (core->bcBitCount <= 8) ? 1 << core->bcBitCount : 0; + return sizeof(BITMAPCOREHEADER) + colors * + ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBTRIPLE) : sizeof(WORD)); + } + else /* assume BITMAPINFOHEADER */ + { + colors = info->bmiHeader.biClrUsed; + if (colors > 256) /* buffer overflow otherwise */ + colors = 256; + if (!colors && (info->bmiHeader.biBitCount <= 8)) + colors = 1 << info->bmiHeader.biBitCount; + if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3; + size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) ); + return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD)); + } +} + +/************* IMPLEMENTATION CORE ****************/ + +static BOOL CURSORICON_GetIconInfoFromBMI( + _Inout_ ICONINFO* pii, + _In_ const BITMAPINFO *pbmi, + _In_ int cxDesired, + _In_ int cyDesired +) +{ + UINT ubmiSize = bitmap_info_size(pbmi, DIB_RGB_COLORS); + BOOL monochrome = is_dib_monochrome(pbmi); + HDC hdc, hdcScreen; + BITMAPINFO* pbmiCopy; + HBITMAP hbmpOld = NULL; + BOOL bResult = FALSE; + const VOID *pvColor, *pvMask; + + /* Check for invalid data */ + if ( (pbmi->bmiHeader.biSize != sizeof(BITMAPCOREHEADER) && + pbmi->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) || + pbmi->bmiHeader.biCompression != BI_RGB ) + { + WARN("Invalid resource bitmap header.\n"); + return FALSE; + } + + /* Fix the hotspot coords */ + if(cxDesired != pbmi->bmiHeader.biWidth) + pii->xHotspot = (pii->xHotspot * cxDesired) / pbmi->bmiHeader.biWidth; + if(cxDesired != (pbmi->bmiHeader.biHeight/2)) + pii->yHotspot = (pii->yHotspot * cyDesired * 2) / pbmi->bmiHeader.biHeight; + + hdcScreen = CreateDCW(L"DISPLAY", NULL, NULL, NULL); + if(!hdcScreen) + return FALSE; + hdc = CreateCompatibleDC(hdcScreen); + if(!hdc) + { + DeleteDC(hdcScreen); + return FALSE; + } + + pbmiCopy = HeapAlloc(GetProcessHeap(), 0, max(ubmiSize, FIELD_OFFSET(BITMAPINFO, bmiColors[3]))); + if(!pbmiCopy) + goto done; + RtlCopyMemory(pbmiCopy, pbmi, ubmiSize); + pbmiCopy->bmiHeader.biHeight /= 2; + + pvColor = (const char*)pbmi + ubmiSize; + pvMask = (const char*)pvColor + + get_dib_image_size(pbmi->bmiHeader.biWidth, pbmiCopy->bmiHeader.biHeight, pbmi->bmiHeader.biBitCount ); + + /* Set XOR bits */ + if(monochrome) + { + /* Create the 1bpp bitmap which will contain everything */ + pii->hbmColor = NULL; + pii->hbmMask = CreateCompatibleBitmap(hdc, cxDesired, cyDesired * 2); + if(!pii->hbmMask) + goto done; + hbmpOld = SelectObject(hdc, pii->hbmMask); + if(!hbmpOld) + goto done; + + if(!StretchDIBits(hdc, 0, cyDesired, cxDesired, cyDesired, + 0, 0, pbmiCopy->bmiHeader.biWidth, pbmiCopy->bmiHeader.biHeight, + pvColor, pbmiCopy, DIB_RGB_COLORS, SRCCOPY)) + goto done; + } + else + { + /* Create the bitmap. It has to be compatible with the screen surface */ + pii->hbmColor = CreateCompatibleBitmap(hdcScreen, cxDesired, cyDesired); + if(!pii->hbmColor) + goto done; + /* Create the 1bpp mask bitmap */ + pii->hbmMask = CreateCompatibleBitmap(hdc, cxDesired, cyDesired); + if(!pii->hbmMask) + goto done; + hbmpOld = SelectObject(hdc, pii->hbmColor); + if(!hbmpOld) + goto done; + if(!StretchDIBits(hdc, 0, 0, cxDesired, cyDesired, + 0, 0, pbmiCopy->bmiHeader.biWidth, pbmiCopy->bmiHeader.biHeight, + pvColor, pbmiCopy, DIB_RGB_COLORS, SRCCOPY)) + goto done; + + /* Now convert the info to monochrome for the mask bits */ + pbmiCopy->bmiHeader.biBitCount = 1; + /* Handle the CORE/INFO difference */ + if (pbmiCopy->bmiHeader.biSize != sizeof(BITMAPCOREHEADER)) + { + RGBQUAD *rgb = pbmiCopy->bmiColors; + + pbmiCopy->bmiHeader.biClrUsed = pbmiCopy->bmiHeader.biClrImportant = 2; + rgb[0].rgbBlue = rgb[0].rgbGreen = rgb[0].rgbRed = 0x00; + rgb[1].rgbBlue = rgb[1].rgbGreen = rgb[1].rgbRed = 0xff; + rgb[0].rgbReserved = rgb[1].rgbReserved = 0; + } + else + { + RGBTRIPLE *rgb = (RGBTRIPLE *)(((BITMAPCOREHEADER *)pbmiCopy) + 1); + + rgb[0].rgbtBlue = rgb[0].rgbtGreen = rgb[0].rgbtRed = 0x00; + rgb[1].rgbtBlue = rgb[1].rgbtGreen = rgb[1].rgbtRed = 0xff; + } + } + /* Set the mask bits */ + if(!SelectObject(hdc, pii->hbmMask)) + goto done; + bResult = StretchDIBits(hdc, 0, 0, cxDesired, cyDesired, + 0, 0, pbmiCopy->bmiHeader.biWidth, pbmiCopy->bmiHeader.biHeight, + pvMask, pbmiCopy, DIB_RGB_COLORS, SRCCOPY) != 0; + +done: + DeleteDC(hdcScreen); + if(hbmpOld) SelectObject(hdc, hbmpOld); + DeleteDC(hdc); + if(pbmiCopy) HeapFree(GetProcessHeap(), 0, pbmiCopy); + /* Clean up in case of failure */ + if(!bResult) + { + if(pii->hbmMask) DeleteObject(pii->hbmMask); + if(pii->hbmColor) DeleteObject(pii->hbmColor); + } + return bResult; +} + +static +HANDLE +BITMAP_LoadImageW( + _In_opt_ HINSTANCE hinst, + _In_ LPCWSTR lpszName, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT fuLoad +) +{ + UNIMPLEMENTED; + return NULL; +} + +static +HANDLE +CURSORICON_LoadImageW( + _In_opt_ HINSTANCE hinst, + _In_ LPCWSTR lpszName, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT fuLoad, + _In_ BOOL bIcon +) +{ + HRSRC hrsrc, hrsrc2; + HANDLE handle, hCurIcon; + CURSORICONDIR* dir; + WORD wResId; + LPBYTE bits; + ICONINFO ii; + BOOL bStatus; + + if(fuLoad & LR_LOADFROMFILE) + { + UNIMPLEMENTED; + return NULL; + } + + /* Check if caller wants OEM icons */ + if(!hinst) + hinst = User32Instance; + + /* Find resource ID */ + hrsrc = FindResourceW( + hinst, + lpszName, + (LPWSTR)(bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR)); + + /* We let FindResource, LoadResource, etc. call SetLastError */ + if(!hrsrc) + return NULL; + + /* Fix width/height */ + if(fuLoad & LR_DEFAULTSIZE) + { + if(!cxDesired) cxDesired = GetSystemMetrics(bIcon ? SM_CXICON : SM_CXCURSOR); + if(!cyDesired) cyDesired = GetSystemMetrics(bIcon ? SM_CYICON : SM_CYCURSOR); + } + + /* If LR_SHARED is set, we must check for the cache */ + hCurIcon = NtUserFindExistingCursorIcon(hinst, hrsrc, cxDesired, cyDesired); + if(hCurIcon) + return hCurIcon; + + handle = LoadResource(hinst, hrsrc); + if(!handle) + return NULL; + + dir = LockResource(handle); + if(!dir) return NULL; + + /* For now, take the first entry */ + wResId = dir->idEntries[0].wResId; + FreeResource(handle); + + /* Get the relevant resource pointer */ + hrsrc2 = FindResourceW( + hinst, + MAKEINTRESOURCEW(wResId), + (LPWSTR)(bIcon ? RT_ICON : RT_CURSOR)); + if(!hrsrc2) + return NULL; + + handle = LoadResource(hinst, hrsrc2); + if(!handle) + return NULL; + + bits = LockResource(handle); + if(!bits) + { + FreeResource(handle); + return NULL; + } + + /* Get the hospot */ + if(bIcon) + { + ii.xHotspot = cxDesired/2; + ii.yHotspot = cyDesired/2; + } + else + { + SHORT* ptr = (SHORT*)bits; + ii.xHotspot = ptr[0]; + ii.yHotspot = ptr[1]; + bits += 2*sizeof(SHORT); + } + ii.fIcon = bIcon; + + /* Get the bitmaps */ + bStatus = CURSORICON_GetIconInfoFromBMI( + &ii, + (BITMAPINFO*)bits, + cxDesired, + cyDesired); + + FreeResource( handle ); + + if(!bStatus) + return NULL; + + /* Create the handle */ + hCurIcon = NtUserxCreateEmptyCurObject(bIcon ? 0 : 1); + if(!hCurIcon) + return NULL; + + /* Tell win32k */ + if(fuLoad & LR_SHARED) + bStatus = NtUserSetCursorIconData(hCurIcon, hinst, hrsrc, &ii); + else + bStatus = NtUserSetCursorIconData(hCurIcon, NULL, NULL, &ii); + + if(!bStatus) + { + NtUserDestroyCursor(hCurIcon, TRUE); + hCurIcon = NULL; + } + + DeleteObject(ii.hbmMask); + DeleteObject(ii.hbmColor); + + return hCurIcon; +} + +/************* PUBLIC FUNCTIONS *******************/ + +HANDLE WINAPI CopyImage( + _In_ HANDLE hImage, + _In_ UINT uType, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT fuFlags +) +{ + UNIMPLEMENTED; + return NULL; +} + +HICON WINAPI CopyIcon( + _In_ HICON hIcon +) +{ + UNIMPLEMENTED; + return NULL; +} + +BOOL WINAPI DrawIcon( + _In_ HDC hDC, + _In_ int X, + _In_ int Y, + _In_ HICON hIcon +) +{ + return DrawIconEx(hDC, X, Y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE); +} + +BOOL WINAPI DrawIconEx( + _In_ HDC hdc, + _In_ int xLeft, + _In_ int yTop, + _In_ HICON hIcon, + _In_ int cxWidth, + _In_ int cyWidth, + _In_ UINT istepIfAniCur, + _In_opt_ HBRUSH hbrFlickerFreeDraw, + _In_ UINT diFlags +) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI GetIconInfo( + _In_ HICON hIcon, + _Out_ PICONINFO piconinfo +) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI DestroyIcon( + _In_ HICON hIcon +) +{ + UNIMPLEMENTED; + return FALSE; +} + +HICON WINAPI LoadIconA( + _In_opt_ HINSTANCE hInstance, + _In_ LPCSTR lpIconName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_a(lpIconName)); + + return LoadImageA(hInstance, + lpIconName, + IMAGE_ICON, + 0, + 0, + LR_SHARED | LR_DEFAULTSIZE ); +} + +HICON WINAPI LoadIconW( + _In_opt_ HINSTANCE hInstance, + _In_ LPCWSTR lpIconName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_w(lpIconName)); + + return LoadImageW(hInstance, + lpIconName, + IMAGE_ICON, + 0, + 0, + LR_SHARED | LR_DEFAULTSIZE ); +} + +HCURSOR WINAPI LoadCursorA( + _In_opt_ HINSTANCE hInstance, + _In_ LPCSTR lpCursorName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_a(lpCursorName)); + + return LoadImageA(hInstance, + lpCursorName, + IMAGE_CURSOR, + 0, + 0, + LR_SHARED | LR_DEFAULTSIZE ); +} + +HCURSOR WINAPI LoadCursorW( + _In_opt_ HINSTANCE hInstance, + _In_ LPCWSTR lpCursorName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_w(lpCursorName)); + + return LoadImageW(hInstance, + lpCursorName, + IMAGE_CURSOR, + 0, + 0, + LR_SHARED | LR_DEFAULTSIZE ); +} + +HCURSOR WINAPI LoadCursorFromFileA( + _In_ LPCSTR lpFileName +) +{ + TRACE("%s\n", debugstr_a(lpFileName)); + + return LoadImageA(NULL, + lpFileName, + IMAGE_CURSOR, + 0, + 0, + LR_LOADFROMFILE | LR_DEFAULTSIZE ); +} + +HCURSOR WINAPI LoadCursorFromFileW( + _In_ LPCWSTR lpFileName +) +{ + TRACE("%s\n", debugstr_w(lpFileName)); + + return LoadImageW(NULL, + lpFileName, + IMAGE_CURSOR, + 0, + 0, + LR_LOADFROMFILE | LR_DEFAULTSIZE ); +} + +HBITMAP WINAPI LoadBitmapA( + _In_opt_ HINSTANCE hInstance, + _In_ LPCSTR lpBitmapName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_a(lpBitmapName)); + + return LoadImageA(hInstance, + lpBitmapName, + IMAGE_BITMAP, + 0, + 0, + 0); +} + +HBITMAP WINAPI LoadBitmapW( + _In_opt_ HINSTANCE hInstance, + _In_ LPCWSTR lpBitmapName +) +{ + TRACE("%p, %s\n", hInstance, debugstr_w(lpBitmapName)); + + return LoadImageW(hInstance, + lpBitmapName, + IMAGE_BITMAP, + 0, + 0, + 0); +} + +HANDLE WINAPI LoadImageA( + _In_opt_ HINSTANCE hinst, + _In_ LPCSTR lpszName, + _In_ UINT uType, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT fuLoad +) +{ + HANDLE res; + LPWSTR u_name; + DWORD len; + + if (IS_INTRESOURCE(lpszName)) + return LoadImageW(hinst, (LPCWSTR)lpszName, uType, cxDesired, cyDesired, fuLoad); + + len = MultiByteToWideChar( CP_ACP, 0, lpszName, -1, NULL, 0 ); + u_name = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpszName, -1, u_name, len ); + + res = LoadImageW(hinst, u_name, uType, cxDesired, cyDesired, fuLoad); + HeapFree(GetProcessHeap(), 0, u_name); + return res; +} + +HANDLE WINAPI LoadImageW( + _In_opt_ HINSTANCE hinst, + _In_ LPCWSTR lpszName, + _In_ UINT uType, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT fuLoad +) +{ + /* Redirect to each implementation */ + switch(uType) + { + case IMAGE_BITMAP: + return BITMAP_LoadImageW(hinst, lpszName, cxDesired, cyDesired, fuLoad); + case IMAGE_CURSOR: + case IMAGE_ICON: + return CURSORICON_LoadImageW(hinst, lpszName, cxDesired, cyDesired, fuLoad, uType == IMAGE_ICON); + default: + SetLastError(ERROR_INVALID_PARAMETER); + break; + } + return NULL; +} + +int WINAPI LookupIconIdFromDirectory( + _In_ PBYTE presbits, + _In_ BOOL fIcon +) +{ + UNIMPLEMENTED; + return 0; +} + +int WINAPI LookupIconIdFromDirectoryEx( + _In_ PBYTE presbits, + _In_ BOOL fIcon, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT Flags +) +{ + UNIMPLEMENTED; + return 0; +} + +HICON WINAPI CreateIcon( + _In_opt_ HINSTANCE hInstance, + _In_ int nWidth, + _In_ int nHeight, + _In_ BYTE cPlanes, + _In_ BYTE cBitsPixel, + _In_ const BYTE *lpbANDbits, + _In_ const BYTE *lpbXORbits +) +{ + UNIMPLEMENTED; + return NULL; +} + +HICON WINAPI CreateIconFromResource( + _In_ PBYTE presbits, + _In_ DWORD dwResSize, + _In_ BOOL fIcon, + _In_ DWORD dwVer +) +{ + return CreateIconFromResourceEx( presbits, dwResSize, fIcon, dwVer, 0,0,0); +} + +HICON WINAPI CreateIconFromResourceEx( + _In_ PBYTE pbIconBits, + _In_ DWORD cbIconBits, + _In_ BOOL fIcon, + _In_ DWORD dwVersion, + _In_ int cxDesired, + _In_ int cyDesired, + _In_ UINT uFlags +) +{ + UNIMPLEMENTED; + return NULL; +} + +HICON WINAPI CreateIconIndirect( + _In_ PICONINFO piconinfo +) +{ + UNIMPLEMENTED; + return NULL; +} + +HCURSOR WINAPI CreateCursor( + _In_opt_ HINSTANCE hInst, + _In_ int xHotSpot, + _In_ int yHotSpot, + _In_ int nWidth, + _In_ int nHeight, + _In_ const VOID *pvANDPlane, + _In_ const VOID *pvXORPlane +) +{ + UNIMPLEMENTED; + return NULL; +} + +BOOL WINAPI SetSystemCursor( + _In_ HCURSOR hcur, + _In_ DWORD id +) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI SetCursorPos( + _In_ int X, + _In_ int Y +) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI GetCursorPos( + _Out_ LPPOINT lpPoint +) +{ + UNIMPLEMENTED; + return FALSE; +} + +int WINAPI ShowCursor( + _In_ BOOL bShow +) +{ + UNIMPLEMENTED; + return -1; +} + +HCURSOR WINAPI GetCursor(void) +{ + UNIMPLEMENTED; + return NULL; +} + +BOOL WINAPI DestroyCursor( + _In_ HCURSOR hCursor +) +{ + UNIMPLEMENTED; + return FALSE; +}