How to Change system cursor? Not as simple as it looks!!

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
jly
Posts: 89
Joined: 30 Sep 2020, 06:06

How to Change system cursor? Not as simple as it looks!!

Post by jly » 28 Jan 2022, 04:11

I found this to change the system cursor, but there are two problems with this method.
Changing the system cursor (using SetSystemCursor)
https://www.autohotkey.com/board/topic/32608-changing-the-system-cursor/



problems 1:
The changed Cursor is blurred.

problems 2:
when the Cursor is in a certain position of a program, the program will automatically change the shape of the Cursor.



Check out the 3 screenshots below:
Number 1, the normal shape of the Cursor.
Number 2, the Cursor modified with SetSystemCursor method, is blurred.
Number 3, move the Cursor in the second picture to the left, and the shape of the mouse is changed by the program (NotePad++).
Cursor_Shapes2.png
Cursor_Shapes2.png (51.96 KiB) Viewed 961 times


I have seen some third screenshot tools. When taking screenshots, they will change the shape of the cursor without the above two problems.
It doesn't seem difficult to achieve.

How can you solve these two problems?



Here is my test script, I modified SetSystemCursor to SetSystemCursor2, which can solve the fuzzy problem,
but when in different positions, the cursor seems to be a bit messy。

press F1 to change/resotre Cursor Shape:

Code: Select all


#SingleInstance Force
F1::
	if(flag := !flag)
	{
		SetSystemCursor( "IDC_HAND")   ;IDC_ARROW     IDC_CROSS
		;SetSystemCursor2( "IDC_HAND")
	}
	else
		RestoreCursors()
return

SetSystemCursor(Cursor) {

   Static
   Initialized := False
   SystemCursors := "32512IDC_ARROW|32513IDC_IBEAM|32514IDC_WAIT|32515IDC_CROSS|"
                  . "32516IDC_UPARROW|32642IDC_SIZENWSE|"
                  . "32643IDC_SIZENESW|32644IDC_SIZEWE|32645IDC_SIZENS|32646IDC_SIZEALL|"
                  . "32648IDC_NO|32649IDC_HAND|32650IDC_APPSTARTING|32651IDC_HELP"
   If !(Initialized) { ; load system cursors only once
      Loop, Parse, SystemCursors, |
      {
         CID := SubStr(A_LoopField, 1, 5)  ;32512
         CNA := SubStr(A_LoopField, 6)     ;IDC_ARROW
         HC%CNA% := DllCall("LoadCursor", "UInt", 0, "Int", CID)
      }
      Initialized := True
   }
   If !(HC%Cursor%) {
      Msgbox, 0, SetSystemCursor, Error: Invalid cursor name %Cursor%
      Return False
   }
   Loop, Parse, SystemCursors, |
   {
      CID := SubStr(A_LoopField, 1, 5)

	  ;This way, the Cursor becomes blurry
      HCUR := DllCall("CopyIcon", "UInt" , HC%Cursor%)
      DllCall("SetSystemCursor", "UInt", HCUR, "Int", CID)

	  ;It is clear to use the original CursorHandle setting directly.
	  ;But the Cursor seems to be messed up
	  ;DllCall("SetSystemCursor", "UInt", HC%Cursor%, "Int", CID)

   }
   Return True
}

