 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Wed Sep 10, 2008 3:07 am Post subject: Changing the system cursor |
|
|
I thought I'd write a guide to hopefully make this more accessible to others.
Update 2008/09/10 - Wrapper function released. (See end of post).
The first thing is decide what kind of cursor to use - a system cursor, or a cursor loaded from a file? It is also possible to load a cursor from within a script, see Crazy Scripting : Include an Icon in your script.
Using a system cursor
These are our cursor IDs:
| Code: | IDC_ARROW := 32512
IDC_IBEAM := 32513
IDC_WAIT := 32514
IDC_CROSS := 32515
IDC_UPARROW := 32516
IDC_SIZE := 32640
IDC_ICON := 32641
IDC_SIZENWSE := 32642
IDC_SIZENESW := 32643
IDC_SIZEWE := 32644
IDC_SIZENS := 32645
IDC_SIZEALL := 32646
IDC_NO := 32648
IDC_HAND := 32649
IDC_APPSTARTING := 32650
IDC_HELP := 32651 |
To load a system cursor use LoadCursor, which returns a handle to use in SetSystemCursor. Here I've used IDC_SIZEALL:
| Code: | CursorHandle := DllCall( "LoadCursor", Uint,0, Int,IDC_SIZEALL )
|
Loading a cursor from a file
If you want to load a cursor from a file, use LoadCursorFromFile instead. You can also load an icon file here:
| Code: | Cursor = %A_ScriptDir%\mycursor.ani
CursorHandle := DllCall( "LoadCursorFromFile", Str,Cursor ) |
Size of cursor
We can change the size of the cursor with CopyImage, although this method stretches it so the end look may not be satisfactory. This step is optional:
| Code: | ; uType:
IMAGE_BITMAP := 0x0
IMAGE_CURSOR := 0x2
IMAGE_ICON := 0x1
; Size:
cx := 32, cy := cx
; fuFlags:
LR_COPYFROMRESOURCE := 0x4000
CursorHandle := DllCall( "CopyImage", uint,CursorHandle, uint,IMAGE_CURSOR, int,cx, int,cy, uint,0 ) |
We can use LoadImage instead of LoadCursorFromFile to load non-standard size icons and cursors. I used this method to load a 48x48 icon when my system is set to 32x32. I also tried this with multi icon and it seems to always load the largest icon.
Note: LoadCursorFromFile will load other size icons and cursors but it seems to force the system settings onto it as it loads. The result after a resize via CopyImage isn't as nice even with the LR_COPYFROMRESOURCE option.
| Code: | Image = %A_ScriptDir%\mymulti.ico
; uType:
IMAGE_BITMAP := 0x0
IMAGE_CURSOR := 0x2
IMAGE_ICON := 0x1
; Size:
cx := 48, cy := cx
; fuLoad:
LR_COLOR := 0x2
LR_CREATEDIBSECTION := 0x2000
LR_DEFAULTSIZE := 0x40
LR_LOADFROMFILE := 0x10
LR_LOADMAP3DCOLORS := 0x1000
CursorHandle := DllCall( "LoadImageA", UInt,0, Str,Image, UInt,IMAGE_ICON, Int,cx, Int,cy, UInt,0x10 ) |
Loading from a DLL or EXE
It may also be possible to load a cursor or icon from a DLL or EXE file. Here we need GetModuleHandle to get a handle for LoadImage, and MAKEINTRESOURCE to load a resource by ordinal. If anyone knows how to use MAKEINTRESOURCE macro within DllCall do post.
| Code: | ; uType:
IMAGE_BITMAP := 0x0
IMAGE_CURSOR := 0x2
IMAGE_ICON := 0x1
; Size:
cx := 48, cy := cx
; fuLoad:
LR_COLOR := 0x2
LR_CREATEDIBSECTION := 0x2000
LR_DEFAULTSIZE := 0x40
LR_LOADFROMFILE := 0x10
LR_LOADMAP3DCOLORS := 0x1000
Module = %A_WinDir%\SYSTEM32\SHELL32.DLL
ModuleHandle := DllCall("GetModuleHandleA", Str,Module)
CursorHandle := DllCall( "LoadImageA", Uint,ModuleHandle, Str,Cursor, Uint,IMAGE_ICON, Int,cx, Int,cy, UInt,0x10 ) |
Replacing the cursors
Now that we've loaded a cursor, the next step is to loop through a list of system cursors to replace all of them:
| Code: | Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
Loop, Parse, Cursors, `,
{
DllCall( "SetSystemCursor", Uint,CursorHandle, Int,A_Loopfield )
}
|
Restoring system cursors
To restore system cursors use SystemParametersInfo/SPI_SETCURSORS:
| Code: | SPI_SETCURSORS := 0x57
DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 ) ; Reload the system cursors |
Example script: The following changes the cursor while the MButton is down, and restores it on release.
| Code: | #SingleInstance Force
~MButton::SetSystemCursor()
~MButton Up::RestoreCursors()
SetSystemCursor()
{
IDC_SIZEALL := 32646
CursorHandle := DllCall( "LoadCursor", Uint,0, Int,IDC_SIZEALL )
Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
Loop, Parse, Cursors, `,
{
DllCall( "SetSystemCursor", Uint,CursorHandle, Int,A_Loopfield )
}
}
RestoreCursors()
{
SPI_SETCURSORS := 0x57
DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
} |
Wrapper function
This covers nearly everything in a single function. I've left out RestoreCursors as I think it should be seperate.
| Code: | | SetSystemCursor( File path or cursor name, Width, Height ) |
Parameter 1 is file path or cursor name, e.g. IDC_SIZEALL. If this is omitted it will hide the cursor.
Parameters 2 and 3 are the desired width and height of cursor. Omit these to use the default size, e.g. loading a 48x48 cursor will display as 48x48.
Changelog
2008/09/13 - Set cursor now shows everywhere bar apps that set their own cursor
2008/09/10 - Wrapper function released
| Code: | SetSystemCursor( 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 ) )
}
}
}
} |
| Code: | RestoreCursors()
{
SPI_SETCURSORS := 0x57
DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 )
} |
_________________ "Anything worth doing is worth doing slowly." - Mae West

