真正的“打字时隐藏鼠标指针”

(——旧的一些废话——)

Win10 1809 LTSC
打字时经常被站在插入点的鼠标指针挡住视线,有时可忍有时不可忍……
尽管Windows 是自带了“在打字时隐藏指针”选项,但该选项•半•点•用•也•没•有!
在中文网络基本上没人讨论,在英文网络倒是找到一些讨论,基本上都是说无效,有人还总结了具体会在什么地方无效……
(搞笑的是,这选项只是提供一个状态标记,其实际生效需要软件自行判断何时是“打字时”并自行检查上述状态标记,如果状态标记为“True”则在打字时自行隐藏鼠标指针;更搞笑的是,连微软自己的软件都不一定鸟这状态标记……)

问最近火爆的AI DeepSeek,其给出的方案一是使用第三方软件,第一个软件是 AutoHideMouseCursor (但它给了个错或过时的网址),第二个软件是 Cursor Hider(它依然给了个错或过时的网址)。第二个软件是试用版(共享)软件,开发商主页已消失;第一个是免费的而且我已经用过,的确是能隐藏鼠标指针,但问题就是其包含了个定时隐藏鼠标指针的主功能,打字隐藏鼠标只是附带的,也就是必需开着主功能不然附带功能也不生效……
方案二是用Windows自己的设置,已经确认无效;方案三和五是将鼠标指针移动到屏幕边缘,只不过方案三是写了个AutoHotkey脚本实现按快捷键移动鼠标指针而已;方案四则是离题了,让设置触摸板敏感度……

最后,在一个对微软的提问贴中找到了解决办法
AHK脚本 Windows Cursor Hider
完全实现真正的打字时隐藏鼠标指针功能,完美!
↑ 并非完全,在恢复鼠标指针时会造成动画指针只显示第一帧
——
于是,我(以原版脚本为基础?)结合微软资料(winuser.h相关)和AI(Hunyuan & Gemini)重新编写的脚本,使用句柄取代简单图像提取,实现鼠标指针隐藏和恢复提示、可恢复原动画指针、可通过托盘菜单禁用。

与AI的“协作”……
  • 因原脚本的缺陷,本来我已经弃用其一段时间了,但因为 鼠标动画不动了 提出其使用原版(并非我的改版)后遇到原版鼠标动画丢失的 bug ,于是我也尝试寻找这 bug 的源头及解决办法
  • 基础问题:以原版(提供其 Github 网址)代码为基础,使用句柄方式解决动画指针不能恢复的问题(大概意思;使用句柄方式是在和 Hunyuan 问答中确定的)
    • 通过几轮和 Hunyuan 的 battle 之后,包括它傻乎乎地自问自答 working in background 指针应该是哪个ID,它终于给出了比较完整的代码,但代码和源脚本完全不是一回事,推测只是阅读了源脚本的说明
    • 且运行起来就只隐藏了 normal 指针,其他的都没正常隐藏,反馈这 bug 后其非但没修正甚至越改越不知在做什么无用功……
  • 因为我也没看出代码问题在哪(涉及 Windows API 的指令不熟),所以同样问题我又找别的 AI 来尝试
    • 找 DeepSeek ,估计也是没从原网址中查看源代码,只是输出了一通 Basic 代码;我追问 AHK 版本,其给出了一个用快捷键显示隐藏鼠标指针的脚本……根本不用试就知道其理解错了需求……
    • 找 Gemini ,同样的问题,也是同样给出了用快捷键显隐鼠标指针的 AHK 脚本,而且还漏了持续运行的代码;反馈之后,不知它想通了啥,又自作主张将脚本改成定时三秒自动隐藏、移动鼠标恢复显示的 AHK 脚本……
  • 但关键是,Hunyuan 的代码隐藏鼠标指针的响应和功能没问题(因为问了很多,所以其并非仅依据原版脚本的链接去思考,所以代码其实比较全面),只是没隐藏对;而 Gemini 则是非常好地实现了显隐,但没看清题意,而且其自己构建的空白指针是随机雪花的黑块……
  • 于是我决定结合其两者的脚本代码,也就是用后者的显隐功能替代前者,但使用前者的响应逻辑和空白指针生成功能(空白指针是我指示 Hunyuan 参考我复制的源脚本代码段给出的),最终结果完满成功!

可在以下链接中下载 【打字隐藏鼠标指针.ahk】 来获取
——
注意
①如果需要更换鼠标指针图案或主题,需要先关闭该脚本,更换后再重新启动该脚本。(因为脚本启动时会缓存当前在用的整套鼠标指针图案并在恢复显示时调用)
——

——
如果不便访问,以下是【打字隐藏鼠标指针.ahk】的源码:

展开源码
; ==================================================
; 系统光标隐藏与恢复脚本(无外部文件,内存创建透明光标)
; + 托盘菜单开关(启用/禁用功能)
; 触发:主键盘字符键按下(字母、数字、符号键,允许Shift,不含Ctrl/Alt/Win)
; 恢复:鼠标移动超过阈值(10像素)
; 仅针对系统光标,忽略其他程序自定义光标
; ==================================================