SetSystemCursor2(Cursor) {

   Static
   Initialized := False
   SystemCursors := "32512IDC_ARROW|32513IDC_IBEAM|32514IDC_WAIT|32515IDC_CROSS|"
                  . "32516IDC_UPARROW|32642IDC_SIZENWSE|"
                  . "32643IDC_SIZENESW|32644IDC_SIZEWE|32645IDC_SIZENS|32646IDC_SIZEALL|"
                  . "32648IDC_NO|32649IDC_HAND|32650IDC_APPSTARTING|32651IDC_HELP"
   If !(Initialized) { ; load system cursors only once
      Loop, Parse, SystemCursors, |
      {
         CID := SubStr(A_LoopField, 1, 5)  ;32512
         CNA := SubStr(A_LoopField, 6)     ;IDC_ARROW
         HC%CNA% := DllCall("LoadCursor", "UInt", 0, "Int", CID)
      }
      Initialized := True
   }
   If !(HC%Cursor%) {
      Msgbox, 0, SetSystemCursor, Error: Invalid cursor name %Cursor%
      Return False
   }
   Loop, Parse, SystemCursors, |
   {
      CID := SubStr(A_LoopField, 1, 5)

	  ;This way, the Cursor becomes blurry
      HCUR := DllCall("CopyIcon", "UInt" , HC%Cursor%)
      ;DllCall("SetSystemCursor", "UInt", HCUR, "Int", CID)

	  ;It is clear to use the original CursorHandle setting directly.
	  ;But the Cursor seems to be messed up
	  DllCall("SetSystemCursor", "UInt", HC%Cursor%, "Int", CID)

   }
   Return True
}

RestoreCursors()
{
	SPI_SETCURSORS := 0x57
	DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
}
esc::
RestoreCursors()
ExitApp
return




jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: How to Change system cursor? Not as simple as it looks!!

Post by jly » 28 Jan 2022, 04:18

And I use :
AutoHotkey:1.1.33.08
OS: Windows10 Professiona 19042.1466

User avatar
boiler
Posts: 17043
Joined: 21 Dec 2014, 02:44

Re: How to Change system cursor? Not as simple as it looks!!

Post by boiler » 28 Jan 2022, 06:47

I don't have the blurring problem. Maybe you are running at a Windows scaling setting other than 100%, and that might cause it. Just a guess.

Regarding other programs changing it while hovering over certain areas, you could try monitoring the current cursor shape, and when it changes, then change it back. I don't know if that will leave it for the whole time you are hovering over that section of Notepad++ until the cursor exits, or if there will be fight back and forth between your script and Notepad++ continuously changing the cursor.

jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: How to Change system cursor? Not as simple as it looks!!

Post by jly » 28 Jan 2022, 07:36

boiler wrote:
28 Jan 2022, 06:47
I don't have the blurring problem. Maybe you are running at a Windows scaling setting other than 100%, and that might cause it. Just a guess.

Regarding other programs changing it while hovering over certain areas, you could try monitoring the current cursor shape, and when it changes, then change it back. I don't know if that will leave it for the whole time you are hovering over that section of Notepad++ until the cursor exits, or if there will be fight back and forth between your script and Notepad++ continuously changing the cursor.



Forgot to mention, I'm using a desktop computer.

1 I adjusted the scaling to 100% and restart the computer to test, and still have the blurring problem.
2 I tried monitoring the current cursor shape, when it was changed and I changed it back, it would keep flashing when cursor in certain posision of Notepad++.

3 In addition, I ran the follow script that kept modifying the shape of the cursor, and I saw the cursor flickering constantly.
Then I run a third-party screenshot program, and the cursor shape is changed by it, but the cursor does not flicker at this time.


the script kept modifying the cursor shape, Press Esc to exit.

Code: Select all

#SingleInstance Force


SetTimer m1,10

m1:
		SetSystemCursor( "IDC_HAND")
		sleep,10
		RestoreCursors()

return




SetSystemCursor(Cursor) {
   Static
   Initialized := False
   SystemCursors := "32512IDC_ARROW|32513IDC_IBEAM|32514IDC_WAIT|32515IDC_CROSS|"
                  . "32516IDC_UPARROW|32642IDC_SIZENWSE|"
                  . "32643IDC_SIZENESW|32644IDC_SIZEWE|32645IDC_SIZENS|32646IDC_SIZEALL|"
                  . "32648IDC_NO|32649IDC_HAND|32650IDC_APPSTARTING|32651IDC_HELP"
   If !(Initialized) { ; load system cursors only once
      Loop, Parse, SystemCursors, |
      {
         CID := SubStr(A_LoopField, 1, 5)  ;32512
         CNA := SubStr(A_LoopField, 6)     ;IDC_ARROW
         HC%CNA% := DllCall("LoadCursor", "UInt", 0, "Int", CID)
      }
      Initialized := True
   }
   If !(HC%Cursor%) {
      Msgbox, 0, SetSystemCursor, Error: Invalid cursor name %Cursor%
      Return False
   }
   Loop, Parse, SystemCursors, |
   {
      CID := SubStr(A_LoopField, 1, 5)
      HCUR := DllCall("CopyIcon", "UInt" , HC%Cursor%)
      DllCall("SetSystemCursor", "UInt", HCUR, "Int", CID)

   }
   Return True
}

