• 安装 Hammerspoon
  • 设置
  • 确保 init.lua 正常运行
  • 绑定系统全局热键
    • maps 的工作原理
    • 示例
  • 结论
  • 实用链接
  • 首页
  • 文章
  • 笔记
  • 书架
  • 作者
🇺🇸 en 🇫🇷 fr 🇮🇳 ml

Nathaniel Thomas

macOS 上的 Hammerspoon 魔法

2023年8月4日

如果你是个极客,并且使用 Mac 有一段时间了,你可能会记得 Applescript。这是苹果开发的一种语言,旨在让中级到高级用户编写简单的脚本来控制 Mac 应用程序。它实际上是为了模仿英语而设计的,因此访问一个像素的代码可能会写成这样:

pixel 7 of row 3 of TIFF image "my bitmap"

或者甚至这样:

TIFF image "my bitmap"'s 3rd row's 7th pixel

不用说,现代编程语言不这样设计是有充分理由的:它不具备扩展性。任何长期使用 Applescript 的人都知道,你很快就会遇到它的局限性。苹果在 2016 年非正式地弃用了它,当时其创始人 Sal Soghoian 因“商业原因”被解雇(来源)。

在 Applescript 逐渐衰落的同时,一位名叫 Steven 的智者正在编写 Hammerspoon,这是一个连接 macOS API 的 Lua 桥接器。Lua 作为一种简单且现代的编程语言,非常适合这个任务。那么,让我们来看看如何使用它。

安装 Hammerspoon

使用 Homebrew。

brew install hammerspoon

设置

创建一个名为 ~/.hammerspoon/init.lua 的文件。

mkdir ~/.hammerspoon && touch ~/.hammerspoon/init.lua

打开应用程序。

open -a Hammerspoon

你应该会看到一个类似这样的 Lua 控制台(不用担心里面的文字)。

console

点击偏好设置,确保已启用辅助功能。

preferences

如果你愿意,也可以勾选 登录时启动 Hammerspoon。

确保 init.lua 正常运行

将以下内容粘贴到你创建的 init.lua 文件中。

local logger = hs.logger.new("init.lua", "debug")
logger.d("SUCCESSFULLY RAN init.lua")

保存文件,并在控制台中点击 Reload 按钮。如果你看到输出,说明一切正常。

绑定系统全局热键

我主要使用 Hammerspoon 来创建“智能”热键,而不必去折腾偏好设置中那些有问题的热键,或者下载像 Karabiner 这样的另一个应用。以下是一些可以在 init.lua 中使用的入门函数,它们能让创建新热键变得极其简单。

local function stringsplit(inputstr, sep)
	if sep == nil then
		sep = "%s"
	end
	local t = {}
	for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
		table.insert(t, str)
	end
	return t
end

local function keyMapArray(global_modifiers, mappings)
	for k, v in pairs(mappings) do
		if type(v) == "string" then
			-- 我们要映射到的修饰键
			local modifiers = {}
			local splitkey = stringsplit(v, "-")

			local splitlen = #splitkey
			-- 将 v 中包含的任何映射修饰键
			-- 添加到修饰键数组中
			for i = 1, splitlen - 1 do
				modifiers[i] = splitkey[i]
			end

			-- 获取要传递给绑定函数的数字键码
			local mappedKeyCode = hs.keycodes.map[splitkey[splitlen]]
			if mappedKeyCode then
				hs.hotkey.bind(global_modifiers, k, function()
					hs.eventtap.keyStroke(modifiers, mappedKeyCode, 1)
				end)
			else
				-- 数字键码为 nil,表示它不存在
				-- 现在我们尝试将其视为系统键事件代码,例如
				-- PLAY
				hs.hotkey.bind(global_modifiers, k, function()
					hs.eventtap.event.newSystemKeyEvent(v, true):post()
					hs.eventtap.event.newSystemKeyEvent(v, false):post()
				end)
			end
		elseif type(v) == "function" then
			hs.hotkey.bind(global_modifiers, k, v)
		end
	end
end

local function processMaps(maps)
	for globals, mappings in pairs(maps) do
		keyMapArray(globals, mappings)
	end
end