#NoEnv
#SingleInstance Force
SetBatchLines, -1

; --- 配置 ---
threshold := 10          ; 鼠标移动恢复阈值(像素)
enabled := true          ; 初始启用光标隐藏功能

; --- 系统光标槽 OCR 常量(十进制)---
global CursorIDs := "32512,32513,32514,32515,32516,32642,32643,32644,32645,32646,32648,32649,32650"
global OriginalHandles := {}

cursor_hidden := false
hiddenX := hiddenY := 0

; 创建透明光标(32x32 单色全透明)
blankCursorHandle := CreateBlankCursor()
if !blankCursorHandle {
    MsgBox, 创建透明光标失败!
    ExitApp
}

; 注册字符键热键(字母、数字、主键盘符号键)
charKeys := "abcdefghijklmnopqrstuvwxyz0123456789"
loop, parse, charKeys
{
    hotkey := "~" A_LoopField
    Hotkey, %hotkey%, TriggerHide, On
}
symbolKeys := "-=[]\\;',./"
loop, parse, symbolKeys
{
    hotkey := "~" A_LoopField
    Hotkey, %hotkey%, TriggerHide, On
}

; 启动鼠标位置监控
SetTimer, CheckMouseMove, 50

; === 托盘菜单 ===
Menu, Tray, NoStandard
Menu, Tray, Add, 切换光标隐藏, ToggleEnabled
Menu, Tray, Add, 退出, ExitScript

; 初始化托盘提示文字
UpdateTrayTip()

Menu, Tray, Icon
return

UpdateTrayTip() {
    statusText := enabled ? "启用" : "禁用"
    Menu, Tray, Tip, 系统光标隐藏脚本`n状态: %statusText%
}

ToggleEnabled:
enabled := !enabled
UpdateTrayTip()
; 先准备 ToolTip 文本
newStatus := enabled ? "启用" : "禁用"
ToolTip, 光标隐藏功能已%newStatus%
SetTimer, RemoveToolTip, 1500
return

RemoveToolTip:
SetTimer, RemoveToolTip, Off
ToolTip
return

ExitScript:
ExitApp

; --------------------------------------------------
TriggerHide:
if !enabled
    return
if (cursor_hidden)
    return

mods := GetModifiersState()
if (mods.Ctrl || mods.Alt || mods.Win)
    return

; 执行隐藏
HideCursors()
cursor_hidden := true
MouseGetPos, hiddenX, hiddenY
ToolTip, 光标已隐藏
SetTimer, RemoveToolTip, 1000
return

; --------------------------------------------------
CheckMouseMove:
if !enabled
    return
if (!cursor_hidden)
    return

MouseGetPos, curX, curY
dx := curX - hiddenX
dy := curY - hiddenY
if (Abs(dx) > threshold || Abs(dy) > threshold) {
    RestoreCursors()
    cursor_hidden := false
    ToolTip, 光标已恢复
    SetTimer, RemoveToolTip, 1000
}
return

; --------------------------------------------------
CreateBlankCursor() {
    width := 32
    height := 32
    rowSize := (width + 7) // 8
    totalBytes := rowSize * height

    VarSetCapacity(andMask, totalBytes, 0xFF)
    VarSetCapacity(xorMask, totalBytes, 0)

    andPtr := &andMask
    xorPtr := &xorMask

    hCursor := DllCall("CreateCursor"
        , "uint", 0
        , "int", 0
        , "int", 0
        , "int", width
        , "int", height
        , "uint", andPtr
        , "uint", xorPtr
        , "uint")
    return hCursor
}

HideCursors() {
    ; 创建透明光标
    hInvisibleCursor := CreateBlankCursor()

    Loop, Parse, CursorIDs, `,
    {
        ID := A_LoopField
        ; 复制当前光标句柄(包含动画状态)并保存
        hCurrent := DllCall("CopyIcon", "Ptr", DllCall("LoadCursor", "Ptr", 0, "Ptr", ID), "Ptr")
        OriginalHandles[ID] := hCurrent
        
        ; 替换为透明
        DllCall("SetSystemCursor", "Ptr", DllCall("CopyIcon", "Ptr", hInvisibleCursor), "Int", ID)
    }
}