Last edited by Serenity on Sat Sep 13, 2008 6:14 pm; edited 11 times in total |
|
| Back to top |
|
 |
jaco0646
Joined: 07 Oct 2006 Posts: 3113 Location: MN, USA
|
Posted: Thu Sep 11, 2008 12:23 am Post subject: |
|
|
| That's a neat trick! Switching cursors is a long-awaited feature. I've never seen it coded that concisely. Thank you! |
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Thu Sep 11, 2008 2:32 am Post subject: |
|
|
Thanks Jaco.
I wrote a wrapper function which covers nearly everything. See my first post for details. _________________ "Anything worth doing is worth doing slowly." - Mae West
 |
|
| Back to top |
|
 |
freakkk
Joined: 29 Jul 2005 Posts: 179
|
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Thu Sep 11, 2008 3:02 am Post subject: |
|
|
Thanks, I didn't know about that. I'll rename this one to SetSystemCursor since it changes it system-wide. _________________ "Anything worth doing is worth doing slowly." - Mae West
 |
|
| Back to top |
|
 |
derRaphael
Joined: 23 Nov 2007 Posts: 841 Location: ~/.
|
Posted: Thu Sep 11, 2008 4:28 am Post subject: |
|
|
hi, there
i have been testing ur script, and it seems like there is a bug when being used on windows 2k
| Code: | #SingleInstance Force
space::SetSystemCursor("IDC_NO")
space Up::RestoreCursors()
esc::exitapp |
this was the code i used and it seems that there is a bug. when i hold space (expecting my cursor to display the slashed circle) its hows the circle for a splitsecond and then starts cycling mad thru all available cursor shapes.
i guess it is not the desired result.
greets
dR _________________
All scripts, unless otherwise noted, are hereby released under CC-BY |
|
| Back to top |
|
 |
Slanter
Joined: 28 May 2008 Posts: 739 Location: Minnesota, USA
|
Posted: Thu Sep 11, 2008 5:13 am Post subject: |
|
|
There are also some interesting results when you change the cursor and then mouse over something that also tries to change your cursor (like edit fields and the edges of re-sizable windows). I'm running win2k as well. _________________ Unless otherwise stated, all code is untested
(\__/) This is Bunny.
(='.'=) Cut, copy, and paste bunny onto your sig.
(")_(") Help Bunny gain World Domination. |
|
| Back to top |
|
 |
Centra Guest
|
Posted: Thu Sep 11, 2008 5:17 am Post subject: |
|
|
Can someone pls show me how to make all system Cursors, be just a simple arrow? That is, no matter what, I just have a arrow... no hourglass, etc  |
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Thu Sep 11, 2008 11:50 am Post subject: |
|
|
| DerRaphael wrote: | | when i hold space (expecting my cursor to display the slashed circle) its hows the circle for a splitsecond and then starts cycling mad thru all available cursor shapes. |
Hi, thanks for posting. The same thing happens for me on 2k. I tried calling the function with a 100ms timer and got the same thing, so this is what happens when it is called repeatedly. It seems holding down a key has the same effect; this doesn't happen with a mouse button or if called normally.
I tried adding the following and results are the same. If I add a sleep of 1000-2000ms after calling the function it slows it down a little, but the calls seem to be buffered so it still cycles, albeit at a slower rate.
| Code: | #MaxThreadsPerHotkey 1
#MaxThreads 1 |
| Slanter wrote: | | There are also some interesting results when you change the cursor and then mouse over something that also tries to change your cursor (like edit fields and the edges of re-sizable windows). |
What happens, and how are you using the function? From my testing if the app has changed the cursor (Photoshop for instance) it takes priority and it is as if the function was never called, but only for these controls, then it changes back.
| Centra wrote: | | Can someone pls show me how to make all system Cursors, be just a simple arrow? That is, no matter what, I just have a arrow... no hourglass, etc Smile |
Try calling the wrapper function with the cursor name IDC_ARROW.
If you really mean no matter what, you could use Resource Hacker to change them in system DLLs such as COMCTL32.DLL but this won't work for apps that load custom cursors, you'll have to change these as well.
You could also try changing them via Control Panel/Mouse/Pointers or replacing the files in %WinDir%\Cursors. _________________ "Anything worth doing is worth doing slowly." - Mae West
 |
