Macbook 下嘉立创 EDA 触控板手势修复
我也不知道为什么学院会给所有专业的人安排硬件实训,我一个人工智能方向的学生为什么要使用嘉立创画板子搞四足机器人呢?
Mac 版嘉立创 EDA 在触控板上的默认手感有点反直觉:双指上下滚动经常被当成放大缩小,而不是像在触摸屏上一样拖动画布。真正想要的效果其实很简单:
- 双指上下滚动:拖动画布。
- 双指左右滚动:保持原本的左右移动。
- 双指捏合:正常放大缩小。
嘉立创 EDA 里虽然可以用 Ctrl + 滚轮 实现拖动画布,但放在触控板上就会变得很难用。每次都按着 Ctrl 再双指上下拖,实在不像一个正常 Mac 软件该有的操作方式。
问题
在 macOS 上,触控板的双指上下滚动会被嘉立创 EDA 当成滚轮缩放处理。也就是说:
- 左右移动正常
- 上下滑动变成了放大缩小
- 双指捏合变成了上下滚动
嘉立创其实针对这个问题提出了解决方法,但是具体解决方法吗……如提。
解决思路
用 Hammerspoon 拦截嘉立创 EDA 的输入事件,只在嘉立创 EDA 处于前台时生效:
- 捕获触控板双指上下滚动。
- 给它自动加上
Ctrl,让嘉立创 EDA 识别成拖动画布。 - 捕获触控板捏合手势。
- 把捏合手势伪造成普通滚轮事件,让嘉立创 EDA 继续执行放大缩小。
- 捏合过程中屏蔽触控板自己夹带的滚轮事件,避免又被误判成
Ctrl + 滚轮。
最终效果是:双指上下拖就是拖板子,双指捏合才是缩放。
安装 Hammerspoon
如果已经安装过 Hammerspoon,可以跳过这一步。
brew install --cask hammerspoon
第一次打开 Hammerspoon 时,需要在 macOS 里给它辅助功能权限:
系统设置 -> 隐私与安全性 -> 辅助功能 -> 打开 Hammerspoon。
如果后面脚本没反应,也可以检查一下:
系统设置 -> 隐私与安全性 -> 输入监控 -> 打开 Hammerspoon。
完整配置
打开 Hammerspoon 菜单栏图标,选择 Open Config,把下面这段粘贴到 ~/.hammerspoon/init.lua 里。
如果之前已经写过旧版嘉立创相关脚本,建议先删掉旧代码,再完整粘贴这一份。
-- LCEDA / EasyEDA Mac trackpad gesture swap
-- 双指上下滚动 = Ctrl + 滚轮 = 拖动画布
-- 双指捏合 = 伪造触控板上下滚动 = 放大缩小
local eventtap = hs.eventtap
local event = eventtap.event
local types = event.types
local props = event.properties
local TRACKPAD_ONLY = true
-- 捏合缩放力度:太慢就调大,比如 48 / 64 / 80;太快就调小
local ZOOM_STEP = 5
-- 每次捏合事件发几个滚轮包:一般保持 1,太慢再改成 2
local ZOOM_BURST = 1
-- 方向反了就改成 -1
local ZOOM_DIRECTION = 1
local PINCH_BLOCK_SECONDS = 0.12
local SHOW_DEBUG = false
local MAGIC = 0x1CEDA2026
local pinchUntil = 0
local lastDebugAt = 0
local function now()
return hs.timer.secondsSinceEpoch()
end
local function debug(msg)
if not SHOW_DEBUG then return end
local t = now()
if t - lastDebugAt > 0.45 then
lastDebugAt = t
hs.alert.show(msg, 0.2)
end
end
local function isLCEDA(app)
if not app then return false end
local name = app:name() or ""
local bundleID = app:bundleID() or ""
return name:find("嘉立创EDA", 1, true)
or name:find("LCEDA", 1, true)
or name:find("EasyEDA", 1, true)
or bundleID:lower():find("lceda", 1, true)
or bundleID:lower():find("easyeda", 1, true)
end
local function isTrackpadScroll(e)
if not TRACKPAD_ONLY then return true end
local continuous = e:getProperty(props.scrollWheelEventIsContinuous)
return continuous ~= nil and continuous ~= 0
end
local function axis(e, pointProp, lineProp)
local v = e:getProperty(pointProp)
if v == nil or v == 0 then
v = e:getProperty(lineProp)
end
return v or 0
end
local function isMagnify(e)
local t = e:getType(true)
return t == types.magnify or types[t] == "magnify"
end
if lcedaScrollFix then
lcedaScrollFix:stop()
lcedaScrollFix = nil
end
if lcedaPinchZoom then
lcedaPinchZoom:stop()
lcedaPinchZoom = nil
end
if lcedaPinchWatch then
lcedaPinchWatch:stop()
lcedaPinchWatch = nil
end
lcedaPinchZoom = eventtap.new({ types.gesture }, function(e)
if not isLCEDA(hs.application.frontmostApplication()) then
return false
end
if not isMagnify(e) then
return false
end
local details = e:getTouchDetails() or {}
local mag = details.magnification or 0
if math.abs(mag) < 0.000001 then
return true
end
pinchUntil = now() + PINCH_BLOCK_SECONDS
debug("pinch")
local sign = mag > 0 and 1 or -1
local amount = sign * ZOOM_STEP * ZOOM_DIRECTION
local out = {}
for i = 1, ZOOM_BURST do
local s = event.newScrollEvent({ 0, amount }, {}, "pixel")
s:setProperty(props.eventSourceUserData, MAGIC)
table.insert(out, s)
end
return true, out
end)
lcedaScrollFix = eventtap.new({ types.scrollWheel }, function(e)
if not isLCEDA(hs.application.frontmostApplication()) then
return false
end
-- 放行脚本自己造出来的缩放滚轮
if e:getProperty(props.eventSourceUserData) == MAGIC then
return false
end
local trackpad = isTrackpadScroll(e)
-- 捏合时吞掉触控板夹带的原始滚轮,防止又被加 Ctrl
if trackpad and now() < pinchUntil then
return true
end
local flags = e:getFlags()
if flags.ctrl or flags.shift or flags.alt or flags.cmd or flags.fn then
return false
end
if not trackpad then
return false
end
local dy = axis(e, props.scrollWheelEventPointDeltaAxis1, props.scrollWheelEventDeltaAxis1)
local dx = axis(e, props.scrollWheelEventPointDeltaAxis2, props.scrollWheelEventDeltaAxis2)
-- 左右双指滚动保持原样
if math.abs(dy) <= math.abs(dx) then
return false
end
-- 上下双指滚动改成 Ctrl + 滚轮,也就是拖动画布
e:setFlags({ ctrl = true })
return false
end)
lcedaPinchZoom:start()
lcedaScrollFix:start()
hs.alert.show("LCEDA trackpad swap loaded")
保存后点击 Hammerspoon 菜单栏图标,选择 Reload Config。
调参
如果捏合缩放比例太大,调小这两行:
local ZOOM_STEP = 5
local ZOOM_BURST = 1
如果捏合缩放太慢,调大 ZOOM_STEP:
local ZOOM_STEP = 48
local ZOOM_BURST = 1
如果缩放方向反了:
local ZOOM_DIRECTION = -1
如果想确认 Hammerspoon 有没有抓到捏合,可以临时打开调试提示:
local SHOW_DEBUG = true
捏合时如果右上角弹出 pinch,说明 Hammerspoon 已经捕获到捏合事件。
排错
如果 Ctrl + 滚动 被系统拿去做整屏缩放,需要关闭 macOS 的辅助功能缩放手势:
系统设置 -> 辅助功能 -> 缩放 -> 关闭“使用带修饰键的滚动手势来缩放”。
如果脚本完全没反应,优先检查:
- Hammerspoon 是否已经
Reload Config。 - Hammerspoon 是否有辅助功能权限。
- Hammerspoon 是否有输入监控权限。
- 嘉立创 EDA 的应用名或 bundle ID 是否包含
嘉立创EDA、LCEDA或EasyEDA。
如果应用名不匹配,可以临时运行下面这段查看当前前台应用信息:
hs.alert.show(
(hs.application.frontmostApplication():name() or "unknown")
.. "\n"
.. (hs.application.frontmostApplication():bundleID() or "unknown")
)
然后把输出的名称或 bundle ID 补进 isLCEDA 函数里。
最终效果
改完以后,嘉立创 EDA 在 Mac 触控板上的操作就接近正常画布软件了:
- 双指上下滚动:拖动画布。
- 双指左右滚动:左右移动。
- 双指捏合:放大缩小。
这个方案不改嘉立创 EDA 本身,只在 Hammerspoon 层对输入事件做转换,所以也方便随时关闭或继续微调。