RestoreCursors() {
    Loop, Parse, CursorIDs, `,
    {
        ID := A_LoopField
        hSaved := OriginalHandles[ID]
        
        if (hSaved) {
            ; 还原保存的句柄
            DllCall("SetSystemCursor", "Ptr", DllCall("CopyIcon", "Ptr", hSaved), "Int", ID)
            ; 清理保存的句柄副本
            DllCall("DestroyIcon", "Ptr", hSaved)
            OriginalHandles[ID] := 0
        }
    }
    
    ; 刷新系统光标设置
    DllCall("SystemParametersInfo", "UInt", 0x57, "UInt", 0, "Ptr", 0, "UInt", 0)
}

; --------------------------------------------------
GetModifiersState() {
    state := Object()
    state.Ctrl  := GetKeyState("Ctrl")  ? true : false
    state.Alt   := GetKeyState("Alt")   ? true : false
    state.Shift := GetKeyState("Shift") ? true : false
    state.Win   := GetKeyState("LWin") || GetKeyState("RWin") ? true : false
    return state
}

; --------------------------------------------------
; 退出时恢复光标并销毁透明光标句柄
OnExit, Cleanup
Cleanup:
    RestoreCursors()
    if (blankCursorHandle) {
        DllCall("DestroyCursor", "uint", blankCursorHandle)
    }
ExitApp

2 个赞

确实有时候挺烦人

如果我使用了自己定义的鼠标指针, 使用它会被reset成系统指针. :frowning:

我用了这么久也没发现这种情况,但的确会影响鼠标指针图案更换,这我补充在顶楼了。
我现在在用的就是自定义的鼠标:
image
——
隐藏和重现鼠标指针的代码是包含在官方的帮助中的(虽然这脚本有一点点和官方的风格差异),如果有将设好的指针在运行后还原成默认指针的情况肯定会有反馈和修正。
——
如果是通过系统鼠标设置之外的情况自定义的鼠标,或者是Win11,我就不清楚了。

这个脚本12年了,生命力啊

有赖于Windows 数十年如一日的向后兼容性 :wink:

有人反馈和剪贴板历史有冲突。

也不知道这脚本需要维护不…

我自己修改和fork之前没看issue :joy:
(为啥github现在不让看别人的脚本的各个fork ……)
——
里面两个issue提到剪贴板历史的问题,如果是Win-V快捷键的话,在我这连修改之前的版本都没问题(虽然按下后鼠标指针不见了,但动一下就出来,同时剪贴板历史弹窗一直可见且可正常操作),更不用说修改后的版本了。
——
从issue里面看到,还有个把空格、退格、删除键也纳入触发键的版本,还有一个V2超级加强版.

最快快捷键,设置“检测光标=1”,就可以在打字时自动将光标移到屏幕边缘。设置“检测选框=1”,可以只在输入中文时将光标移到边缘。当光标处于屏幕边缘时会提示输入法中英文状态。当光标不在屏幕边缘时会解除打字状态,可以设置“边缘锁定=1”来防止误触鼠标或触摸板。

更新:重构版【打字隐藏鼠标指针.ahk】,(以原版脚本为基础?)结合微软资料(winuser.h相关)和AI(Hunyuan & Gemini)重新编写的脚本,使用句柄取代简单图像提取,实现鼠标指针隐藏和恢复提示、可恢复原动画指针、可通过托盘菜单禁用。

我以前也用ahk折腾过, 后来发现了这个 AutoHideMouseCursor 5.99 Automatically hide the mouse cursor 就一直用了

更新:重构版【打字隐藏鼠标指针.ahk】,(以原版脚本为基础?)结合微软资料(winuser.h相关)和AI(Hunyuan & Gemini)重新编写的脚本,使用句柄取代简单图像提取,实现鼠标指针隐藏和恢复提示、可恢复原动画指针、可通过托盘菜单禁用。

谢谢,隐藏和恢复的提示,以及托盘菜单禁用,我给去掉了,毕竟重复看多了提示有点烦,哈哈。而托盘菜单,基本开了之后就没必要关闭了,所以两个我都去掉了。反而我正在打算将在隐藏前先保存当前指针位置,隐藏指针后,将鼠标移动到屏幕X=0 Y=0的位置,恢复的时候再调用原先保存的位置。目的是将指针彻底不挡道,毕竟原先只是将指针“透明化”了,实际上还是有可能会停留在输入法候选词上面,看到有半透明的候选词背景。

抱歉,多少有点洁癖哈哈。

我以前也用ahk折腾过, 后来发现了这个 AutoHideMouseCursor 5.99 Automatically hide the mouse cursor 就一直用了

AutoHideMouseCursor 5.99 Automatically hide the mouse cursor 在win10上有bug。不如楼主的这个小工具方便和小巧。

用过原版 Windows Cursor Hider 脚本,发现对hdpi指针兼容性不好,恢复时会模糊掉;也用过 AutoHideMouseCursor ,但是在win11下有未知bug,老是不生效。所以干脆在ai帮助下用cpp写了个原生win32 api的应用 去你的鼠标指针 ,实现在任何场景下打字时隐藏鼠标指针,支持高dpi、动画指针、开机自启、托盘快捷操作,欢迎使用和提issue。GitHub链接: github.com/ttwjz/FuckMouseCursor

excellent work!50k的程序,跟楼主的程序效果一样,不过在输入的时候大概会有0.5秒的隐藏指针延迟。如果可以调节的话更好。

不想用Autohotkey的话就用您这个方便多了。

0.5s是特意加的防抖,考虑有时候只是单纯按一个键然后马上就会移动鼠标的场景,就不会造成指针闪烁。为了轻量化所以目前没有加修改配置的功能,暂时可以修改硬编码的HIDE_DELAY_MS值,然后重新编译。后期会考虑增加配置文件ini,开放更多自定义选项。感谢您的建议。