diff --git a/.hammerspoon/Spoons/Quitter.spoon/init.lua b/.hammerspoon/Spoons/Quitter.spoon/init.lua new file mode 100644 index 0000000..3e31d69 --- /dev/null +++ b/.hammerspoon/Spoons/Quitter.spoon/init.lua @@ -0,0 +1,93 @@ +--- === Quitter === +-- +-- Quits configured apps if they have not been used since a specified amount of time. + +local obj = {} +obj.__index = obj + +-- Metadata + +obj.name = "Quitter" +obj.version = "0.1" +obj.author = "Alpha Chen " +obj.license = "MIT - https://opensource.org/licenses/MIT" + +obj.logger = hs.logger.new("quitter", "debug") +obj.lastFocused = {} + +--- Quitter.quitAppsAfter +--- Variable +--- Table of application bundle IDs to quit when unused. Use `mdls -name +--- --kMDItemCFBundleIdentifier -r /path/to/app` to find unknown bundle IDs. + +obj.quitAppsAfter = {} + +--- Quitter:start() +--- Method +--- Start Quitter +--- +--- Parameters: +--- * None +function obj:start() + self:reset() + + -- Reset if we're waking from sleep + hs.caffeinate.watcher.new(function(event) + if event ~= hs.caffeinate.watcher.systemDidWake then return end + self:reset() + end) + + -- Set last focused time for relevant apps + hs.window.filter.default:subscribe(hs.window.filter.windowFocused, function(window, appName) + local bundleID = window:application():bundleID() + if not self.quitAppsAfter[bundleID] then return end + self.lastFocused[bundleID] = os.time() + end) + + self.timer = hs.timer.doEvery(60, function() + self:reap() + end):start() + + return self +end + +function obj:reset() + hs.fnutils.ieach(hs.application.runningApplications(), function(app) + local bundleID = app:bundleID() + + local duration = self.quitAppsAfter[bundleID] + if not duration then return false end + + self.lastFocused[bundleID] = os.time() + end) +end + +function obj:reap() + self.logger.d("reaping") + local appsToQuit = hs.fnutils.ifilter(hs.application.runningApplications(), function(app) + -- Don't quit the currently focused app + if app:isFrontmost() then return false end + + local duration = self.quitAppsAfter[app:bundleID()] + if not duration then return false end + + local lastFocused = self.lastFocused[app:bundleID()] + if not lastFocused then return false end + + self.logger.d("app: " .. app:name() .. " last focused at " .. lastFocused) + + return (os.time() - lastFocused) > duration + end) + + for _,app in pairs(appsToQuit) do + hs.notify.new({ + title = "Hammerspoon", + informativeText = "Quitting " .. app:name(), + withdrawAfter = 2, + }):send() + app:kill() + self.lastFocused[app:bundleID()] = nil + end +end + +return obj diff --git a/.hammerspoon/init.lua b/.hammerspoon/init.lua index e9d4dce..6af5a08 100644 --- a/.hammerspoon/init.lua +++ b/.hammerspoon/init.lua @@ -1,7 +1,8 @@ local mash = {"cmd", "alt", "ctrl"} local smash = {"cmd", "alt", "ctrl", "shift"} --- Window management +-- Window management -- + wm = { left = hs.layout.left50, right = hs.layout.right50, @@ -22,7 +23,33 @@ hs.hotkey.bind(mash, "h", function() move("left") end) hs.hotkey.bind(mash, "l", function() move("right") end) hs.hotkey.bind(mash, "m", function() move("max") end) +-- Defeat paste blocking -- + +hs.hotkey.bind({"cmd", "alt"}, "v", function() hs.eventtap.keyStrokes(hs.pasteboard.getContents()) end) + +-- Debugging -- + +-- hs.hotkey.bind(mash, "d", function() +-- end) + +-- Spoons -- + +hs.loadSpoon("Quitter") +spoon.Quitter.quitAppsAfter = { + -- mdls -name kMDItemCFBundleIdentifier -r /path/to/app + ["com.apple.iChat"] = 600, + ["com.freron.MailMate"] = 600, + ["com.kapeli.dashdoc"] = 600, + ["com.reederapp.macOS"] = 600, + ["com.tinyspeck.slackmacgap"] = 600, +} +spoon.Quitter:start() + hs.loadSpoon("ReloadConfiguration") spoon.ReloadConfiguration:start() -hs.alert.show("Config loaded") +hs.notify.new({ + title = "Hammerspoon", + informativeText = "Config loaded", + withdrawAfter = 2, +}):send()