惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
有赞技术团队
有赞技术团队
www.infosecurity-magazine.com
www.infosecurity-magazine.com
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
V
Visual Studio Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
人人都是产品经理
人人都是产品经理
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Cloudflare Blog
N
News and Events Feed by Topic
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
AWS News Blog
AWS News Blog
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Spread Privacy
Spread Privacy
J
Java Code Geeks
博客园 - 聂微东
T
Tor Project blog
宝玉的分享
宝玉的分享
博客园 - 叶小钗
Webroot Blog
Webroot Blog
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
H
Heimdal Security Blog
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
I
InfoQ
Security Latest
Security Latest
Martin Fowler
Martin Fowler
Hacker News: Ask HN
Hacker News: Ask HN
P
Privacy International News Feed
C
CERT Recently Published Vulnerability Notes
Latest news
Latest news
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
H
Help Net Security
L
LINUX DO - 最新话题
L
LINUX DO - 热门话题

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第11章 - 全局事件监听(GoHook)
我才是银古 · 2026-06-24 · via 博客园 - 我才是银古

第11章 - 全局事件监听(GoHook)

前面章节我们都在“主动控制”计算机——让程序去移动鼠标、敲键盘。本章则反过来:让程序“被动感知”用户的键盘鼠标动作。这种全局事件监听能力由 RobotGo 的配套子库 gohookrobotn/gohook)提供,是实现全局热键、操作录制、快捷工具的核心。

11.1 什么是全局事件监听

全局事件监听(Global Event Hook)是指:无论焦点在哪个窗口,程序都能捕获到系统范围内发生的键盘按键、鼠标点击、移动、滚轮等事件。它与普通 GUI 程序“只能收到自己窗口内事件”不同,是系统级的钩子(hook)。

典型用途:

  • 全局热键:按下某个组合键(如 Ctrl+Shift+Q)触发自定义动作,即使你的程序在后台。
  • 操作录制:记录用户的一连串键鼠操作,之后回放。
  • 效率工具:监听特定按键序列触发文本扩展、窗口切换等。

11.2 引入 gohook

事件监听不在 RobotGo 主库,需要单独导入 gohook:

import (
    hook "github.com/robotn/gohook"
)

并在 Linux 上确保安装了 GoHook 所需的依赖库(xcb、libxkbcommon 等,见第 2 章),否则无法编译或运行。

11.3 注册事件回调:Register

hook.Register 是最常用的高层 API,用于注册“当某个按键/组合键被按下时”执行的回调函数。

package main

import (
    "fmt"

    hook "github.com/robotn/gohook"
)

func main() {
    fmt.Println("--- 按 ctrl + shift + q 停止监听 ---")
    hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
        fmt.Println("触发了 ctrl-shift-q")
        hook.End() // 停止监听
    })

    fmt.Println("--- 按 w 键 ---")
    hook.Register(hook.KeyDown, []string{"w"}, func(e hook.Event) {
        fmt.Println("你按了 w")
    })

    s := hook.Start()
    <-hook.Process(s) // 阻塞,直到 hook.End() 被调用
}

理解这个程序的关键:

  • hook.Register(事件类型, 按键组合, 回调):声明“当满足条件时执行回调”。
  • hook.KeyDown:事件类型,表示“按键按下”。还有 hook.KeyUphook.MouseDownhook.MouseUphook.MouseMovehook.MouseWheel 等。
  • hook.Start():启动事件监听,返回一个事件通道。
  • hook.Process(s):处理已注册的回调,返回一个通道;<- 阻塞主协程直到 hook.End() 被调用。
  • hook.End():停止监听,使 Process 返回、程序得以退出。

11.4 低层事件流:直接消费事件通道

如果你不想用 Register 的“注册回调”模式,而是想自己处理每一个原始事件,可以直接消费 hook.Start() 返回的事件通道:

func low() {
    evChan := hook.Start()
    defer hook.End()

    for ev := range evChan {
        fmt.Println("捕获事件:", ev)
    }
}

这种“低层模式”能拿到所有事件的完整信息(事件类型、键码、鼠标坐标等),适合做操作录制、事件分析等需要原始数据的场景。

11.5 事件结构 hook.Event

每个事件都是一个 hook.Event 结构,包含诸如事件类型(Kind)、键码(Keycode/Rawcode)、键盘字符、鼠标按键、坐标(XY)、滚轮量等字段。你可以根据 ev.Kind 判断事件类型再读取对应字段:

