Last active
March 7, 2022 18:00
-
-
Save TwoLeaves/a9d226ac98be5109a226 to your computer and use it in GitHub Desktop.
Hammerspoon config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-------------------------------------------------------------------------------- | |
-- 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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this! I've split out the CPU temp display into a Spoon here: https://github.com/catskull/CPUTempMenuDisplay.spoon