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

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

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

Post by iseahound » 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: 1445
Joined: 13 Aug 2016, 21:04
Contact:

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

Post by iseahound » 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

Post by kczx3 » 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: 1445
Joined: 13 Aug 2016, 21:04
Contact:

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

Post by iseahound » 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.

User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

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

Post by kczx3 » 21 Oct 2021, 13:48

I'd just use StrUpper.

safetycar
Posts: 435
Joined: 12 Aug 2017, 04:27

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

Post by safetycar » 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: 428
Joined: 19 Oct 2022, 20:52

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

Post by ntepa » 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)
}

Post Reply

Return to “Scripts and Functions (v2)”