RestoreCursors()
{
	SPI_SETCURSORS := 0x57
	DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
}
esc::
RestoreCursors()
ExitApp
return




I used this screenshot tool to test:
https://www.snipaste.com/


4 I have also tested hiding the cursor, but problem 2 also exists

x32
Posts: 177
Joined: 25 Nov 2016, 16:44

Re: How to Change system cursor? Not as simple as it looks!!

Post by x32 » 28 Jan 2022, 07:41

I have used the change system cursor function quite a bit and haven't had the blurring issue you seem to have. I think I likely got my script from the same post you linked to, but I made a library function with it that you might try to see if you get the same results.

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;     Cursor Functions     ;;;;;;;;;;;;;;;;;;;;;;;;

Cursor_Restore()
{
   SPI_SETCURSORS := 0x57
   DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
}

Cursor_Set( Cursor = "", cx = 0, cy = 0 )
{
   BlankCursor := 0, SystemCursor := 0, FileCursor := 0 ; init
   
   SystemCursors = 32512IDC_ARROW,32513IDC_IBEAM,32514IDC_WAIT,32515IDC_CROSS
   ,32516IDC_UPARROW,32640IDC_SIZE,32641IDC_ICON,32642IDC_SIZENWSE
   ,32643IDC_SIZENESW,32644IDC_SIZEWE,32645IDC_SIZENS,32646IDC_SIZEALL
   ,32648IDC_NO,32649IDC_HAND,32650IDC_APPSTARTING,32651IDC_HELP
   
   If Cursor = ; empty, so create blank cursor
   {
      VarSetCapacity( AndMask, 32*4, 0xFF ), VarSetCapacity( XorMask, 32*4, 0 )
      BlankCursor = 1 ; flag for later
   }
   Else If SubStr( Cursor,1,4 ) = "IDC_" ; load system cursor
   {
      Loop, Parse, SystemCursors, `,
      {
         CursorName := SubStr( A_Loopfield, 6, 15 ) ; get the cursor name, no trailing space with substr
         CursorID := SubStr( A_Loopfield, 1, 5 ) ; get the cursor id
         SystemCursor = 1
         If ( CursorName = Cursor )
         {
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            Break               
         }
      }   
      If CursorHandle = ; invalid cursor name given
      {
         Msgbox,, SetCursor, Error: Invalid cursor name
         CursorHandle = Error
      }
   }   
   Else If FileExist( Cursor )
   {
      SplitPath, Cursor,,, Ext ; auto-detect type
      If Ext = ico
         uType := 0x1   
      Else If Ext in cur,ani
         uType := 0x2     
      Else ; invalid file ext
      {
         Msgbox,, SetCursor, Error: Invalid file type
         CursorHandle = Error
      }     
      FileCursor = 1
   }
   Else
   {   
      Msgbox,, SetCursor, Error: Invalid file path or cursor name
      CursorHandle = Error ; raise for later
   }
   If CursorHandle != Error
   {
      Loop, Parse, SystemCursors, `,
      {
         If BlankCursor = 1
         {
            Type = BlankCursor
            %Type%%A_Index% := DllCall( "CreateCursor"
            , Uint,0, Int,0, Int,0, Int,32, Int,32, Uint,&AndMask, Uint,&XorMask )
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }         
         Else If SystemCursor = 1
         {
            Type = SystemCursor
            CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID )   
            %Type%%A_Index% := DllCall( "CopyImage"
            , Uint,CursorHandle, Uint,0x2, Int,cx, Int,cy, Uint,0 )     
            CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 )
            DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) )
         }
         Else If FileCursor = 1
         {
            Type = FileCursor
            %Type%%A_Index% := DllCall( "LoadImageA"
            , UInt,0, Str,Cursor, UInt,uType, Int,cx, Int,cy, UInt,0x10 )
            DllCall( "SetSystemCursor", Uint,%Type%%A_Index%, Int,SubStr( A_Loopfield, 1, 5 ) )         
         }         
      }
   }   
}
And an example script:

