AutoHotkey Community

It is currently May 26th, 2012, 12:17 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 48 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
PostPosted: September 10th, 2008, 4:07 am 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Last edited by Serenity on September 13th, 2008, 7:14 pm, edited 11 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 1:23 am 
Offline

Joined: October 7th, 2006, 4:50 pm
Posts: 3157
Location: MN, USA
That's a neat trick! Switching cursors is a long-awaited feature. I've never seen it coded that concisely. Thank you!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 3:32 am 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 3:53 am 
Offline

Joined: July 29th, 2005, 5:32 pm
Posts: 179
Nice! :D

You may want to point out how this one differs from..

[fun] SetCursor 1.1 - Set cursor shape for control or window

..since it shares the same stdlib name, but this has been often requested.

Thx!

_________________
.o0[ corey ]0o.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 4:02 am 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 5:28 am 
Offline

Joined: November 23rd, 2007, 10:23 am
Posts: 841
Location: ~/.
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

_________________
Image
    All scripts, unless otherwise noted, are hereby released under CC-BY


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 6:13 am 
Offline

Joined: May 28th, 2008, 2:11 am
Posts: 739
Location: Minnesota, USA
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 6:17 am 
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 :)


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 12:50 pm 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2008, 6:20 pm 
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


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2008, 1:48 am 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2008, 2:21 am 
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 :| :oops:


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2008, 2:55 am 
Offline

Joined: April 18th, 2008, 7:57 am
Posts: 1390
Location: The Interwebs
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()


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 12th, 2008, 3:02 am 
Offline

Joined: May 28th, 2008, 2:11 am
Posts: 739
Location: Minnesota, USA
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 13th, 2008, 12:26 am 
Offline

Joined: November 8th, 2004, 12:46 am
Posts: 1271
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
Image


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 48 posts ]  Go to page 1, 2, 3, 4  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: bbwht, oldbrother and 54 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group