|
| Back to top |
|
 |
Centra Guest
|
Posted: Thu Sep 11, 2008 5:20 pm Post subject: |
|
|
I am impressed with that wrapper function! I am also intimidated by its power, but yes, I would like to perform this for my apps and not worry about other apps that may overide.
but when I try,
| Code: |
SetSystemCursor("IDC_ARROW", 700, 700)
|
I get a nice big arrow... but still get the Hourglass/arrow.
| Code: |
Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
Loop, Parse, Cursors, `,
{
DllCall( "SetSystemCursor", Uint,CursorHandle, Int,A_Loopfield )
}
|
I keyed in on this above code... but don't throughly understand it. Do you mean it is possible to loop through all possible cursors, and replace with a straight arrow? Best regards, Centra |
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Fri Sep 12, 2008 12:48 am Post subject: |
|
|
Thanks Centra. Under what condition does the hourglass cursor show? Is it normal size?
| Centra wrote: | | I keyed in on this above code... but don't throughly understand it. Do you mean it is possible to loop through all possible cursors, and replace with a straight arrow? |
Yes. The loop just replaces what would be sixteen different DllCalls to replace each cursor. To understand it better try this:
| Code: | Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
Loop, Parse, Cursors, `,
{
; DllCall( "SetSystemCursor", Uint,CursorHandle, Int,A_Loopfield )
Msgbox % A_Loopfield
} |
_________________ "Anything worth doing is worth doing slowly." - Mae West
 |
|
| Back to top |
|
 |
Centra Guest
|
Posted: Fri Sep 12, 2008 1:21 am Post subject: |
|
|
Aie... but can you pls oblige if possible, ... what can the proper script/procedure to achieve my earlier goal?? I am just too confused by all the cryptic ID codes here, but I think I grasp the concept of what is going on from what you show me, but I just don't see all the paths and possibilities  |
|
| Back to top |
|
 |
Krogdor
Joined: 18 Apr 2008 Posts: 1390 Location: The Interwebs
|
Posted: Fri Sep 12, 2008 1:55 am Post subject: |
|
|
| Serenity wrote: | | DerRaphael wrote: | | when i hold space (expecting my cursor to display the slashed circle) its hows the circle for a splitsecond and then starts cycling mad thru all available cursor shapes. |
Hi, thanks for posting. The same thing happens for me on 2k. I tried calling the function with a 100ms timer and got the same thing, so this is what happens when it is called repeatedly. It seems holding down a key has the same effect; this doesn't happen with a mouse button or if called normally.
I tried adding the following and results are the same. If I add a sleep of 1000-2000ms after calling the function it slows it down a little, but the calls seem to be buffered so it still cycles, albeit at a slower rate.
| Code: | #MaxThreadsPerHotkey 1
#MaxThreads 1 |
|
Perhaps try
| Code: | space::
SetSystemCursor("IDC_NO")
KeyWait, Space
RestoreCursors() |
|
|
| Back to top |
|
 |
Slanter
Joined: 28 May 2008 Posts: 739 Location: Minnesota, USA
|
Posted: Fri Sep 12, 2008 2:02 am Post subject: |
|
|
| Serenity wrote: | | Slanter wrote: | | There are also some interesting results when you change the cursor and then mouse over something that also tries to change your cursor (like edit fields and the edges of re-sizable windows). |
What happens, and how are you using the function? From my testing if the app has changed the cursor (Photoshop for instance) it takes priority and it is as if the function was never called, but only for these controls, then it changes back. |
In edit fields for example where the "I" type cursor is showing up, if I hit the hotkey it turns into the normal cursor. If I'm at the top of a resizable window it normally shows the double vertical arrow, when I hit the hotkey it switches to horizontal. There are several more that I've seen, but it looks to me like it's just cycling to the next cursor or something.
| Code: | #SingleInstance Force
space::
SetSystemCursor("IDC_NO")
KeyWait, Space
RestoreCursors()
Return
esc::exitapp |
_________________ Unless otherwise stated, all code is untested
(\__/) This is Bunny.
(='.'=) Cut, copy, and paste bunny onto your sig.
(")_(") Help Bunny gain World Domination. |
|
| Back to top |
|
 |
Serenity
Joined: 07 Nov 2004 Posts: 1271
|
Posted: Fri Sep 12, 2008 11:26 pm Post subject: |
|
|
Thanks Slanter, I can reproduce this. The solution is to always use CopyImage after LoadCursor. I've updated the function to do so. This also solves the bug reported by DerRaphael. Unfortunately the function will always be overridden by controls such as the resize border of windows and edit fields, but at least they now display correctly. _________________ "Anything worth doing is worth doing slowly." - Mae West
 |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|