I'm having a setup where I need to run a couple of scripts when dark mode is activated. Should we add another watcher for this event?
Related: https://medium.com/@ruiaureliano/check-light-dark-appearance-for-macos-mojave-catalina-fb2343af875f
In the meantime, the following rough script might work:
function appearanceIsDark()
asSuccess, asObj, asDesc = hs.osascript.applescript("tell application \"System Events\" to tell appearance preferences to return dark mode")
return asObj
end
lastUIAppearance = appearanceIsDark()
hs.timer.doEvery(1, function()
currentUIAppearance = appearanceIsDark()
if currentUIAppearance ~= lastUIAppearance then
if currentUIAppearance then
-- dark scripts
else
-- light scripts
end
end
lastUIAppearance = currentUIAppearance
end)
P.S. This can also be done via hs.distributednotifications using 'AppleInterfaceThemeChangedNotification'. Not sure if this is more efficient than my previous suggestion with hs.timer or not, but it seems like it would be:
function appearanceIsDark()
asSuccess, asObj, asDesc = hs.osascript.applescript("tell application \"System Events\" to tell appearance preferences to return dark mode")
return asObj
end
lastUIAppearance = appearanceIsDark()
hs.distributednotifications.new(function(name, object, userInfo)
currentUIAppearance = appearanceIsDark()
if currentUIAppearance ~= lastUIAppearance then
if currentUIAppearance then
-- dark scripts
else
-- light scripts
end
end
lastUIAppearance = currentUIAppearance
end, 'AppleInterfaceThemeChangedNotification'):start()
P.S. This can also be done via
hs.distributednotificationsusing 'AppleInterfaceThemeChangedNotification'. Not sure if this is more efficient than my previous suggestion withhs.timeror not, but it _seems_ like it would be:
Yes it should be more efficient, since it does not get fired every second. Thanks for the nice solution. This could be implemented as an official module so this should be only fired once from the system and lua manages all callbacks.
I created a small library for darkmode detection and event handling.
darkmode.lua
-- --------------------------------------------------------------------------
--
-- Implementation of a dark mode library for detecting and setting dark
-- mode in MacOS.
--
-- --------------------------------------------------------------------------
--
-- Example:
--
-- dm = require "darkmode"
-- dm.addHandler(function(dm2) print('darkmode changed:',dm2) end)
-- print('darkmode:',dm.getDarkMode())
-- dm.setDarkMode(not dm.getDarkMode())
-- print('darkmode:',dm.getDarkMode())
-- dm.setDarkMode(not dm.getDarkMode())
--
-- --------------------------------------------------------------------------
-- --------------------------------------------------------------------------
-- internal Data which should not be garbage collected
-- --------------------------------------------------------------------------
local internalData = {
darkmode = false,
watcher = nil,
handler = {}
}
-- --------------------------------------------------------------------------
-- General functions
-- --------------------------------------------------------------------------
local function getDarkModeFromSystem()
-- local _, darkmode = hs.osascript.applescript('tell application "System Events"\nreturn dark mode of appearance preferences\nend tell')
local _, darkmode = hs.osascript.javascript("Application('System Events').appearancePreferences.darkMode.get()")
return darkmode
end
local function getDarkMode()
return internalData.darkmode
end
local function setDarkMode(state)
hs.osascript.javascript(string.format("Application('System Events').appearancePreferences.darkMode.set(%s)",state))
end
local function addHandler(fn)
-- add it here...
internalData.handler[#internalData.handler+1] = fn
end
-- --------------------------------------------------------------------------
-- Internal functions
-- --------------------------------------------------------------------------
local function initialize()
internalData.darkmode = getDarkModeFromSystem()
end
local function initializeWatcher()
-- exit if already watching
if internalData.watcher ~= nil then return end
internalData.watcher = hs.distributednotifications.new(function(name, object, userInfo)
local hasDarkMode = getDarkModeFromSystem()
if hasDarkMode ~= internalData.darkmode then
internalData.darkmode = hasDarkMode
-- execute each handler with the darkmode as parameter (may change in future)
for index, fn in ipairs(internalData.handler) do
fn(hasDarkMode)
end
end
end,'AppleInterfaceThemeChangedNotification')
internalData.watcher:start()
end
-- --------------------------------------------------------------------------
-- Initialization
-- --------------------------------------------------------------------------
initialize()
initializeWatcher()
local module = {
_ = internalData,
setDarkMode = setDarkMode,
getDarkMode = getDarkMode,
addHandler = addHandler
}
return module
A note for anyone following rsefer's example in https://github.com/Hammerspoon/hammerspoon/issues/2386#issuecomment-643715994: I believe that you need to store the result of hs.distributednotifications.new in a variable, or else it will be garbage collected and you will stop getting notifications. Signed, someone who keeps forgetting about this in Lua/Hammerspoon.
A note for anyone following rsefer's example in #2386 (comment): I believe that you need to store the result of
hs.distributednotifications.newin a variable, or else it will be garbage collected and you will stop getting notifications. Signed, someone who _keeps forgetting_ about this in Lua/Hammerspoon.
Yes, you are right. I think too it will be garbage collected in rsefer's source. This is why i saved it in an internalData variable in the module i posted. ;)
@dsedivec Good catch! Thanks for finding that
Most helpful comment
P.S. This can also be done via
hs.distributednotificationsusing 'AppleInterfaceThemeChangedNotification'. Not sure if this is more efficient than my previous suggestion withhs.timeror not, but it seems like it would be: