Lua Script to Start, Unhide, and/or Auto-Arrange

I’m starting to try using HammerSpoon, which is driven by Lua, for automatically arranging my applications depending on application scenarios.

I have 3 screens, including a MacBook Pro at Retina native resolution and a Seiki 4K display. I grabbed the names of the monitors from Display Menu‘s menu bar menu.

I initially made the mistake of looking for “Outlook”, “OneNote”, and “Chrome”, but all three applications require their respective company’s names to be included in the application name.

https://en.wikipedia.org/wiki/Web_colors#X11_color_names
hs.streamdeck.init(function(connected, sd)
device = hs.streamdeck.getDevice(1)
device.setButtonColor("1", hs.drawing.color.x11.teal)
end)
applications = { "Messages", "iTunes", "Skype for Business", "iTerm2", "Microsoft Outlook", "HipChat", "Microsoft OneNote", "MacVim", "RubyMine", "Firefox" }
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "pageup", function()
for _k, app_name in pairs(applications) do
hs.application.launchOrFocus(app_name)
print(app_name)
app = hs.application.find(app_name)
if app then
hs.application.unhide(app)
end
end
local lgUltra="LG Ultra HD"
local windowLayout = {
upper right quadrant
{"Slack", nil, lgUltra, hs.geometry.rect(0,0,0.5,0.5), nil, nil},
upper left quadrant
{"Notes", nil, lgUltra, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil},
lower right quadrant
{"Skype for Business", nil, lgUltra, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil},
lower left quadrant
{"iTerm2", nil, lgUltra, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil}
}
hs.layout.apply(windowLayout)
local laptopScreen="Color LCD"
local windowLayout = {
{"Microsoft Outlook", nil, laptopScreen, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil},
{"HipChat", nil, laptopScreen, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil},
{"Microsoft OneNote", nil, laptopScreen, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil},
{"Notes", nil, laptopScreen, hs.geometry.rect(0,0,0.5,0.5), nil, nil}
}
hs.layout.apply(windowLayout)
local seiki4KScreen="SE39UY04"
local windowLayout = {
{"MacVim", nil, seiki4KScreen, hs.geometry.rect(0,0,0.5,0.5), nil, nil},
{"Emacs", nil, seiki4KScreen, hs.geometry.rect(0.5,0,0.5,0.5), nil, nil},
{"RubyMine-EAP", nil, seiki4KScreen, hs.geometry.rect(0,0.5,0.5,0.5), nil, nil},
{"iTerm2", nil, seiki4KScreen, hs.geometry.rect(0.5,0.5,0.5,0.5), nil, nil}
}
hs.layout.apply(windowLayout)
end)
Convert a lua table into a lua syntactically correct string
function table_to_string(tbl)
local result = "{"
for k, v in pairs(tbl) do
Check the key type (ignore any numerical keys – assume its an array)
if type(k) == "string" then
result = result.."[\""..k.."\"]".."="
end
Check the value type
if type(v) == "table" then
result = result..table_to_string(v)
elseif type(v) == "boolean" then
result = result..tostring(v)
else
result = result.."\""..v.."\""
end
result = result..","
end
Remove leading commas from the result
if result ~= "" then
result = result:sub(1, result:len()1)
end
return result.."}"
end
hs.hotkey.bind({}, "f13", function()
hs.location.start()
locationTable = hs.location.get()
hs.eventtap.keyStrokes(locationTable["latitude"] .. "," .. locationTable["longitude"])
print(table_to_string(locationTable))
end)
function move_to_third(r,c)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x + (extents.w / 3) * c
f.y = extents.y + (extents.h / 3) * r
f.w = extents.w / 3
f.h = extents.h / 3
win:setFrame(f)
end
function move_to_two_third_horz(c)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x + (extents.w / 3) * c
f.y = extents.y
f.w = extents.w / 3 * 2
f.h = extents.h
win:setFrame(f)
end
function move_to_two_third_vert(r)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x
f.y = extents.y + (extents.h / 3) * r
f.w = extents.w
f.h = extents.h / 3 * 2
win:setFrame(f)
end
function move_to_four_ninths(r,c)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x + (extents.w / 3) * c
f.y = extents.y + (extents.h / 3) * r
f.w = extents.w / 3 * 2
f.h = extents.h / 3 * 2
win:setFrame(f)
end
function move_to_fourth(r,c)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x + (extents.w / 4) * c
f.y = extents.y + (extents.h / 4) * r
f.w = extents.w / 4
f.h = extents.h / 4
win:setFrame(f)
end
for i=0,15 do
hs.hotkey.bind({"cmd", "alt", "ctrl"}, string.format("%x", i), function()
move_to_fourth(math.floor(i/4),i%4)
end)
end
for i=1,9 do
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "pad"..tostring(i), function()
move_to_third(math.floor((9i)/3),(i1)%3)
end)
end
for i=1,9 do
hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "pad"..tostring(i), function()
if i == 1 then move_to_four_ninths(1,0)
elseif i == 3 then move_to_four_ninths(1,1)
elseif i == 7 then move_to_four_ninths(0,0)
elseif i == 9 then move_to_four_ninths(0,1)
elseif i == 2 then move_to_two_third_vert(1)
elseif i == 4 then move_to_two_third_horz(0)
elseif i == 6 then move_to_two_third_horz(1)
elseif i == 8 then move_to_two_third_vert(0)
end
end)
end
https://www.hammerspoon.org/docs/hs.application.watcher.html
https://www.lua.org/pil/9.1.html
https://stackoverflow.com/questions/16984540/how-to-pass-a-function-as-a-parameter-in-lua
hs.application.watcher.new()
TODO Plan:
Create application watcher
currentApp = ""
function applicationWatcher(appName, eventType, appObject)
if(eventType == hs.application.watcher.activated) then
if (appName == "HipChat") then
currentApp = "HipChat"
end
if (appName == "Google Chrome") then
currentApp = "Chrome"
end
end
end
function alertCurrent()
hs.alert(currentApp)
end
hs.timer.doEvery(5, alertCurrent)

view raw
init.lua
hosted with ❤ by GitHub


Hammerspoon Lua Script – Split Windows 3×3

I’ve been using using Spectacle pretty heavily, but with a larger screen, I always felt like quadrants were a poor use of real estate, so I began to crack open the source code, which was pretty challenging to navigate because it’s a mix of JavaScript and Objective-C reminiscent of user interface programming from ages past. So, I turned next to Hammerspoon.

Hammerspoon¬†uses Lua as a programming interface. I was able to automate moving windows around a 3×3 grid on the current screen with 18 lines of code.

function move_to_third(r,c)
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local extents = screen:frame()
f.x = extents.x + (extents.w / 3) * c
f.y = extents.y + (extents.h / 3) * r
f.w = extents.w / 3
f.h = extents.h / 3
win:setFrame(f)
end
for i=1,9 do
hs.hotkey.bind({"cmd", "alt", "ctrl"}, tostring(i), function()
move_to_third(math.floor((i1)/3),(i1)%3)
end)
end

view raw
init.lua
hosted with ❤ by GitHub