Skip to content

Commit

Permalink
[NTUSER][USER32] Make possible to set custom mouse cursors
Browse files Browse the repository at this point in the history
[NTUSER]

    - Implement SPI_SETCURSORS case for SystemParametersInfoA/W. According to MSDN, it updates the system mouse cursors by the ones provided by user (can be set via main.cpl). It does not use any parameters from SystemParametersInfo:

    1. First, get the cursor path from user defined values in registry via win32k!RegReadUserSetting.
    2. Then load the cursor handle from the specified cursor via user32!LoadImageW called from the aprropriate win32k callback via KeUserModeCallback.
    3. Set received handle for an appropriate resource ID via win32k!NtUserSetSystemCursor. Do this for each system defined cursor.

    - NEW: Call an internal handler for SPI_SETCURSORS from win32k!SpiUpdatePerUserSystemParameters, to reload user-defined cursors from Registry after reboot. This is called from WinLogon at each startup.
    - Implement co_IntLoadImage callback for user32!LoadImageW. Add an appropriate part in user32 also.
    - Rewrite co_IntSetupDefaultCursors callback, responsible for default (system) cursors loading. Call a callback to user32!LoadImageW, to load each system cursor, and then use win32k!NtUserSetSystemCursor to set each of them as the current system cursor.
    - Refactor some other several cursor/icon functions: NtUserFindExistingCursorIcon, NtUserSetSystemCursor and DefWndHandleSetCursor.
    - Handle HTHELP case in win32k!GetNCHitEx and user32!CreateDialogIndirectA/W, which is responsible for help button class. Set an appropriate cursor for this case in DefWndHandleSetCursor (DefWindowProc!WM_SETCURSOR message).
    - Remove bogus WM_SETCURSOR handing from win32k!DesktopWindowProc and user32!DesktopWndProcW, since it does not load a proper cursor at all, only default IDC_ARROW. It is already handled properly in win32k!IntDefWindowProc.
    - Set correct GreSetPointerShape flags for animated mouse cursors.
    - NEW: Add the system timer for animated mouse cursors where an each frame is enumerated separately and call it from win32k!UserSetCursor. This allows *.ani cursors to actually animate.

[USER32]

    - Add/fix user mode parts of LoadImage and SetDefaultCursors callbacks. Don't try to load system cursor scheme, it should be done in main.cpl instead.
    - Handle animated mouse cursors (*.ani). Load them correcly in user32!CURSORICON_LoadFromFileW. We already have CURSORICON_GetCursorDataFromANI, which handles it properly. Also set the correct flags for CURSORDATA structure and enable CURSORF_ACON flag on cursor creation in case cursor is animated.
    - NEW: Load user-defined cursors from HKCU\Control Panel\Cursors Reigstry key. Call it from user32!ClientThreadSetup, to load a cursors at each startup.
    - NEW: Add a small workaround to user32!LoadCursorW: try to find and load current cursor from Registry set by user first, in case it is set. Only in case it was not found, continue normal execution: load default system cursor, as the function should do. This allows to properly load the correct cursor for all UI elements.

Remaining bugs/issues:

    - Animated cursors always have a bit wrong position compared to Windows. However it's absolutely correct for standart cursors (with a *.cur extension).
    - Sometimes the animation becomes too fast (perhaps because of a recusrsive win32k!IntSetTimer calls, need it some another condition to kill the timer?).
    - In case of changing *.cur -> *.ani, sometimes the animation is continuing infinitely on some UI elements (or in the window where the previous *.ani cursor was initially set), even after cursor is changed. Needs to restart an app/explorer/etc. to avoid the problem. However, this does not occur when changing *.cur -> *.cur, *.cur -> *.ani and *.ani -> *.ani. Again, it seems to require one more condition to kill the timer.

CORE-14165, CORE-14166
  • Loading branch information
oleg-dubinskiy committed Jul 2, 2024
1 parent 0f9e889 commit da9dbae
Show file tree
Hide file tree
Showing 16 changed files with 531 additions and 211 deletions.
31 changes: 31 additions & 0 deletions win32ss/include/callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ typedef struct _HOOKPROC_CALLBACK_ARGUMENTS
WCHAR ModuleName[512];
} HOOKPROC_CALLBACK_ARGUMENTS, *PHOOKPROC_CALLBACK_ARGUMENTS;

typedef struct _LOADIMAGE_CALLBACK_ARGUMENTS
{
UINT ImageType;
int cxDesired;
int cyDesired;
UINT fuFlags;
WCHAR ImageName[MAX_PATH];
} LOADIMAGE_CALLBACK_ARGUMENTS, *PLOADIMAGE_CALLBACK_ARGUMENTS;

