2016-08-12 23:04:59 +02:00
|
|
|
--[[
|
2017-05-25 15:41:27 +02:00
|
|
|
Copyright 2017 Stefano Mazzucco <stefano AT curso DOT re>
|
2016-08-12 23:04:59 +02:00
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
This program was inspired by the
|
2016-08-12 23:04:59 +02:00
|
|
|
[Awesome Pulseaudio Widget (APW)](https://github.com/mokasin/apw)
|
|
|
|
]]
|
2017-05-25 15:41:27 +02:00
|
|
|
local string = string
|
|
|
|
|
2016-08-12 23:04:59 +02:00
|
|
|
local awful = require("awful")
|
2017-05-25 15:41:27 +02:00
|
|
|
local gears = require("gears")
|
2016-08-12 23:04:59 +02:00
|
|
|
|
|
|
|
local wibox = require("wibox")
|
|
|
|
local beautiful = require("beautiful")
|
|
|
|
local naughty = require("naughty")
|
|
|
|
|
|
|
|
local pulse = require("pulseaudio_dbus")
|
|
|
|
|
|
|
|
local icon_theme = "/usr/share/icons/Adwaita/scalable/status"
|
|
|
|
local icon_extension = ".svg"
|
|
|
|
|
|
|
|
icon_theme = beautiful.pulse_icon_theme or icon_theme
|
|
|
|
icon_extension = beautiful.pulse_icon_extension or icon_extension
|
|
|
|
|
|
|
|
local icon = {
|
|
|
|
high = icon_theme .. "/audio-volume-high-symbolic" .. icon_extension,
|
|
|
|
med = icon_theme .. "/audio-volume-medium-symbolic" .. icon_extension,
|
|
|
|
low = icon_theme .. "/audio-volume-low-symbolic" .. icon_extension,
|
|
|
|
muted = icon_theme .. "/audio-volume-muted-symbolic" .. icon_extension
|
|
|
|
}
|
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
local widget = wibox.widget.imagebox()
|
|
|
|
widget.tooltip = awful.tooltip({ objects = { widget },})
|
2016-08-12 23:04:59 +02:00
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
function widget:update_appearance(v)
|
|
|
|
local i, msg
|
2016-08-12 23:04:59 +02:00
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
if v == "Muted" then
|
|
|
|
msg = v
|
2016-08-12 23:04:59 +02:00
|
|
|
i = icon.muted
|
|
|
|
else
|
2017-05-25 15:41:27 +02:00
|
|
|
v = v == "Unmuted" and self.sink:get_volume_percent()[1] or tonumber(v)
|
|
|
|
msg = string.format("%d%%", v)
|
|
|
|
if v <= 33 then
|
|
|
|
i = icon.low
|
|
|
|
elseif v <= 66 then
|
|
|
|
i = icon.med
|
|
|
|
else
|
|
|
|
i = icon.high
|
|
|
|
end
|
2016-08-12 23:04:59 +02:00
|
|
|
end
|
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
self:set_image(i)
|
|
|
|
self.tooltip:set_text(msg)
|
|
|
|
|
2016-08-12 23:04:59 +02:00
|
|
|
end
|
|
|
|
|
2018-12-02 22:43:15 +01:00
|
|
|
function widget:notify(v)
|
2017-05-25 15:41:27 +02:00
|
|
|
local msg = tonumber(v) and string.format("%d%%", v) or v
|
2018-12-02 22:43:15 +01:00
|
|
|
|
|
|
|
if self.notification then
|
|
|
|
naughty.destroy(self.notification, naughty.notificationClosedReason.dismissedByCommand)
|
|
|
|
end
|
|
|
|
|
|
|
|
self.notification = naughty.notify({text=msg, timeout=1})
|
|
|
|
|
2016-08-12 23:04:59 +02:00
|
|
|
end
|
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
function widget:update_sink(object_path)
|
2017-06-01 23:29:23 +02:00
|
|
|
self.sink = pulse.get_device(self.connection, object_path)
|
2016-08-12 23:04:59 +02:00
|
|
|
end
|
|
|
|
|
2017-11-11 18:59:57 +01:00
|
|
|
function widget:update_sources(sources)
|
2017-06-02 13:12:42 +02:00
|
|
|
for _, source_path in ipairs(sources) do
|
|
|
|
local s = pulse.get_device(self.connection, source_path)
|
|
|
|
if s.Name and not s.Name:match("%.monitor$") then
|
|
|
|
self.source = s
|
|
|
|
break
|
|
|
|
else
|
|
|
|
self.source = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-08-12 23:04:59 +02:00
|
|
|
function widget.volume_up()
|
2017-05-25 15:41:27 +02:00
|
|
|
if not widget.sink:is_muted() then
|
2016-08-12 23:04:59 +02:00
|
|
|
widget.sink:volume_up()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function widget.volume_down()
|
2017-05-25 15:41:27 +02:00
|
|
|
if not widget.sink:is_muted() then
|
2016-08-12 23:04:59 +02:00
|
|
|
widget.sink:volume_down()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function widget.toggle_muted()
|
|
|
|
widget.sink:toggle_muted()
|
|
|
|
end
|
|
|
|
|
2017-06-02 13:12:42 +02:00
|
|
|
function widget.volume_up_mic()
|
|
|
|
if widget.source and not widget.source:is_muted() then
|
|
|
|
widget.source:volume_up()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function widget.volume_down_mic()
|
|
|
|
if widget.source and not widget.source:is_muted() then
|
|
|
|
widget.source:volume_down()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function widget.toggle_muted_mic()
|
|
|
|
if widget.source then
|
|
|
|
widget.source:toggle_muted()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-10 23:06:41 +02:00
|
|
|
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)))
|
2017-05-25 15:41:27 +02:00
|
|
|
|
2017-07-10 23:06:41 +02:00
|
|
|
function widget:connect_device(device)
|
|
|
|
if not device then
|
|
|
|
return
|
2017-06-02 13:12:42 +02:00
|
|
|
end
|
|
|
|
|
2017-07-10 23:06:41 +02:00
|
|
|
if device.signals.VolumeUpdated then
|
|
|
|
device:connect_signal(
|
2017-11-11 18:59:57 +01:00
|
|
|
function (this, volume)
|
2017-07-10 23:06:41 +02:00
|
|
|
-- FIXME: BaseVolume for sources (i.e. microphones) won't give the correct percentage
|
2017-11-11 18:59:57 +01:00
|
|
|
local v = math.ceil(tonumber(volume[1]) / this.BaseVolume * 100)
|
2017-07-10 23:06:41 +02:00
|
|
|
if this.object_path == self.sink.object_path then
|
|
|
|
self:update_appearance(v)
|
2018-12-02 22:43:15 +01:00
|
|
|
self:notify(v)
|
2017-06-02 13:12:42 +02:00
|
|
|
end
|
2017-07-10 23:06:41 +02:00
|
|
|
end,
|
|
|
|
"VolumeUpdated"
|
|
|
|
)
|
|
|
|
end
|
2017-06-02 13:12:42 +02:00
|
|
|
|
2017-07-10 23:06:41 +02:00
|
|
|
if device.signals.MuteUpdated then
|
|
|
|
device:connect_signal(
|
|
|
|
function (this, is_mute)
|
2017-11-11 18:59:57 +01:00
|
|
|
local m = is_mute and "Muted" or "Unmuted"
|
2017-07-10 23:06:41 +02:00
|
|
|
if this.object_path == self.sink.object_path then
|
|
|
|
self:update_appearance(m)
|
2018-12-02 22:43:15 +01:00
|
|
|
self:notify(m)
|
2017-05-25 15:41:27 +02:00
|
|
|
end
|
2017-07-10 23:06:41 +02:00
|
|
|
end,
|
|
|
|
"MuteUpdated"
|
|
|
|
)
|
|
|
|
end
|
2016-08-12 23:04:59 +02:00
|
|
|
end
|
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
function widget:init()
|
|
|
|
local status, address = pcall(pulse.get_address)
|
|
|
|
if not status then
|
|
|
|
naughty.notify({title="Error while loading the PulseAudio widget",
|
|
|
|
text=address,
|
|
|
|
preset=naughty.config.presets.critical})
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
self.mixer = "pavucontrol"
|
|
|
|
|
|
|
|
self.connection = pulse.get_connection(address)
|
|
|
|
self.core = pulse.get_core(self.connection)
|
|
|
|
|
2017-07-10 23:06:41 +02:00
|
|
|
-- 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(
|
2017-11-11 18:59:57 +01:00
|
|
|
function (_, newsink)
|
|
|
|
self:update_sink(newsink)
|
2017-07-10 23:06:41 +02:00
|
|
|
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(
|
2017-11-11 18:59:57 +01:00
|
|
|
function (_, newsource)
|
|
|
|
self:update_sources({newsource})
|
2017-07-10 23:06:41 +02:00
|
|
|
self:connect_device(self.source)
|
|
|
|
end,
|
|
|
|
"NewSource"
|
|
|
|
)
|
|
|
|
|
2017-11-11 18:59:57 +01:00
|
|
|
self:update_sources(self.core:get_sources())
|
2017-07-10 23:06:41 +02:00
|
|
|
self:connect_device(self.source)
|
2017-06-02 13:12:42 +02:00
|
|
|
|
|
|
|
local sink_path = assert(self.core:get_sinks()[1], "No sinks found")
|
2017-05-25 15:41:27 +02:00
|
|
|
self:update_sink(sink_path)
|
2017-07-10 23:06:41 +02:00
|
|
|
self:connect_device(self.sink)
|
|
|
|
|
2017-05-25 23:07:53 +02:00
|
|
|
local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1]
|
2017-05-25 15:41:27 +02:00
|
|
|
self:update_appearance(volume)
|
|
|
|
|
|
|
|
self.__index = self
|
|
|
|
|
|
|
|
return self
|
|
|
|
end
|
2016-08-12 23:04:59 +02:00
|
|
|
|
2017-05-25 15:41:27 +02:00
|
|
|
return widget:init()
|