-------------------------------------------------------------------------------- -- 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()