typedef struct _HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS
{
CREATESTRUCTW Cs; /* lpszName and lpszClass replaced by offsets */
Expand Down Expand Up @@ -128,6 +137,26 @@ typedef struct _GET_CHARSET_INFO
CHARSETINFO Cs;
} GET_CHARSET_INFO, *PGET_CHARSET_INFO;

typedef struct _LOADCURSORS_CALLBACK_ARGUMENTS
{
HCURSOR hCursorArrow;
HCURSOR hCursorIbeam;
HCURSOR hCursorWait;
HCURSOR hCursorCross;
HCURSOR hCursorUp;
HCURSOR hCursorIcon;
HCURSOR hCursorSize;
HCURSOR hCursorSizeNwse;
HCURSOR hCursorSizeNesw;
HCURSOR hCursorSizeWe;
HCURSOR hCursorSizeNs;
HCURSOR hCursorSizeAll;
HCURSOR hCursorNo;
HCURSOR hCursorHand;
HCURSOR hCursorAppStarting;
HCURSOR hCursorHelp;
} LOADCURSORS_CALLBACK_ARGUMENTS, *PLOADCURSORS_CALLBACK_ARGUMENTS;

typedef struct _SETWNDICONS_CALLBACK_ARGUMENTS
{
HICON hIconSample;
Expand Down Expand Up @@ -185,6 +214,8 @@ typedef struct _IMMLOADLAYOUT_CALLBACK_OUTPUT
IMEINFOEX iiex;
} IMMLOADLAYOUT_CALLBACK_OUTPUT, *PIMMLOADLAYOUT_CALLBACK_OUTPUT;

NTSTATUS WINAPI
User32CallLoadImageFromKernel(PVOID Arguments, ULONG ArgumentLength);
NTSTATUS WINAPI
User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength);
NTSTATUS WINAPI
Expand Down
21 changes: 11 additions & 10 deletions win32ss/include/u32cb.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ DEFINE_USER32_CALLBACK(USER32_CALLBACK_LOADMENU, 6, User32CallLoa
DEFINE_USER32_CALLBACK(USER32_CALLBACK_CLIENTTHREADSTARTUP, 7, User32CallClientThreadSetupFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_CLIENTLOADLIBRARY, 8, User32CallClientLoadLibraryFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_GETCHARSETINFO, 9, User32CallGetCharsetInfo)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_COPYIMAGE, 10, User32CallCopyImageFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_SETWNDICONS, 11, User32CallSetWndIconsFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DELIVERUSERAPC, 12, User32DeliverUserAPC)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DDEPOST, 13, User32CallDDEPostFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DDEGET, 14, User32CallDDEGetFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_SETOBM, 15, User32CallOBMFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_LPK, 16, User32CallLPKFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_UMPD, 17, User32CallUMPDFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_IMMPROCESSKEY, 18, User32CallImmProcessKeyFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_IMMLOADLAYOUT, 19, User32CallImmLoadLayoutFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_LOADIMAGE, 10, User32CallLoadImageFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_COPYIMAGE, 11, User32CallCopyImageFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_SETWNDICONS, 12, User32CallSetWndIconsFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DELIVERUSERAPC, 13, User32DeliverUserAPC)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DDEPOST, 14, User32CallDDEPostFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_DDEGET, 15, User32CallDDEGetFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_SETOBM, 16, User32CallOBMFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_LPK, 17, User32CallLPKFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_UMPD, 18, User32CallUMPDFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_IMMPROCESSKEY, 19, User32CallImmProcessKeyFromKernel)
DEFINE_USER32_CALLBACK(USER32_CALLBACK_IMMLOADLAYOUT, 20, User32CallImmLoadLayoutFromKernel)
101 changes: 91 additions & 10 deletions win32ss/user/ntuser/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,39 +465,65 @@ co_IntLoadSysMenuTemplate(VOID)
return (HMENU)Result;
}

extern HCURSOR gDesktopCursor;