for ev := range evChan {
    switch ev.Kind {
    case hook.KeyDown:
        fmt.Println("按键按下, keychar:", ev.Keychar)
    case hook.MouseDown:
        fmt.Printf("鼠标按下, 坐标 (%d, %d)\n", ev.X, ev.Y)
    case hook.MouseMove:
        fmt.Printf("鼠标移动到 (%d, %d)\n", ev.X, ev.Y)
    }
}

不同版本的 gohook 字段名可能略有差异,使用时可结合 fmt.Printf("%+v", ev) 打印完整结构来确认可用字段。

11.6 添加单次事件等待:AddEvent / AddEvents

gohook 还提供了“阻塞等待某个事件发生一次”的便捷函数,适合简单的“等用户按某个键再继续”的场景:

func event() {
    // 等待组合键 q + ctrl + shift
    ok := hook.AddEvents("q", "ctrl", "shift")
    if ok {
        fmt.Println("检测到组合键")
    }

    // 等待单个键 k
    keve := hook.AddEvent("k")
    if keve {
        fmt.Println("你按了 k")
    }

    // 等待鼠标左键
    mleft := hook.AddEvent("mleft")
    if mleft {
        fmt.Println("你按了鼠标左键")
    }
}

AddEvent / AddEvents 会阻塞直到对应事件发生,返回 true。鼠标按键用 "mleft""mright" 等名称表示。

11.7 完整示例

下面是官方 gohook 示例的整理版,把三种用法放在一起(实际运行时一次选用一种):

package main

import (
    "fmt"

    hook "github.com/robotn/gohook"
)

func main() {
    add()
    // low()
    // event()
}

func add() {
    fmt.Println("--- 按 ctrl + shift + q 停止 ---")
    hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
        fmt.Println("ctrl-shift-q")
        hook.End()
    })

    fmt.Println("--- 按 w ---")
    hook.Register(hook.KeyDown, []string{"w"}, func(e hook.Event) {
        fmt.Println("w")
    })

    s := hook.Start()
    <-hook.Process(s)
}

func low() {
    evChan := hook.Start()
    defer hook.End()
    for ev := range evChan {
        fmt.Println("hook:", ev)
    }
}

func event() {
    ok := hook.AddEvents("q", "ctrl", "shift")
    if ok {
        fmt.Println("add events...")
    }

    keve := hook.AddEvent("k")
    if keve {
        fmt.Println("you press k")
    }

    mleft := hook.AddEvent("mleft")
    if mleft {
        fmt.Println("you press mouse left")
    }
}

11.8 实战:用热键触发自动化动作

把事件监听(gohook)与主动控制(robotgo)结合,就能做出真正的“全局热键工具”。下面演示按 Ctrl+Shift+P 自动粘贴一段签名文本:

package main

import (
    "fmt"

    "github.com/go-vgo/robotgo"
    hook "github.com/robotn/gohook"
)

func main() {
    fmt.Println("按 ctrl+shift+p 粘贴签名,按 ctrl+shift+q 退出")

    hook.Register(hook.KeyDown, []string{"p", "ctrl", "shift"}, func(e hook.Event) {
        robotgo.WriteAll("此致\n敬礼\n张三")
        robotgo.MilliSleep(50)
        robotgo.KeyTap("v", "ctrl")
    })

    hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
        hook.End()
    })

    s := hook.Start()
    <-hook.Process(s)
}

这个小程序常驻后台,任何时候按下热键都会自动粘贴预设文本——这正是事件监听与桌面控制协同的威力。

11.9 实战技巧与注意事项

  1. 平台权限:与控制类似,事件监听在 macOS 上同样需要“辅助功能”权限;Linux 上需要安装 GoHook 相关依赖库。
  2. 务必能退出:注册一个“停止热键”(如 Ctrl+Shift+Q 调 hook.End()),否则 <-hook.Process(s) 会一直阻塞,程序无法正常退出。
  3. 回调要轻量:事件回调中不要做耗时操作,以免阻塞事件流。耗时任务应放到单独的 goroutine。
  4. 避免自触发:当你用 robotgo 模拟按键时,gohook 也可能捕获到这些“自己发出的”事件,设计逻辑时要避免无限循环触发。
  5. 录制回放:用低层事件流记录事件序列时,注意同时记录时间戳,回放时按相对时间间隔重放才自然。

11.10 小结

本章我们学习了基于 gohook 子库的全局事件监听:用 Register 注册热键回调、用 Start/Process/End 控制监听生命周期、用低层事件通道消费原始事件、用 AddEvent/AddEvents 阻塞等待单次事件,并通过“热键触发自动粘贴”示例把监听与控制结合起来。至此,RobotGo 主库及核心子库的能力已基本讲完。下一章我们进入进阶话题——基于 OpenCV 的图像识别。