windowsmousemouse-cursor

How to globally hide/show the mouse cursor in Windows, programmatically?


In some applications, such as Notepad, when you start typing, the mouse cursor disappears. Then as soon as you move the mouse, the cursor reappears. This is great, because a lot of the time, the mouse cursor gets in the way of typing, especially when using large inverted cursors.

However, this doesn't work in most modern applications; only those that use common Windows controls like the textarea control.

I want to create an app that runs in the background, which globally listens for any keypress, no matter which app is in the foreground, and hides the mouse pointer until the mouse is physically moved again.

I have tried searching everywhere, but every solution either does nothing at all, or only hides the cursor when it is over the test application's window (not for all windows). I tried ChatGPT and it provides broken solutions that don't work. I have tried "solutions" in AutoHotkey, C#, C++ with no luck.

I don't care what programming language is used, as long as this can be achieved. Please do not post solutions that only hide the cursor for the app itself. The cursor must disappear, regardless of which app has focus, and regardless of where the cursor is located, when any keyboard key is pressed. Once the mouse is moved, the cursor should reappear where it last existed (should not be teleported somewhere else).

The HideCursor() function of windows.h does not work. CreateCursor/SetSystemCursor do not work. Running the script/app as Administrator does not work.

My current solution is to simply move the cursor about 100px away from its current spot. This usually gets it out of the way of typing, but sometimes it then hovers over something else that pops up in the way, so it's not an optimal solution.

MANY THANKS for any working suggestions.


Solution

  • I believe this AHK v2 script does exactly what you describe. Other than non-standard keys like media keys and macro keys etc I can't find a keyboard key that does not trigger the hiding of the cursor:

    InstallKeybdHook
    InstallMouseHook
    CoordMode("mouse", "screen")
    
    OnExit (*) => SystemCursor("Show")  ; Ensure the cursor is made visible when the script exits
    
    SetTimer(Main, 10)
    
    Main() {
    
       if(A_TimeIdleKeyboard < 25)
          ;A keyboard key has been pressed, hide cursor
          SystemCursor("Hide")
    
       else if(A_TimeIdleMouse < 25)
          ;Mouse have been used, show cursor
          SystemCursor("Show")
    }    
    
    SystemCursor(cmd) { ; cmd = "Show|Hide|Toggle|Reload"
    
       static visible := true, c := Map()
       static sys_cursors := [32512, 32513, 32514, 32515, 32516, 32642, 32643, 32644, 32645, 32646, 32648, 32649, 32650]
    
       if (cmd = "Reload" or !c.Count) { ; Reload when requested or at first call.
       
          for i, id in sys_cursors {          
             h_cursor := DllCall("LoadCursor", "Ptr", 0, "Ptr", id)
             h_default := DllCall("CopyImage", "Ptr", h_cursor, "UInt", 2, "Int", 0, "Int", 0, "UInt", 0)
             h_blank := DllCall("CreateCursor", "Ptr", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "Ptr", Buffer(32 * 4, 0xFF), "Ptr", Buffer(32 * 4, 0))
             c[id] := { default: h_default, blank: h_blank }
          }
       }
    
       switch cmd {
          case "Show":
             if (!visible)
                visible := true
             else
                return
          case "Hide":
             if (visible)
                visible := false
             else
                return
          case "Toggle":
             visible := !visible
          default: return
       }
    
       for id, handles in c {       
          h_cursor := DllCall("CopyImage", "Ptr", visible ? handles.default : handles.blank, "UInt", 2, "Int", 0, "Int", 0, "UInt", 0)
          DllCall("SetSystemCursor", "Ptr", h_cursor, "UInt", id)
       }
    }
    

    The 'SystemCursor' function probably makes it look intimidating but it was copied wholesale from the official DllCall documentation. Though I did update it with a optimisation, now it only does its thing when actually needed instead of every single time for no reason.

    Update: Updated script to v2. Instead of manually checking the cursor position simply checking the mouse idle time instead, and SetTimer instead of a loop with Sleep. Much cleaner and should function near-identically (now mouse clicks also reveal it, which I don't see as a bad thing)

    Update 2: SystemCursor optimization. Now only does the dll calls when the visibility state changes instead of always for no reason