diff --git a/pactl-widget/pactl.lua b/pactl-widget/pactl.lua index 0dbae944..e23ee300 100644 --- a/pactl-widget/pactl.lua +++ b/pactl-widget/pactl.lua @@ -1,52 +1,79 @@ local spawn = require("awful.spawn") +local awful = require("awful") local utils = require("awesome-wm-widgets.pactl-widget.utils") local pactl = {} +-- Cached volume/mute state, updated asynchronously via update_async() +local cache = {} + +-- Build a pactl argv table with the C locale forced for consistent output. +local function pactl_cmd(...) + return {'env', 'LC_ALL=C', 'pactl', ...} +end function pactl.volume_increase(device, step) - spawn('pactl set-sink-volume ' .. device .. ' +' .. step .. '%', false) + spawn(pactl_cmd('set-sink-volume', device, '+' .. step .. '%'), false) end function pactl.volume_decrease(device, step) - spawn('pactl set-sink-volume ' .. device .. ' -' .. step .. '%', false) + spawn(pactl_cmd('set-sink-volume', device, '-' .. step .. '%'), false) end function pactl.mute_toggle(device) - spawn('pactl set-sink-mute ' .. device .. ' toggle', false) + spawn(pactl_cmd('set-sink-mute', device, 'toggle'), false) end function pactl.get_volume(device) - local stdout = utils.popen_and_return('pactl get-sink-volume ' .. device) - - local volsum, volcnt = 0, 0 - for vol in string.gmatch(stdout, "(%d?%d?%d)%%") do - vol = tonumber(vol) - if vol ~= nil then - volsum = volsum + vol - volcnt = volcnt + 1 + return cache[device] and cache[device].volume +end + +function pactl.get_mute(device) + return cache[device] and cache[device].mute or false +end + +function pactl.update_async(device, callback) + awful.spawn.easy_async(pactl_cmd('get-sink-volume', device), function(vol_stdout, _, _, vol_exit) + if vol_exit ~= 0 then + cache[device] = nil + if callback then callback(nil, false) end + return end - end - if volcnt == 0 then - return nil - end + if not cache[device] then cache[device] = {} end - return volsum / volcnt -end + local volsum, volcnt = 0, 0 + for vol in string.gmatch(vol_stdout, "(%d?%d?%d)%%") do + vol = tonumber(vol) + if vol ~= nil then + volsum = volsum + vol + volcnt = volcnt + 1 + end + end -function pactl.get_mute(device) - local stdout = utils.popen_and_return('LC_ALL=C pactl get-sink-mute ' .. device) - if string.find(stdout, "yes") then - return true - else - return false - end + if volcnt > 0 then + cache[device].volume = volsum / volcnt + end + + awful.spawn.easy_async(pactl_cmd('get-sink-mute', device), function(mute_stdout, _, _, mute_exit) + if mute_exit ~= 0 then + cache[device] = nil + if callback then callback(nil, false) end + return + end + cache[device].mute = string.find(mute_stdout, "yes") ~= nil + if callback then + callback(cache[device].volume, cache[device].mute) + end + end) + end) end function pactl.get_sinks_and_sources() - local default_sink = utils.trim(utils.popen_and_return('pactl get-default-sink')) - local default_source = utils.trim(utils.popen_and_return('pactl get-default-source')) + local default_sink = utils.trim(utils.popen_and_return( + table.concat(pactl_cmd('get-default-sink'), ' '))) + local default_source = utils.trim(utils.popen_and_return( + table.concat(pactl_cmd('get-default-source'), ' '))) local sinks = {} local sources = {} @@ -57,7 +84,8 @@ function pactl.get_sinks_and_sources() local value local in_section - for line in utils.popen_and_return('LC_ALL=C pactl list'):gmatch('[^\r\n]*') do + for line in utils.popen_and_return( + table.concat(pactl_cmd('list'), ' ')):gmatch('[^\r\n]*') do if string.match(line, '^%a+ #') then in_section = nil @@ -117,7 +145,7 @@ function pactl.get_sinks_and_sources() end function pactl.set_default(type, name) - spawn('pactl set-default-' .. type .. ' "' .. name .. '"', false) + spawn(pactl_cmd('set-default-' .. type, name), false) end diff --git a/pactl-widget/volume.lua b/pactl-widget/volume.lua index 1c3108cd..ff1d3e4b 100644 --- a/pactl-widget/volume.lua +++ b/pactl-widget/volume.lua @@ -166,31 +166,40 @@ local function worker(user_args) end local function update_graphic(widget) - local vol = pactl.get_volume(device) - if vol ~= nil then - widget:set_volume_level(vol) - end - - if pactl.get_mute(device) then - widget:mute() - else - widget:unmute() - end + pactl.update_async(device, function(vol, is_muted) + if vol ~= nil then + widget:set_volume_level(vol) + end + if is_muted then + widget:mute() + else + widget:unmute() + end + end) end function volume:inc(s) pactl.volume_increase(device, s or step) - update_graphic(volume.widget) + gears.timer.start_new(0.1, function() + update_graphic(volume.widget) + return false + end) end function volume:dec(s) pactl.volume_decrease(device, s or step) - update_graphic(volume.widget) + gears.timer.start_new(0.1, function() + update_graphic(volume.widget) + return false + end) end function volume:toggle() pactl.mute_toggle(device) - update_graphic(volume.widget) + gears.timer.start_new(0.1, function() + update_graphic(volume.widget) + return false + end) end function volume:popup() @@ -218,7 +227,7 @@ local function worker(user_args) ) ) - gears.timer { + local volume_timer = gears.timer { timeout = refresh_rate, call_now = true, autostart = true, @@ -231,7 +240,8 @@ local function worker(user_args) awful.tooltip { objects = { volume.widget }, timer_function = function() - return pactl.get_volume(device) .. " %" + local vol = pactl.get_volume(device) + return (vol and string.format("%.0f", vol) or "?") .. " %" end, } end