parent
41bc4d6be1
commit
7c90a58c36
@ -1,151 +0,0 @@
|
|||||||
--- === Quitter ===
|
|
||||||
--
|
|
||||||
-- Quits 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 <alpha@kejadlen.dev>"
|
|
||||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
|
||||||
|
|
||||||
obj.logger = hs.logger.new("quitter", "warning")
|
|
||||||
obj.lastFocused = {}
|
|
||||||
obj.state = "stopped"
|
|
||||||
|
|
||||||
--- Quitter.quitAppsAfter
|
|
||||||
--- Variable
|
|
||||||
--- Table of applications to quit when unused.
|
|
||||||
obj.quitAppsAfter = {}
|
|
||||||
|
|
||||||
--- Quitter:start()
|
|
||||||
--- Method
|
|
||||||
--- Start Quitter
|
|
||||||
---
|
|
||||||
--- Parameters:
|
|
||||||
--- * None
|
|
||||||
function obj:start()
|
|
||||||
if self.state == "started" then return end
|
|
||||||
|
|
||||||
self:reset()
|
|
||||||
|
|
||||||
-- Reset if we're waking from sleep
|
|
||||||
self.watcher = 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
|
|
||||||
self.windowFilter = hs.window.filter.new(false)
|
|
||||||
for app, _ in pairs(self.quitAppsAfter) do
|
|
||||||
self.windowFilter:allowApp(app)
|
|
||||||
end
|
|
||||||
self.windowFilter:subscribe(
|
|
||||||
{hs.window.filter.windowFocused, hs.window.filter.windowUnfocused},
|
|
||||||
function(window, appName)
|
|
||||||
local name = window:application():name()
|
|
||||||
self.lastFocused[name] = os.time()
|
|
||||||
end)
|
|
||||||
|
|
||||||
self.timer = hs.timer.doEvery(60, function()
|
|
||||||
self:reap()
|
|
||||||
end):start()
|
|
||||||
|
|
||||||
self.state = "started"
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Quitter:stop()
|
|
||||||
--- Method
|
|
||||||
--- Stop Quitter
|
|
||||||
---
|
|
||||||
--- Parameters:
|
|
||||||
--- * None
|
|
||||||
function obj:stop()
|
|
||||||
if self.state == "stopped" then return end
|
|
||||||
|
|
||||||
self.watcher:stop()
|
|
||||||
self.windowFilter:unsubscribeAll()
|
|
||||||
self.timer:stop()
|
|
||||||
|
|
||||||
self.state = "stopped"
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Quitter:toggle()
|
|
||||||
--- Method
|
|
||||||
--- Toggle Quitter
|
|
||||||
---
|
|
||||||
--- Parameters:
|
|
||||||
--- * None
|
|
||||||
function obj:toggle()
|
|
||||||
if self.state == "started" then
|
|
||||||
self:stop()
|
|
||||||
else
|
|
||||||
self:start()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function obj:reset()
|
|
||||||
hs.fnutils.ieach(hs.application.runningApplications(), function(app)
|
|
||||||
local name = app:name()
|
|
||||||
|
|
||||||
local duration = self.quitAppsAfter[name]
|
|
||||||
if duration == nil then return false end
|
|
||||||
|
|
||||||
self.lastFocused[name] = os.time()
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
function obj:shouldQuit(app)
|
|
||||||
if app == nil then return false end
|
|
||||||
|
|
||||||
-- Don't quit the currently focused app
|
|
||||||
if app:isFrontmost() then return false end
|
|
||||||
|
|
||||||
local duration = self.quitAppsAfter[app:name()]
|
|
||||||
if duration == nil then return false end
|
|
||||||
|
|
||||||
local lastFocused = self.lastFocused[app:name()]
|
|
||||||
if lastFocused == nil then return false end
|
|
||||||
|
|
||||||
self.logger.d("app: " .. app:name() .. " last focused at " .. lastFocused)
|
|
||||||
|
|
||||||
return (os.time() - lastFocused) > duration
|
|
||||||
end
|
|
||||||
|
|
||||||
function obj:reap()
|
|
||||||
self.logger.d("reaping")
|
|
||||||
|
|
||||||
local frontmostApp = hs.application.frontmostApplication()
|
|
||||||
for appName, _ in pairs(self.quitAppsAfter) do
|
|
||||||
local app = hs.application.get(appName)
|
|
||||||
|
|
||||||
if self:shouldQuit(app) then
|
|
||||||
hs.notify.new(
|
|
||||||
function() hs.application.open(app:name()) end,
|
|
||||||
{
|
|
||||||
title = "Hammerspoon",
|
|
||||||
informativeText = "Quitting " .. app:name(),
|
|
||||||
withdrawAfter = 2,
|
|
||||||
}
|
|
||||||
):send()
|
|
||||||
app:kill()
|
|
||||||
self.lastFocused[app:name()] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function obj:debug()
|
|
||||||
local lastFocused = hs.fnutils.map(
|
|
||||||
spoon.Quitter.lastFocused,
|
|
||||||
function(t)
|
|
||||||
return os.date("!%Y-%m-%dT%TZ", t)
|
|
||||||
end)
|
|
||||||
print(hs.inspect(lastFocused))
|
|
||||||
end
|
|
||||||
|
|
||||||
return obj
|
|
@ -0,0 +1,51 @@
|
|||||||
|
(local {: application : caffeinate : logger : timer : window} hs)
|
||||||
|
|
||||||
|
(local log (logger.new :quitter :info))
|
||||||
|
|
||||||
|
(local to-kill {})
|
||||||
|
|
||||||
|
(fn kill [app]
|
||||||
|
(when (not (app:isFrontmost))
|
||||||
|
(log.i (.. "killing " (app:name)))
|
||||||
|
(app:kill)))
|
||||||
|
|
||||||
|
(fn mark [app]
|
||||||
|
(when (= (app:kind) 1)
|
||||||
|
(log.i (.. "marking " (app:name)))
|
||||||
|
(set (. to-kill (app:bundleID)) (timer.doAfter 300 #(kill app)))))
|
||||||
|
|
||||||
|
(fn unmark [win]
|
||||||
|
(let [app (win:application)
|
||||||
|
bundle-id (app:bundleID)
|
||||||
|
t (?. to-kill bundle-id)]
|
||||||
|
(when (not= t nil)
|
||||||
|
(log.i (.. "unmarking " (app:name)))
|
||||||
|
(t:stop)
|
||||||
|
(set (. to-kill bundle-id) nil))))
|
||||||
|
|
||||||
|
(fn on-wake [event]
|
||||||
|
(when (= event caffeinate.watcher.systemDidWake)
|
||||||
|
(each [_ app (ipairs (application.find ""))]
|
||||||
|
(mark app))))
|
||||||
|
|
||||||
|
(local cw (caffeinate.watcher.new on-wake))
|
||||||
|
|
||||||
|
(local wf (window.filter.new {:Safari false
|
||||||
|
:Arc false
|
||||||
|
"Firefox Developer Edition" false
|
||||||
|
:Ghostty false
|
||||||
|
:Miniflux false
|
||||||
|
:Phanpy false
|
||||||
|
:Obsidian false
|
||||||
|
:default true}))
|
||||||
|
|
||||||
|
(fn start []
|
||||||
|
(log.i :starting)
|
||||||
|
(cw:start)
|
||||||
|
(wf:subscribe {window.filter.windowFocused unmark
|
||||||
|
window.filter.windowUnfocused #(mark ($1:application))}))
|
||||||
|
|
||||||
|
;; use global so this isn't GC'ed
|
||||||
|
(set _G.quitter {: to-kill})
|
||||||
|
|
||||||
|
{: start}
|
Loading…
Reference in new issue