Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lua/auto-dark-mode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ M.state = {
system = nil,
---@type table
query_command = {},
---@type table
monitor_command = {},
}

---@return nil
Expand Down Expand Up @@ -92,6 +94,14 @@ M.init = function()
"string:org.freedesktop.appearance",
"string:color-scheme",
}

if vim.fn.executable("dbus-monitor") ~= 0 then
M.state.monitor_command = {
"dbus-monitor",
"--session",
"type=signal,interface=org.freedesktop.portal.Settings,member=SettingChanged,path=/org/freedesktop/portal/desktop,arg0='org.freedesktop.appearance',arg1='color-scheme'",
}
end
elseif M.state.system == "Windows_NT" or M.state.system == "WSL" then
local reg = "reg.exe"

Expand Down
56 changes: 52 additions & 4 deletions lua/auto-dark-mode/interval.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ local uv = vim.uv or vim.loop
---@return Appearance?
local function parse_query_response(stdout, stderr)
if M.state.system == "Linux" then
if stderr ~= "" then
return nil;
end
if stderr ~= "" then
return nil
end

-- https://github.com/flatpak/xdg-desktop-portal/blob/c0f0eb103effdcf3701a1bf53f12fe953fbf0b75/data/org.freedesktop.impl.portal.Settings.xml#L32-L46
-- 0: no preference
Expand Down Expand Up @@ -67,6 +67,49 @@ local function sync_theme(appearance)
end
end

-- Uses a subprocess to monitor the system for the current dark mode setting.
-- The callback is called with the plaintext stdout response of the query.
---@param callback? fun(stdout: string, stderr: string): nil
---@return nil
M.monitor_dark_mode = function(callback)
-- if no callback is provided, use a no-op
callback = callback or function() end

if vim.system then
vim.system(M.state.monitor_command, {
text = true,
stdout = function(_, data)
if string.match(data, "uint32") then
-- check if this was a signal update with a new value,
-- as otherwise the fallback option will be incorrectly triggered
callback(data, "")
end
end,
stderr = function(_, data)
callback("", data)
end,
})
else
-- Legacy implementation using `vim.fn.jobstart` instead of `vim.system`,
-- for use in neovim <0.10.0
vim.fn.jobstart(M.state.monitor_command, {
on_stdout = function(_, data, _)
data = table.concat(data, "")

if string.match(data, "uint32") then
-- check if this was a signal update with a new value,
-- as otherwise the fallback option will be incorrectly triggered
callback(data, "")
end
end,
on_stderr = function(_, data, _)
data = table.concat(data, "")
callback("", data)
end,
})
end
end

-- Uses a subprocess to query the system for the current dark mode setting.
-- The callback is called with the plaintext stdout response of the query.
---@param callback? fun(stdout: string, stderr: string): nil
Expand Down Expand Up @@ -151,7 +194,12 @@ M.start = function(options, state)
-- act as if the timer has finished once to instantly sync on startup
timer_callback()

M.start_timer()
-- if the system supports monitoring, prefer that over polling updates
if next(M.state.monitor_command) ~= nil then
M.monitor_dark_mode(M.parse_callback)
else
M.start_timer()
end
end

return M
Loading