如果你不熟悉 Lua 并感到有些畏惧,别担心。这个函数的目的是让你无需学习 Lua 就能上手。让我们看看如何创建一个新的映射。假设你想要在系统范围内使用 Vim 风格的箭头键绑定,并通过 ctrl 激活。

-- 定义映射
local maps = {
	[{ "ctrl" }] = {
		-- 箭头键
		["h"] = "left",
		["j"] = "down",
		["k"] = "up",
		["l"] = "right",
	},
}

-- 绑定映射
processMaps(maps)

只需将其放在文件底部,重新加载配置,你的键就应该被映射好了!

maps 的工作原理

maps 是一个数组,或者说是一个表格列表。每个表格的键是一个修饰键数组,值是一个将某个键映射到另一个键的表格。在上面的例子中,唯一的修饰键是 ctrl,这意味着其表格中的所有映射仅在按住 ctrl 时才会激活。然后它将 h 映射到左箭头,j 映射到下箭头,依此类推。

如果你还是不明白,别担心。只需查看这些示例并根据你的需求进行修改即可。

示例

以下是一些 maps tables 的更多示例:

将 ctrl-n 映射为删除单词,ctrl-m 映射为删除字符,ctrl-. 映射为删除光标前的单词,ctrl-, 映射为删除光标前的字符。

-- 在 `maps` 中
[{ "ctrl" }] = {
	-- 删除操作
	["n"] = "alt-delete",
	["m"] = "delete",
	[","] = "forwarddelete",
	["."] = "alt-forwarddelete",
},

将功能键映射为媒体控制。注意这些映射没有使用任何修饰键,因此只需按下 f1 即可激活绑定:

-- 在 `maps` 中
[{}] = {
	-- 媒体控制
	["f1"] = "MUTE",
	["f2"] = "SOUND_DOWN",
	["f3"] = "SOUND_UP",

	["f5"] = "PREVIOUS",
	["f6"] = "PLAY",
	["f7"] = "NEXT",
},

使用 alt-ctrl-r 重新加载 Hammerspoon 配置

-- 在 `maps` 中
[{ "alt", "ctrl" }] = {
	-- 重新加载 Hammerspoon 配置
	["r"] = hs.reload,
},

使用 cmd-g 让笔记本电脑进入睡眠状态

-- 在 `maps` 中
[{ "cmd" }] = {
	["g"] = function()
		-- 该函数执行一个 shell 命令
		os.execute("pmset sleepnow")
	end,
},

使用 cmd-shift-o 启动 Obsidian 应用

[{ "cmd", "shift" }] = {
	["o"] = function()
		hs.application.launchOrFocus("Obsidian")
	end,
},

在定义完所有内容后,记得运行 processMaps(maps)!

local maps = {
	[{}] = {
		-- 内容
	},
	[{ "cmd" }] = {
		-- 内容
	},
	[{ "cmd", "shift" }] = {
		-- 内容
	},
	-- ...
}

processMaps(maps)

结论

Hammerspoon 是一款极其强大的工具。一旦你熟悉了 Lua,其可能性几乎是无限的。本文中我只重点介绍了热键功能,因为这是我发现 Hammerspoon 最大的应用场景。

如果你想查看 Hammerspoon 提供的所有 API 函数,请查阅他们的文档。你可以用这款应用做很多事情——比如将事件绑定到电池状态、调整显示器亮度、打开对话框提示、发送 HTTP 请求、复制和粘贴等等。此外,你还可以通过 Luarocks 利用整个 Lua 生态系统。

另外,别忘了看看 r/Hammerspoon,了解其他爱好者都在做些什么。我们那里需要更多成员加入 :)

实用链接

与 Hammerspoon 相关的资源集合。我会在遇到新内容时保持更新。

网站 描述
文档 完整的 Hammerspoon API 文档
r/Hammerspoon 包含各种 Spoon 和展示的 Reddit 子论坛
Spoons Spoon 列表,Spoon 是 Hammerspoon 的插件。
ControlEscape.spoon 将你的 Control 键映射为轻按时触发 ESC,长按时触发 Ctrl。对 Vim 用户很有用

转向 Obsidian
→

back to top