Code: Select all

#NoEnv  
SendMode Input 
SetWorkingDir %A_ScriptDir%
#SingleInstance, Force
Gui, Font, s12

Gui, Add, Text, x5 y10 w200 vIDC_APPSTARTING , IDC_APPSTARTING
Gui, Add, Text, x5 y+10 w200 vIDC_ARROW , IDC_ARROW
Gui, Add, Text, x5 y+10 w200 vIDC_CROSS , IDC_CROSS
Gui, Add, Text, x5 y+10 w200 vIDC_HAND , IDC_HAND
Gui, Add, Text, x5 y+10 w200 vIDC_HELP , IDC_HELP
Gui, Add, Text, x5 y+10 w200 vIDC_IBEAM , IDC_IBEAM
Gui, Add, Text, x5 y+10 w200 vIDC_NO , IDC_NO

Gui, Add, Text, x250 y10 w200 vIDC_SIZEALL , IDC_SIZEALL
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENESW , IDC_SIZENESW
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENS , IDC_SIZENS
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENWSE , IDC_SIZENWSE
Gui, Add, Text, x250 y+10 w200 vIDC_SIZEWE , IDC_SIZEWE
Gui, Add, Text, x250 y+10 w200 vIDC_UPARROW , IDC_UPARROW
Gui, Add, Text, x250 y+10 w200 vIDC_WAIT , IDC_WAIT

Gui, Show, w450 h300, System Curors
SetTimer, which, 100
Return

which:
MouseGetPos, , , , OCon
GuiControlGet, OutputVar, Name, %OCon%
If 	OutputVar = 
	{
	Cursor_Restore()
	Now = 
	Return
	}

If OutputVar = %Now%
	Return

list = IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_UPARROW, IDC_WAIT
IfInString, list, %OutputVar%
{
	Cursor_Restore()
    Cursor_Set(OutputVar)
	Now = %OutputVar%
}	
Return

Exit:
GuiEscape:
GuiClose:
SetTimer, which, Off
Sleep, 150
Cursor_Restore()
WinClose, Weather
Gui, Destroy
ExitApp
Last edited by x32 on 28 Jan 2022, 08:22, edited 1 time in total.

jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: How to Change system cursor? Not as simple as it looks!!

Post by jly » 28 Jan 2022, 08:07

x32 wrote:
28 Jan 2022, 07:41
I have used the change system cursor function quite a bit and haven't had the blurring issue you seem to have. I think I likely got my script from the same post you linked to, but I made a library function with it that you might try to see if you get the same results.

Code: Select all

