Skip to content

Instantly share code, notes, and snippets.

@TwoLeaves
Last active March 7, 2022 18:00
Show Gist options
  • Save TwoLeaves/a9d226ac98be5109a226 to your computer and use it in GitHub Desktop.
Save TwoLeaves/a9d226ac98be5109a226 to your computer and use it in GitHub Desktop.
Hammerspoon config
--------------------------------------------------------------------------------
-- Unsupported Spaces extension. Uses private APIs but works okay.
-- (http://github.com/asmagill/hammerspoon_asm.undocumented)
spaces = require("hs._asm.undocumented.spaces")
-- Get output of a bash command
function os.capture(cmd)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
-- Update the fan and temp. Needs iStats CLI tool from homebrew.
local function updateStats()
fanSpeed = os.capture("iStats fan speed | cut -c14- | sed 's/\\..*//'")
temp = os.capture("iStats cpu temp | cut -c11- | sed 's/\\..*//'")
end
-- Makes (and updates) the topbar menu filled with the current Space, the
-- temperature and the fan speed. The Space only updates if the space is changed
-- with the Hammerspoon shortcut (option + arrows does not work).
local function makeStatsMenu(calledFromWhere)
if statsMenu == nil then
statsMenu = hs.menubar.new()
end
if calledFromWhere == "spaceChange" then
currentSpace = tostring(spaces.currentSpace())
else
updateStats()
end
statsMenu:setTitle("Space " .. currentSpace .. " | Fan: " .. fanSpeed .. " | Temp: " .. temp)
end
-- Gets a list of windows and iterates until the window title is non-empty.
-- This avoids focusing the hidden windows apparently placed on top of all
-- Google Chrome windows. It also checks if the empty title belongs to Chrome,
-- because some apps don't give any of their windows a title, and should still
-- be focused.
local function spaceChange()
makeStatsMenu("spaceChange")
visibleWindows = hs.window.orderedWindows()
for i, window in ipairs(visibleWindows) do
if window:application():title() == "Google Chrome" then
if window:title() ~= "" then
window:focus()
break
end
else
window:focus()
break
end
end
end
--------------------------------------------------------------------------------
-- Options
-- How often to update Fan and Temp
updateStatsInterval = 20
statsMenuTimer = hs.timer.new(updateStatsInterval, makeStatsMenu)
statsMenuTimer:start()
-- instant window resizing
hs.window.animationDuration = 0
local mash = {"cmd", "alt", "ctrl"}
local mashshift = {"cmd", "alt", "ctrl", "shift"}
--------------------------------------------------------------------------------
-- Applications launch
local function newChromeWindow()
os.execute("/Applications/'Google Chrome.app'/Contents/MacOS/'Google Chrome' --new-window")
visibleWindows = hs.window.orderedWindows()
visibleWindows[2]:focus()
end
hs.hotkey.bind(mash, "c", function()
newChromeWindow()
end)
--------------------------------------------------------------------------------
-- snap windows to edges
-- (and make them half-width or half-height)
hs.hotkey.bind(mash, "y", function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.x = max.x
f.y = max.y
f.w = max.w / 2
f.h = max.h
win:setFrame(f)
end)
hs.hotkey.bind(mash, "u", function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.x = max.x
f.y = max.y + (max.h / 2)
f.w = max.w
f.h = max.h / 2
win:setFrame(f)
end)
hs.hotkey.bind(mash, "i", function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.x = max.x
f.y = max.y
f.w = max.w
f.h = max.h / 2
win:setFrame(f)
end)
hs.hotkey.bind(mash, "o", function()
local win = hs.window.focusedWindow()
local f = win:frame()
local screen = win:screen()
local max = screen:frame()
f.x = max.x + (max.w / 2)
f.y = max.y
f.w = max.w / 2
f.h = max.h
win:setFrame(f)
end)
--------------------------------------------------------------------------------
-- Manual window moving and resizing
-- Credit to GitHub user: ztomer
hs.grid.MARGINX = 0
hs.grid.MARGINY = 0
hs.grid.GRIDHEIGHT = 18
hs.grid.GRIDWIDTH = 18
--Alter gridsize
hs.hotkey.bind(mashshift, '=', function() hs.grid.adjustHeight( 1) end)
hs.hotkey.bind(mashshift, '-', function() hs.grid.adjustHeight(-1) end)
hs.hotkey.bind(mash, '=', function() hs.grid.adjustWidth( 1) end)
hs.hotkey.bind(mash, '-', function() hs.grid.adjustWidth(-1) end)
--Snap windows
hs.hotkey.bind(mash, ';', function() hs.grid.snap(hs.window.focusedWindow()) end)
hs.hotkey.bind(mash, "'", function() hs.fnutils.map(hs.window.visibleWindows(), hs.grid.snap) end)
--Move windows
hs.hotkey.bind(mash, 'j', hs.grid.pushWindowDown)
hs.hotkey.bind(mash, 'k', hs.grid.pushWindowUp)
hs.hotkey.bind(mash, 'h', hs.grid.pushWindowLeft)
hs.hotkey.bind(mash, 'l', hs.grid.pushWindowRight)
--resize windows
hs.hotkey.bind(mashshift, 'k', hs.grid.resizeWindowShorter)
hs.hotkey.bind(mashshift, 'j', hs.grid.resizeWindowTaller)
hs.hotkey.bind(mashshift, 'l', hs.grid.resizeWindowWider)
hs.hotkey.bind(mashshift, 'h', hs.grid.resizeWindowThinner)
-- toggle window zoom (acts like Alt+Shift+GreenPlusButton)
hs.hotkey.bind(mash, "m", function()
local win = hs.window.focusedWindow()
local frame = win:frame()
local id = win:id()
-- init table to save window state
savedwin = savedwin or {}
savedwin[id] = savedwin[id] or {}
if (savedwin[id].maximized == nil or savedwin[id].maximized == false) then
savedwin[id].frame = frame
savedwin[id].maximized = true
win:maximize()
else
savedwin[id].maximized = false
win:setFrame(savedwin[id].frame)
savedwin[id] = nil
end
end)
--------------------------------------------------------------------------------
-- switch Spaces
hs.hotkey.bind(mash, '1', function()
spaces.moveToSpace("1")
spaceChange()
end)
hs.hotkey.bind(mash, '2', function()
spaces.moveToSpace("2")
spaceChange()
end)
hs.hotkey.bind(mash, '3', function()
spaces.moveToSpace("3")
spaceChange()
end)
hs.hotkey.bind(mash, '4', function()
spaces.moveToSpace("4")
spaceChange()
end)
hs.hotkey.bind(mash, '5', function()
spaces.moveToSpace("5")
spaceChange()
end)
hs.hotkey.bind(mash, '6', function()
spaces.moveToSpace("6")
spaceChange()
end)
--------------------------------------------------------------------------------
-- Initialize
currentSpace = tostring(spaces.currentSpace())
updateStats()
makeStatsMenu()
@catskull
Copy link

catskull commented Aug 3, 2021

Thanks for this! I've split out the CPU temp display into a Spoon here: https://github.com/catskull/CPUTempMenuDisplay.spoon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment