概览
ashfinal/awesome-hammerspoon: awesome configuration for Hammerspoon. 开源,有点厉害,初步看着像是提供了整套操纵mac桌面的Lua API. 基本上是等同于alfred的效果了吧?
参照ashfinal/awesome-hammerspoon: awesome configuration for Hammerspoon.部署, 然后修改~/.hammerspoon/init.lua
即可
功能扩展
剪切板历史
Hammerspoon docs: TextClipboardHistory
注册快捷键"alt+"v"
1 | hs.loadSpoon('TextClipboardHistory') |
Hammerspoon docs: TextClipboardHistory
hsearch: 类似alfred
实现类似alfred的自定义能力.
疑似卡顿
Hammerspoon “Freezes” After a While · Issue #2219 · Hammerspoon/hammerspoon 但是hammerspoon的Choooser好像实在是太卡了, hidden和显示都会转圈圈.
提了个issue,看看官方大佬有没有人能够解决, 我试了下我编译老是报错,感觉像是objective-c里还有什么依赖不在那个cocoa里的.
TODO:后来没怎么用这个功能, 后续遇到再定位看看
resizeM: 平铺窗口管理器
实现将当前转换快速修改窗口大小以及在桌面的平铺位置. 默认用的awesome-hammerspoon
里的
Option+R
进入hammerspoon模式, 通过方向键右切换到左右屏幕, 然后通过F来进行全屏化.
另一种实现方式
找的大佬的脚本1 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152-- Window management
-- Defines for window maximize toggler
local frameCache = {}
local logger = hs.logger.new("windows")
-- Resize current window
function winresize(how)
local win = hs.window.focusedWindow()
local app = win:application():name()
local windowLayout
local newrect
if how == "left" then
newrect = hs.layout.left50
elseif how == "right" then
newrect = hs.layout.right50
elseif how == "up" then
newrect = {0,0,1,0.5}
elseif how == "down" then
newrect = {0,0.5,1,0.5}
elseif how == "max" then
newrect = hs.layout.maximized
elseif how == "left_third" or how == "hthird-0" then
newrect = {0,0,1/3,1}
elseif how == "middle_third_h" or how == "hthird-1" then
newrect = {1/3,0,1/3,1}
elseif how == "right_third" or how == "hthird-2" then
newrect = {2/3,0,1/3,1}
elseif how == "top_third" or how == "vthird-0" then
newrect = {0,0,1,1/3}
elseif how == "middle_third_v" or how == "vthird-1" then
newrect = {0,1/3,1,1/3}
elseif how == "bottom_third" or how == "vthird-2" then
newrect = {0,2/3,1,1/3}
end
win:move(newrect)
end
function winmovescreen(how)
local win = hs.window.focusedWindow()
if how == "left" then
win:moveOneScreenWest()
elseif how == "right" then
win:moveOneScreenEast()
end
end
-- Toggle a window between its normal size, and being maximized
function toggle_window_maximized()
local win = hs.window.focusedWindow()
if frameCache[win:id()] then
win:setFrame(frameCache[win:id()])
frameCache[win:id()] = nil
else
frameCache[win:id()] = win:frame()
win:maximize()
end
end
-- Move between thirds of the screen
function get_horizontal_third(win)
local frame=win:frame()
local screenframe=win:screen():frame()
local relframe=hs.geometry(frame.x-screenframe.x, frame.y-screenframe.y, frame.w, frame.h)
local third = math.floor(3.01*relframe.x/screenframe.w)
logger.df("Screen frame: %s", screenframe)
logger.df("Window frame: %s, relframe %s is in horizontal third #%d", frame, relframe, third)
return third
end
function get_vertical_third(win)
local frame=win:frame()
local screenframe=win:screen():frame()
local relframe=hs.geometry(frame.x-screenframe.x, frame.y-screenframe.y, frame.w, frame.h)
local third = math.floor(3.01*relframe.y/screenframe.h)
logger.df("Screen frame: %s", screenframe)
logger.df("Window frame: %s, relframe %s is in vertical third #%d", frame, relframe, third)
return third
end
function left_third()
local win = hs.window.focusedWindow()
local third = get_horizontal_third(win)
if third == 0 then
winresize("hthird-0")
else
winresize("hthird-" .. (third-1))
end
end
function right_third()
local win = hs.window.focusedWindow()
local third = get_horizontal_third(win)
if third == 2 then
winresize("hthird-2")
else
winresize("hthird-" .. (third+1))
end
end
function up_third()
local win = hs.window.focusedWindow()
local third = get_vertical_third(win)
if third == 0 then
winresize("vthird-0")
else
winresize("vthird-" .. (third-1))
end
end
function down_third()
local win = hs.window.focusedWindow()
local third = get_vertical_third(win)
if third == 2 then
winresize("vthird-2")
else
winresize("vthird-" .. (third+1))
end
end
function center()
local win = hs.window.focusedWindow()
win:centerOnScreen()
end
-------- Key bindings
-- Halves of the screen
hs.hotkey.bind({"ctrl","cmd"}, "Left", hs.fnutils.partial(winresize, "left"))
hs.hotkey.bind({"ctrl","cmd"}, "Right", hs.fnutils.partial(winresize, "right"))
hs.hotkey.bind({"ctrl","cmd"}, "Up", hs.fnutils.partial(winresize, "up"))
hs.hotkey.bind({"ctrl","cmd"}, "Down", hs.fnutils.partial(winresize, "down"))
-- Center of the screen
hs.hotkey.bind({"ctrl", "cmd"}, "C", center)
-- Thirds of the screen
hs.hotkey.bind({"ctrl", "alt"}, "Left", left_third)
hs.hotkey.bind({"ctrl", "alt"}, "Right", right_third)
hs.hotkey.bind({"ctrl", "alt"}, "Up", up_third)
hs.hotkey.bind({"ctrl", "alt"}, "Down", down_third)
-- Maximized
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "F", hs.fnutils.partial(winresize, "max"))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Up", hs.fnutils.partial(winresize, "max"))
-- Move between screens
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Left", hs.fnutils.partial(winmovescreen, "left"))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "Right", hs.fnutils.partial(winmovescreen, "right"))
hints: 窗口切换器
实现根据前缀快速切换到指定的窗口.
这里显示的图标位置按照窗口的左上角索引位置排列, 所以只要能记住窗口的位置, 就选择对应的前缀输入就可以完成切换了2
windowhints 经常卡顿几秒才弹出切换的窗口和对应的按键
根据WindowHints Performance · Issue #233 · Hammerspoon/hammerspoon这篇来看, 这个问题早早已经存在, 且主要受限于各自电脑装上的某些会阻塞的程序.
根据下面的代码, 可以发现关键点是windows = windows or window.allWindows()
这段的用时长.
1 | --- hs.hints.windowHints([windows, callback, allowNonStandard]) |
在opt+z
呼出的窗口中输入, 获取到自己环境中导致耗时长的程序
1 | > hs.window._timed_allWindows() |
根据下文这段里的, 可以发现allWindows默认已经通过SKIP_APPS
跳过了一些程序.
1 | local SKIP_APPS={ |
理论上使用hs.window.filter
可能有帮助?
最后短时间没搞明白filter
的注入到window.allWindows
里的防范, 先手动在/Applications/Hammerspoon.app/Contents/Resources/extensions/hs/window.lua
底下的SKIP_APPS
直接增加了自己环境里耗时多的程序, 跳过后基本呼出alt+tab
的延时正常了.34
根据这句来判断, hammerspoon在关闭SIP之后还是会存在这个问题的.
1 | local SKIP_APPS={ |
快捷方式打开指定目录下最新的文件(如截图)
1 | hs.hotkey.bind("alt", "o", function() |
TODO:os.execute会自动补全路径?
1 | > os.execute("open \"~/Desktop/temp.sh\"") |
快速切换显示器信号源, 基于DDC工具
增加了shift+F11
的快速切换信号源的方案.
1 | hs.hotkey.bind("shift", "f11", function() |
日志调试方式
How to debug Hammerspoon scripts? · Issue #989 · Hammerspoon/hammerspoon
[pkulchenko/MobDebug: Remote debugger for Lua.](https://github.com/pkulchenko/MobDebug
根据上述链接, 应该可以用lua的调试方式来操作, mobdebug.lua
日志
1 | logger = hs.logger.new('preview snap') |
Reference
Switching between windows with Alt + Tab · Issue #856 · Hammerspoon/hammerspoon↩︎
Hammerspoon get sluggish with vscode running · Issue #2289 · Hammerspoon/hammerspoon↩︎
windowHints are slow · Issue #2970 · Hammerspoon/hammerspoon > Yabai is capable of doing what you want using only the window id, but that solution requires disabling SIP and injecting code into Dock.app.↩︎