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