local awful = require("awful") local naughty = require("naughty") -- Gio is used to handle the subprocess instead of awful.spawn. -- Gio is more flexible and allows writing to stdin. -- also, awful.spawn.with_line_callback does not play nicely with pavolctld, as -- it seems to feed the stdout back into its stdin and kill performance. local lgi = require("lgi") local Gio = lgi.Gio -- start subprocess local p = Gio.Subprocess.new({ "pavolctld" }, Gio.SubprocessFlags.STDIN_PIPE + Gio.SubprocessFlags.STDOUT_PIPE) local stdout = p:get_stdout_pipe() local stdin = p:get_stdin_pipe() -- state vars local sinks = { default = nil, command = nil, -- sink being modified by commands sinks = {}, } function sinks.get(i) if sinks.sinks[i] == nil then sinks.sinks[i] = { vol = 0, db = 0.0, mute = 0, name = "", desc = "", } end return sinks.sinks[i] end -- return table local pavolctld = { _cb = {}, sinks = sinks } -- callbacks function pavolctld.set_volume_change_callback(cb) pavolctld._cb['v'] = cb end function pavolctld.set_sink_change_callback(cb) pavolctld._cb['s'] = cb end function pavolctld.set_default_sink_change_callback(cb) pavolctld._cb['f'] = cb end function pavolctld.set_sink_remove_callback(cb) pavolctld._cb['x'] = cb end -- parse output function parse_csv(csv) return (csv .. ","):gmatch("(.-),") end awful.spawn.read_lines(stdout, function(s) local cmd = s:sub(1, 1) -- first char of output -- volume change if cmd == 'v' then local v = parse_csv(s:sub(2)) local i = tonumber(v()) local sink = sinks.get(i) sink.vol = tonumber(v()) sink.db = tonumber(v()) sink.mute = tonumber(v()) -- sink description change elseif cmd == 's' then local v = parse_csv(s:sub(2)) local i = tonumber(v()) local sink = sinks.get(i) sink.name = v() sink.desc = v() -- default sink change elseif cmd == 'f' then local f = tonumber(s:sub(2)) sinks.default = sinks.get(f) -- set command sink to default sink for now TODO change later send_cmd("s") -- sink removed elseif cmd == 'x' then local x = tonumber(s:sub(2)) table.remove(sinks.sinks, x) -- unrecognized cmd else naughty.notify({ preset = naughty.config.presets.critical, title = "pavolctld error", text = s }) end -- run command callback if pavolctld._cb[cmd] then pavolctld._cb[cmd]() end end) -- pavolctld takes commands in using stdin function send_cmd(cmd) local _, err = stdin:write_all(cmd .. "\n", nil) if err then return nil end return true end function pavolctld.volume_inc(vol) return send_cmd("v+" .. vol) end function pavolctld.volume_dec(vol) return send_cmd("v-" .. vol) end function pavolctld.volume_set(vol) return send_cmd("v" .. vol) end function pavolctld.mute_set(muted) return send_cmd("m" .. muted and 1 or 0) end function pavolctld.mute_toggle() return send_cmd("m") end function pavolctld.default_sink_set(i) return send_cmd("f" .. i) end return pavolctld