SetSystemCursor() and RestoreCursor() - Changing the system cursor

Post your working scripts, libraries and tools.
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

SetSystemCursor() and RestoreCursor() - Changing the system cursor

27 Aug 2021, 15:07

SetSystemCursor() and RestoreCursor()
Changes the pointer. Original by Serenity.
SetSystemCursor("Cross")
SetSystemCursor("IDC_Cross")

Set cursor size in pixels.
SetSystemCursor("IDC_Cross", 100, 100)

Supports .ani, .cur, and .ico files.
SetSystemCursor("Animation.ani")

Creates an invisible cursor.
SetSystemCursor()

If the system cursor is not changing, restore the original cursor.
RestoreCursor()

This code has been updated for v2. Github: https://github.com/iseahound/SetSystemCursor

Code: Select all

; Source:   Serenity - https://autohotkey.com/board/topic/32608-changing-the-system-cursor/
; Modified: iseahound - https://www.autohotkey.com/boards/viewtopic.php?t=75867

SetSystemCursor(Cursor := "", cx := 0, cy := 0) {

   static SystemCursors := Map("APPSTARTING", 32650, "ARROW", 32512, "CROSS", 32515, "HAND", 32649, "HELP", 32651, "IBEAM", 32513, "NO", 32648,
                           "SIZEALL", 32646, "SIZENESW", 32643, "SIZENS", 32645, "SIZENWSE", 32642, "SIZEWE", 32644, "UPARROW", 32516, "WAIT", 32514)

   if (Cursor = "") {
      AndMask := Buffer(128, 0xFF), XorMask := Buffer(128, 0)

      for CursorName, CursorID in SystemCursors {
         CursorHandle := DllCall("CreateCursor", "ptr", 0, "int", 0, "int", 0, "int", 32, "int", 32, "ptr", AndMask, "ptr", XorMask, "ptr")
         DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
      }
      return
   }

   if (Cursor ~= "^(IDC_)?(?i:AppStarting|Arrow|Cross|Hand|Help|IBeam|No|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait)$") {
      Cursor := RegExReplace(Cursor, "^IDC_")

      if !(CursorShared := DllCall("LoadCursor", "ptr", 0, "ptr", SystemCursors[StrUpper(Cursor)], "ptr"))
         throw Error("Error: Invalid cursor name")

      for CursorName, CursorID in SystemCursors {
         CursorHandle := DllCall("CopyImage", "ptr", CursorShared, "uint", 2, "int", cx, "int", cy, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
      }
      return
   }

   if FileExist(Cursor) {
      SplitPath Cursor,,, &Ext:="" ; auto-detect type
      if !(uType := (Ext = "ani" || Ext = "cur") ? 2 : (Ext = "ico") ? 1 : 0)
         throw Error("Error: Invalid file type")

      if (Ext = "ani") {
         for CursorName, CursorID in SystemCursors {
            CursorHandle := DllCall("LoadImage", "ptr", 0, "str", Cursor, "uint", uType, "int", cx, "int", cy, "uint", 0x10, "ptr")
            DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
         }
      } else {
         if !(CursorShared := DllCall("LoadImage", "ptr", 0, "str", Cursor, "uint", uType, "int", cx, "int", cy, "uint", 0x8010, "ptr"))
            throw Error("Error: Corrupted file")

         for CursorName, CursorID in SystemCursors {
            CursorHandle := DllCall("CopyImage", "ptr", CursorShared, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
            DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
         }
      }
      return
   }

   throw Error("Error: Invalid file path or cursor name")
}

RestoreCursor() {
   return DllCall("SystemParametersInfo", "uint", SPI_SETCURSORS := 0x57, "uint", 0, "ptr", 0, "uint", 0)
}


The following is a demonstration script that switches your mouse pointer between all 14 built-in Windows mouse cursors.

Code: Select all

#Requires AutoHotkey v2-a138+
#include Cursor.ahk

dict := {APPSTARTING: 32650, 
         ARROW: 32512,
         CROSS: 32515,
         HAND: 32649, 
         HELP: 32651,
         IBEAM: 32513, 
         NO: 32648,
         SIZEALL: 32646, 
         SIZENESW: 32643, 
         SIZENS: 32645, 
         SIZENWSE: 32642, 
         SIZEWE: 32644, 
         UPARROW: 32516, 
         WAIT: 32514}

for name, id in dict.ownprops() {
   SetSystemCursor name
   MsgBox name
   RestoreCursor
}
Last edited by iseahound on 21 Oct 2021, 13:19, edited 1 time in total.
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: SetSystemCursor() and RestoreCursor() - Changing the system cursor

20 Oct 2021, 23:41

Uploaded a version that uses static map instead of Loop Parse. It is about 2% slower, unless cx or cy is set in which it is 10% faster. I doubt that such a performance hit matters—but I really liked Serenity's old script and wanted to keep it as simple as possible.
Benchmark
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: SetSystemCursor() and RestoreCursor() - Changing the system cursor

21 Oct 2021, 07:34

Why regex instead of Map.Has()? Also, why would you need to loop over the Map if the Map has the value?
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: SetSystemCursor() and RestoreCursor() - Changing the system cursor

21 Oct 2021, 13:20

Good points. The Regex is for detecting the IDC_ prefix. Do you know if a case insensitive map can be created in one line? Currently I'm using StrUpper which is fine.
safetycar
Posts: 435
Joined: 12 Aug 2017, 04:27

Re: SetSystemCursor() and RestoreCursor() - Changing the system cursor

24 Oct 2021, 12:17

iseahound wrote:
21 Oct 2021, 13:20
Do you know if a case insensitive map can be created in one line?
Not the most beautiful thing but it's possible to do... static m := (m:=Map(), m.caseSense:=False, m.set("APPSTARTING", 32650, "ARROW", 32512, ...)).
I don't know if that's fits your idea.

Edit: .Set() returns the map. For a second I thought I created a bug but it actually works like that, without adding a ", m" at the end.
ntepa
Posts: 426
Joined: 19 Oct 2022, 20:52

Re: SetSystemCursor() and RestoreCursor() - Changing the system cursor

22 Sep 2023, 13:10

I modified the SetSystemCursor function to also accept a base64 string:

Code: Select all

;example
B64 := "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAM6wAADOsB5dZE0gAAAAZQTFRFAAAALuUr"
       . "GAEszAAAAAJ0Uk5TAP9bkSK1AAAAF0lEQVR4nGNgYGxgYKAO8R8IIAT1DAUAMHgXFyzrq5IAAAAASUVORK5CYII="
SetSystemCursor(B64)
Sleep 5000
RestoreCursor()

Code: Select all

SetSystemCursor(Cursor := "", cx := 0, cy := 0) {

   static SystemCursors := Map("APPSTARTING", 32650, "ARROW", 32512, "CROSS", 32515, "HAND", 32649, "HELP", 32651, "IBEAM", 32513, "NO", 32648,
                           "SIZEALL", 32646, "SIZENESW", 32643, "SIZENS", 32645, "SIZENWSE", 32642, "SIZEWE", 32644, "UPARROW", 32516, "WAIT", 32514)

   if (Cursor = "") {
      AndMask := Buffer(128, 0xFF), XorMask := Buffer(128, 0)

      for CursorName, CursorID in SystemCursors {
         CursorHandle := DllCall("CreateCursor", "ptr", 0, "int", 0, "int", 0, "int", 32, "int", 32, "ptr", AndMask, "ptr", XorMask, "ptr")
         DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
      }
      return
   }

   if (Cursor ~= "^(IDC_)?(?i:AppStarting|Arrow|Cross|Hand|Help|IBeam|No|SizeAll|SizeNESW|SizeNS|SizeNWSE|SizeWE|UpArrow|Wait)$") {
      Cursor := RegExReplace(Cursor, "^IDC_")

      if !(CursorShared := DllCall("LoadCursor", "ptr", 0, "ptr", SystemCursors[StrUpper(Cursor)], "ptr"))
         throw Error("Error: Invalid cursor name")

      for CursorName, CursorID in SystemCursors {
         CursorHandle := DllCall("CopyImage", "ptr", CursorShared, "uint", 2, "int", cx, "int", cy, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
      }
      return
   }

   if FileExist(Cursor) {
      SplitPath Cursor,,, &Ext:="" ; auto-detect type
      if !(uType := (Ext = "ani" || Ext = "cur") ? 2 : (Ext = "ico") ? 1 : 0)
         throw Error("Error: Invalid file type")

      if (Ext = "ani") {
         for CursorName, CursorID in SystemCursors {
            CursorHandle := DllCall("LoadImage", "ptr", 0, "str", Cursor, "uint", uType, "int", cx, "int", cy, "uint", 0x10, "ptr")
            DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
         }
      } else {
         if !(CursorShared := DllCall("LoadImage", "ptr", 0, "str", Cursor, "uint", uType, "int", cx, "int", cy, "uint", 0x8010, "ptr"))
            throw Error("Error: Corrupted file")

         for CursorName, CursorID in SystemCursors {
            CursorHandle := DllCall("CopyImage", "ptr", CursorShared, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
            DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
         }
      }
      return
   }

   ; Base64PNG_to_HICON https://www.autohotkey.com/boards/viewtopic.php?p=524691#p524691
   BLen := StrLen(Cursor), nBytes := Floor(StrLen(RTrim(Cursor,"=")) * 3/4 ),  Bin :=Buffer(nBytes)
   CursorShared := DllCall("Crypt32\CryptStringToBinary", "str", Cursor, "uint", BLen, "uint", 1, "ptr", Bin, "uint*", nBytes, "uint", 0, "uint", 0)
                   && DllCall("User32\CreateIconFromResourceEx", "ptr", Bin, "uint", nBytes, "int", true, "uint", 0x30000, "int", cx, "int", cy, "uint", 0, "uptr")
   if CursorShared {
      for CursorName, CursorID in SystemCursors {
         CursorHandle := DllCall("CopyImage", "ptr", CursorShared, "uint", 2, "int", 0, "int", 0, "uint", 0, "ptr")
         DllCall("SetSystemCursor", "ptr", CursorHandle, "int", CursorID) ; calls DestroyCursor
      }
      return
   }

   throw Error("Error: Invalid file path or cursor name")
}

RestoreCursor() {
   return DllCall("SystemParametersInfo", "uint", SPI_SETCURSORS := 0x57, "uint", 0, "ptr", 0, "uint", 0)
}

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: Descolada, kunkel321 and 17 guests