BOOL APIENTRY
co_IntLoadDefaultCursors(VOID)
{
NTSTATUS Status;
PVOID ResultPointer;
ULONG ResultLength;
BOOL DefaultCursor = TRUE;
PVOID Argument, ResultPointer;
ULONG ArgumentLength, ResultLength;
PLOADCURSORS_CALLBACK_ARGUMENTS Common;

/* Do not allow the desktop thread to do callback to user mode */
ASSERT(PsGetCurrentThreadWin32Thread() != gptiDesktopThread);

ResultPointer = NULL;
ResultLength = sizeof(HCURSOR);
ResultLength = ArgumentLength = sizeof(LOADCURSORS_CALLBACK_ARGUMENTS);

Argument = IntCbAllocateMemory(ArgumentLength);
if (!Argument)
{
ERR("Load Default Cursors callback failed: out of memory\n");
return FALSE;
}
Common = (PLOADCURSORS_CALLBACK_ARGUMENTS)Argument;

UserLeaveCo();

Status = KeUserModeCallback(USER32_CALLBACK_LOADDEFAULTCURSORS,
&DefaultCursor,
sizeof(BOOL),
Argument,
ArgumentLength,
&ResultPointer,
&ResultLength);

UserEnterCo();

if (!NT_SUCCESS(Status))
{
ERR("Load Default Cursors callback failed!\n");
IntCbFreeMemory(Argument);
return FALSE;
}

/* HACK: The desktop class doen't have a proper cursor yet, so set it here */
gDesktopCursor = *((HCURSOR*)ResultPointer);
RtlMoveMemory(Common, ResultPointer, ArgumentLength);

NtUserSetSystemCursor(Common->hCursorArrow, OCR_NORMAL);
NtUserSetSystemCursor(Common->hCursorIbeam, OCR_IBEAM);
NtUserSetSystemCursor(Common->hCursorWait, OCR_WAIT);
NtUserSetSystemCursor(Common->hCursorCross, OCR_CROSS);
NtUserSetSystemCursor(Common->hCursorUp, OCR_UP);
NtUserSetSystemCursor(Common->hCursorIcon, OCR_ICON);
NtUserSetSystemCursor(Common->hCursorSize, OCR_SIZE);
NtUserSetSystemCursor(Common->hCursorSizeAll, OCR_SIZEALL);
NtUserSetSystemCursor(Common->hCursorSizeNwse, OCR_SIZENWSE);
NtUserSetSystemCursor(Common->hCursorSizeNesw, OCR_SIZENESW);
NtUserSetSystemCursor(Common->hCursorSizeWe, OCR_SIZEWE);
NtUserSetSystemCursor(Common->hCursorSizeNs, OCR_SIZENS);
NtUserSetSystemCursor(Common->hCursorNo, OCR_NO);
NtUserSetSystemCursor(Common->hCursorHand, OCR_HAND);
NtUserSetSystemCursor(Common->hCursorAppStarting, OCR_APPSTARTING);
NtUserSetSystemCursor(Common->hCursorHelp, OCR_HELP);

IntCbFreeMemory(Argument);

return TRUE;
}
Expand Down Expand Up @@ -981,6 +1007,61 @@ co_IntClientThreadSetup(VOID)
return Status;
}

HANDLE FASTCALL
co_IntLoadImage(LPCWSTR name, UINT type, INT desiredx, INT desiredy, UINT flags)
{
HANDLE Handle;
NTSTATUS Status;
ULONG ArgumentLength, ResultLength;
PVOID Argument, ResultPointer;
PLOADIMAGE_CALLBACK_ARGUMENTS Common;

ArgumentLength = ResultLength = 0;
Argument = ResultPointer = NULL;

ArgumentLength = sizeof(LOADIMAGE_CALLBACK_ARGUMENTS);

Argument = IntCbAllocateMemory(ArgumentLength);
if (!Argument)
{
ERR("LoadImage callback failed: out of memory\n");
return 0;
}
Common = (PLOADIMAGE_CALLBACK_ARGUMENTS) Argument;

RtlStringCchCopyW(Common->ImageName, wcslen(name) + 1, name);

Common->ImageType = type;
Common->cxDesired = desiredx;
Common->cyDesired = desiredy;
Common->fuFlags = flags;

UserLeaveCo();

Status = KeUserModeCallback(USER32_CALLBACK_LOADIMAGE,
Argument,
ArgumentLength,
&ResultPointer,
&ResultLength);


UserEnterCo();

if (NT_SUCCESS(Status))
{
Handle = *(HANDLE*)ResultPointer;
}
else
{
ERR("LoadImage callback failed\n");
Handle = NULL;
}

IntCbFreeMemory(Argument);

return Handle;
}

HANDLE FASTCALL
co_IntCopyImage(HANDLE hnd, UINT type, INT desiredx, INT desiredy, UINT flags)
{
Expand Down
1 change: 1 addition & 0 deletions win32ss/user/ntuser/callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ BOOL
APIENTRY
co_IntGetCharsetInfo(LCID Locale, PCHARSETINFO pCs);

HANDLE FASTCALL co_IntLoadImage(LPCWSTR,UINT,INT,INT,UINT);
HANDLE FASTCALL co_IntCopyImage(HANDLE,UINT,INT,INT,UINT);

BOOL FASTCALL co_IntSetWndIcons(VOID);
Expand Down
Loading

0 comments on commit da9dbae

Please sign in to comment.