From 3410f9ac7ae55fd3128c4c5b89dd96ffd5cdda7c Mon Sep 17 00:00:00 2001 From: Stefano Mazzucco Date: Mon, 10 Jul 2017 22:06:41 +0100 Subject: [PATCH] Unify code from widget and widget_client files It turns out that Awesome can automatically deal with GLib's main loop and we don't need an external process to listen to pulseaudio's DBus. This greatly simplifies the code since we: - can merge the widget and widget_client files - don't need to do all the string parsing but can modify the widget directly Credit to https://github.com/psychon for raising the issue and providing sample code. Closes #1 (https://github.com/stefano-m/awesome-pulseaudio_widget/issues/1) --- README.md | 12 --- pulseaudio_widget-0.3.1-1.rockspec | 27 +++++++ pulseaudio_widget-devel-1.rockspec | 3 +- pulseaudio_widget.lua | 114 +++++++++++++++-------------- pulseaudio_widget_client.lua | 110 ---------------------------- 5 files changed, 87 insertions(+), 179 deletions(-) create mode 100644 pulseaudio_widget-0.3.1-1.rockspec delete mode 100644 pulseaudio_widget_client.lua diff --git a/README.md b/README.md index 2fcf6da..d1cb775 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,6 @@ A widget for the Awesome Window Manager (version 4.x) that uses [pulseaudio_dbus](https://github.com/stefano-m/lua-pulseaudio_dbus) to control your audio devices. -## A note about PulseAudio, DBus and Awesome - -The Pulseaudio DBus interface requires clients to use peer-to-peer connection -rather than the usual system/session buses. This means that we *cannot* use the -Awesome DBus API that supports *only* system and session buses. - -The solution is to run an external client application to establish a -peer-to-peer connection and listen to DBus signals. The output of the client is -read by the widget that updates itself accordingly. This is done thanks -to -[`awful.spawn.with_line_callback`](https://awesomewm.org/apidoc/libraries/awful.spawn.html#with_line_callback). - # Requirements In addition to the requirements listed in the `rockspec` file, you will need diff --git a/pulseaudio_widget-0.3.1-1.rockspec b/pulseaudio_widget-0.3.1-1.rockspec new file mode 100644 index 0000000..5fffa80 --- /dev/null +++ b/pulseaudio_widget-0.3.1-1.rockspec @@ -0,0 +1,27 @@ +package = "pulseaudio_widget" +version = "0.3.1-1" +source = { + url = "git://github.com/stefano-m/awesome-pulseaudio_widget", + tag = "v0.3.1" +} +description = { + summary = "A PulseAudio widget for the Awesome Window Manager", + detailed = [[ + Control your audio in the Awesome with PulseAudio and DBus. + ]], + homepage = "https://github.com/stefano-m/awesome-pulseaudio_widget", + license = "GPL v3" +} +supported_platforms = { + "linux" +} +dependencies = { + "lua >= 5.1", + "pulseaudio_dbus >= 0.11.0, < 0.12" +} +build = { + type = "builtin", + modules = { + pulseaudio_widget = "pulseaudio_widget.lua" + } +} diff --git a/pulseaudio_widget-devel-1.rockspec b/pulseaudio_widget-devel-1.rockspec index 793ec93..3a33ab4 100644 --- a/pulseaudio_widget-devel-1.rockspec +++ b/pulseaudio_widget-devel-1.rockspec @@ -19,6 +19,5 @@ dependencies = { supported_platforms = { "linux" } build = { type = "builtin", - modules = { pulseaudio_widget = "pulseaudio_widget.lua", - pulseaudio_widget_client = "pulseaudio_widget_client.lua"}, + modules = { pulseaudio_widget = "pulseaudio_widget.lua" }, } diff --git a/pulseaudio_widget.lua b/pulseaudio_widget.lua index 8af9acb..a14d2c8 100644 --- a/pulseaudio_widget.lua +++ b/pulseaudio_widget.lua @@ -17,8 +17,6 @@ This program was inspired by the [Awesome Pulseaudio Widget (APW)](https://github.com/mokasin/apw) ]] - -local awesome = awesome -- luacheck: ignore local string = string local awful = require("awful") @@ -124,63 +122,44 @@ function widget.toggle_muted_mic() end end -function widget:kill_client() - if type(self.server_pid) == "number" then - awful.spawn("kill -TERM " .. self.server_pid) - end -end - -function widget:run_client() - - local function update_after_signal(line, regex, sub, sep) - sep = sep or " " - local v, found = line:gsub(regex, sub) - if found ~= 0 then - local idx = v:find(sep) - local vol = v:sub(1, idx - 1) - local path = v:sub(idx + 1) - if path:find("/sink%d+$") then - self:update_appearance(vol) - self.notify(vol) - end - end - end - - local pid = awful.spawn.with_line_callback( - [[lua -e 'require("pulseaudio_widget_client")']], - { - stdout = function (line) - - update_after_signal(line, "^(VolumeUpdated:%s+)(%d+)(|)([%w/]+)", "%2 %4") - - update_after_signal(line, "^(MuteUpdated:%s+)(%w+)(|)([%w/]+)", "%2 %4") - - local v, found - v, found = line:gsub("^(NewSink:%s+)(/.*%w)", "%2") - if found ~=0 then - self:update_sink(v) - local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1] - self:update_appearance(volume) - self.notify(volume) - end - - v, found = line:gsub("^(NewSource:%s+)(/.*%w)", "%2") - if found ~=0 then - self:update_source({v}) - end - end - }) - - self.server_pid = pid -end - widget:buttons(gears.table.join( awful.button({ }, 1, widget.toggle_muted), awful.button({ }, 3, function () awful.spawn(widget.mixer) end), awful.button({ }, 4, widget.volume_up), awful.button({ }, 5, widget.volume_down))) -awesome.connect_signal("exit", function () widget:kill_client() end) +function widget:connect_device(device) + if not device then + return + end + + if device.signals.VolumeUpdated then + device:connect_signal( + function (this, vols) + -- FIXME: BaseVolume for sources (i.e. microphones) won't give the correct percentage + local v = math.ceil(tonumber(vols[1][1]) / this.BaseVolume * 100) + if this.object_path == self.sink.object_path then + self:update_appearance(v) + self.notify(v) + end + end, + "VolumeUpdated" + ) + end + + if device.signals.MuteUpdated then + device:connect_signal( + function (this, is_mute) + local m = is_mute[1] and "Muted" or "Unmuted" + if this.object_path == self.sink.object_path then + self:update_appearance(m) + self.notify(m) + end + end, + "MuteUpdated" + ) + end +end function widget:init() local status, address = pcall(pulse.get_address) @@ -196,15 +175,40 @@ function widget:init() self.connection = pulse.get_connection(address) self.core = pulse.get_core(self.connection) + -- listen on ALL objects as sinks and sources may change + self.core:ListenForSignal("org.PulseAudio.Core1.Device.VolumeUpdated", {}) + self.core:ListenForSignal("org.PulseAudio.Core1.Device.MuteUpdated", {}) + + self.core:ListenForSignal("org.PulseAudio.Core1.NewSink", {self.core.object_path}) + self.core:connect_signal( + function (_, newsinks) + self:update_sink(newsinks[1]) + self:connect_device(self.sink) + local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1] + self:update_appearance(volume) + end, + "NewSink" + ) + + self.core:ListenForSignal("org.PulseAudio.Core1.NewSource", {self.core.object_path}) + self.core:connect_signal( + function (_, newsources) + self:update_source(newsources) + self:connect_device(self.source) + end, + "NewSource" + ) + self:update_source(self.core:get_sources()) + self:connect_device(self.source) local sink_path = assert(self.core:get_sinks()[1], "No sinks found") self:update_sink(sink_path) + self:connect_device(self.sink) + local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1] self:update_appearance(volume) - self:run_client() - self.__index = self return self diff --git a/pulseaudio_widget_client.lua b/pulseaudio_widget_client.lua deleted file mode 100644 index fb628f4..0000000 --- a/pulseaudio_widget_client.lua +++ /dev/null @@ -1,110 +0,0 @@ ---[[ - Copyright 2017 Stefano Mazzucco - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -]] - ---[[-- This module is meant to be run with - [`awful.spawn.with_line_callback`](https://awesomewm.org/apidoc/libraries/awful.spawn.html#with_line_callback). - - It starts a client that listens to the pulseaudio DBus server and prints to - standard output wheter the volume or sinks (e.g. selecting the audio from the - TV) change. The changes are printed to standard output and are used by the - pulseaudio widget to change its appearance. - - We must do this because Awesome's DBus API can only connect to system and - session buses, but pulseaudio uses its own per-user connection. - -]] - -local pulse = require("pulseaudio_dbus") -local GLib = require("lgi").GLib - -local address = pulse.get_address() - -local connection = pulse.get_connection(address) -local core = pulse.get_core(connection) -local sink = pulse.get_device(connection, core.Sinks[1]) -local source - -local function get_source(conn, sources) - for _, source_path in ipairs(sources) do - local s = pulse.get_device(conn, source_path) - if s.Name and not s.Name:match("%.monitor$") then - return s - end - end -end - -source = get_source(connection, core.Sources) - --- listen on ALL objects as sinks may change -core:ListenForSignal("org.PulseAudio.Core1.Device.VolumeUpdated", {}) -core:ListenForSignal("org.PulseAudio.Core1.Device.MuteUpdated", {}) - -local function connect_device(s) - - if s.signals.VolumeUpdated then - s:connect_signal( - function (self, vols) - local v = math.ceil(tonumber(vols[1][1]) / self.BaseVolume * 100) - -- FIXME: BaseVolume for sources (i.e. microphones) won't give the correct percentage - print(string.format("VolumeUpdated: %s|%s", v, self.object_path)) - end, - "VolumeUpdated" - ) - end - - if s.signals.MuteUpdated then - s:connect_signal( - function (self, is_mute) - local m = is_mute[1] and "Muted" or "Unmuted" - print(string.format("MuteUpdated: %s|%s", m, self.object_path)) - end, - "MuteUpdated" - ) - end -end - -connect_device(sink) -if source then - connect_device(source) -end - -core:ListenForSignal("org.PulseAudio.Core1.NewSink", {core.object_path}) -core:connect_signal( - function (_, newsinks) - print(string.format("NewSink: %s", newsinks[1])) - sink = pulse.get_device(connection, newsinks[1]) - connect_device(sink) - end, - "NewSink" -) - -core:ListenForSignal("org.PulseAudio.Core1.NewSource", {core.object_path}) -core:connect_signal( - function (_, newsources) - print(string.format("NewSource: %s", newsources[1])) - source = get_source(connection, newsources) - if source then - connect_device(source) - end - end, - "NewSource" -) - --- Start the client. Send SIGTERM to stop it. -print("Starting Awesome PulseAudio Widget Client") -GLib.MainLoop():run()