SystemCursor(OnOff=1)  
{
    static AndMask, XorMask, $, h_cursor
        ,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13 ; system cursors
        , b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13   ; blank cursors
        , h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11,h12,h13   ; handles of default cursors
    if (OnOff = "Init" or OnOff = "I" or $ = "")       ; init when requested or at first call
    {
        $ = h                                          ; active default cursors
        VarSetCapacity( h_cursor,4444, 1 )
        VarSetCapacity( AndMask, 32*4, 0xFF )
        VarSetCapacity( XorMask, 32*4, 0 )
        system_cursors = 32512,32513,32514,32515,32516,32642,32643,32644,32645,32646,32648,32649,32650
        StringSplit c, system_cursors, `,
        Loop %c0%
        {
            h_cursor   := DllCall( "LoadCursor", "uint",0, "uint",c%A_Index% )
            h%A_Index% := DllCall( "CopyImage",  "uint",h_cursor, "uint",2, "int",0, "int",0, "uint",0 )
            b%A_Index% := DllCall("CreateCursor","uint",0, "int",0, "int",0
                , "int",32, "int",32, "uint",&AndMask, "uint",&XorMask )
        }
    }
    if (OnOff = 0 or OnOff = "Off" or $ = "h" and (OnOff < 0 or OnOff = "Toggle" or OnOff = "T"))
        $ = b  ; use blank cursors
    else
        $ = h  ; use the saved cursors

    Loop %c0%
    {
        h_cursor := DllCall( "CopyImage", "uint",%$%%A_Index%, "uint",2, "int",0, "int",0, "uint",0 )
        DllCall( "SetSystemCursor", "uint",h_cursor, "uint",c%A_Index% )
    }
}
And an example script:

Code: Select all

#NoEnv  
SendMode Input 
SetWorkingDir %A_ScriptDir%
#SingleInstance, Force
Gui, Font, s12

Gui, Add, Text, x5 y10 w200 vIDC_APPSTARTING , IDC_APPSTARTING
Gui, Add, Text, x5 y+10 w200 vIDC_ARROW , IDC_ARROW
Gui, Add, Text, x5 y+10 w200 vIDC_CROSS , IDC_CROSS
Gui, Add, Text, x5 y+10 w200 vIDC_HAND , IDC_HAND
Gui, Add, Text, x5 y+10 w200 vIDC_HELP , IDC_HELP
Gui, Add, Text, x5 y+10 w200 vIDC_IBEAM , IDC_IBEAM
Gui, Add, Text, x5 y+10 w200 vIDC_NO , IDC_NO

Gui, Add, Text, x250 y10 w200 vIDC_SIZEALL , IDC_SIZEALL
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENESW , IDC_SIZENESW
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENS , IDC_SIZENS
Gui, Add, Text, x250 y+10 w200 vIDC_SIZENWSE , IDC_SIZENWSE
Gui, Add, Text, x250 y+10 w200 vIDC_SIZEWE , IDC_SIZEWE
Gui, Add, Text, x250 y+10 w200 vIDC_UPARROW , IDC_UPARROW
Gui, Add, Text, x250 y+10 w200 vIDC_WAIT , IDC_WAIT

Gui, Show, w450 h300, System Curors
SetTimer, which, 100
Return

which:
MouseGetPos, , , , OCon
GuiControlGet, OutputVar, Name, %OCon%
If 	OutputVar = 
	{
	Cursor_Restore()
	Now = 
	Return
	}

If OutputVar = %Now%
	Return

list = IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_UPARROW, IDC_WAIT
IfInString, list, %OutputVar%
{
	Cursor_Restore()
    Cursor_Set(OutputVar)
	Now = %OutputVar%
}	
Return

Exit:
GuiEscape:
GuiClose:
SetTimer, which, Off
Sleep, 150
Cursor_Restore()
WinClose, Weather
Gui, Destroy
ExitApp

hello, it's missing function: Cursor_Restore()
Cursor_Set(OutputVar)

x32
Posts: 177
Joined: 25 Nov 2016, 16:44

Re: How to Change system cursor? Not as simple as it looks!!

Post by x32 » 28 Jan 2022, 08:24

jly wrote:
28 Jan 2022, 08:07
hello, it's missing function: Cursor_Restore()
Cursor_Set(OutputVar)
Sorry, I copied the wrong page. Corrected now, please try it again.

jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: How to Change system cursor? Not as simple as it looks!!

Post by jly » 28 Jan 2022, 08:28

x32 wrote:
28 Jan 2022, 08:24
jly wrote:
28 Jan 2022, 08:07
hello, it's missing function: Cursor_Restore()
Cursor_Set(OutputVar)
Sorry, I copied the wrong page. Corrected now, please try it again.
Unfortunately, my cursor is still blurry .

eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: How to Change system cursor? Not as simple as it looks!!

Post by eugenesv » 30 Aug 2023, 10:35

Have you found the solution to the blurry cursor problem?

Post Reply

Return to “Ask for Help (v1)”