2023-01-19 13:42:20 +00:00
-- This file is part of Reno desktop.
--
-- Reno desktop 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.
--
-- Reno desktop 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 Reno desktop. If not, see <https://www.gnu.org/licenses/>.
-- Pulseaudio per-client volume setting
local awful = require ( " awful " )
local gears = require ( " gears " )
local wibox = require ( " wibox " )
local awmtk2 = require ( " awmtk2 " )
local fastyaml = require ( " parsers " ) . fast_split_yaml
local beautiful = require ( " beautiful " )
local ask = require ( " asckey " )
local test_pactl = os.execute ( " pactl --version " )
local result = test_pactl
if _VERSION : match ( " 5.1 " ) then
result = ( test_pactl == 0 )
end
if not result then
2023-03-05 12:53:37 +00:00
return
2023-01-19 13:42:20 +00:00
end
local function get_icon ( percent )
if percent >= 66 then
return beautiful [ " volume-high-symbolic " ]
elseif percent >= 33 then
return beautiful [ " volume-medium-symbolic " ]
elseif percent > 0 then
return beautiful [ " volume-low-symbolic " ]
else
return beautiful [ " volume-muted-symbolic " ]
end
end
return function ( args )
local style = awmtk2.create_style ( " client_volume " ,
awmtk2.generic . oneline_widget , args.style , args.vertical )
local templates = awmtk2.create_template_lib ( " client_volume " , awmtk2.templates , args.templates )
local t = awmtk2.build_templates ( templates , style , args.vertical )
local widget = wibox.widget ( t.container ( {
t.icon ( {
id = " client_volume_icon " ,
resize = true ,
} ) ,
( args.vertical and {
t.textbox ( {
id = " error "
} ) ,
widget = wibox.container . rotate ,
direction = " east "
} ) or t.textbox ( {
id = " error "
} ) ,
t.slider ( {
minimum = 0 ,
maximum = 100 ,
id = " client_volume " ,
value = - 1
} ) ,
layout = ( args.vertical and wibox.layout . fixed.vertical ) or
wibox.layout . fixed.horizontal
} ) )
local errorbox = widget : get_children_by_id ( " error " ) [ 1 ]
local icon = widget : get_children_by_id ( " client_volume_icon " ) [ 1 ]
local slider = widget : get_children_by_id ( " client_volume " ) [ 1 ]
-- Local tracking value to prevent zero volume on start
local touched = false
-- Attach to focus change
client.connect_signal ( " update_volume " , function ( c )
awful.spawn . easy_async ( " pactl list sink-inputs " , function ( stdout )
local pactl_data = fastyaml ( stdout )
local cl
for _ , v in pairs ( pactl_data ) do
if not c then return end
if v : match ( " application.process.id = \" " .. tostring ( c.pid ) .. " \" " ) then
cl = v
end
end
if not cl then
slider.visible = false
errorbox.visible = true
errorbox : set_markup ( " No sound/Not available " )
icon : set_image ( beautiful [ " volume-muted-symbolic " ] )
return
end
local volume = tonumber ( cl : match ( " Volume:[^ \n ]-(%d*)%% " ) )
slider.visible = true
errorbox.visible = false
icon : set_image ( get_icon ( volume ) )
slider.value = volume
touched = true
end )
end )
client.connect_signal ( " focus " , function ( c )
touched = false
c : emit_signal ( " update_volume " )
end )
local update_timer = gears.timer ( {
timeout = 0.5 ,
autostart = true ,
callback = function ( )
if client.focus then
client.focus : emit_signal ( " update_volume " )
end
end
} )
-- Async lock to prevent callback interference
local volume_lock = false
-- Function to set client volume
local function volume ( value )
if volume_lock then return end
volume_lock = true
awful.spawn . easy_async ( " pactl list sink-inputs " , function ( stdout )
local pactl_data = fastyaml ( stdout )
if not ( client.focus and client.focus . pid ) then
volume_lock = false
return
end
for _ , v in pairs ( pactl_data ) do
if v : match ( " application.process.id = \" " .. tostring ( client.focus . pid ) .. " \" " ) then
local sink_id = v : match ( " ^%s*Sink Input #(%d+) " )
if sink_id then
print ( sink_id , value )
awful.spawn ( " pactl set-sink-input-volume " .. tostring ( sink_id ) .. " " .. tostring ( value ) .. " % " )
end
end
end
volume_lock = false
end )
end
-- Attach change to slider
slider : connect_signal ( " widget::redraw_needed " , function ( )
if touched then
volume ( slider.value )
update_timer : again ( )
end
end )
root.keys ( gears.table . join (
root.keys ( ) ,
ask.k ( " :client.volume_up " , function ( )
volume ( " +5 " )
end , { description = " increase client volume " , group = " client " } ) ,
ask.k ( " :client.volume_down " , function ( )
volume ( " -5 " )
end , { description = " decrease client volume " , group = " client " } ) ,
ask.k ( " :client.volume_mute " , function ( )
volume ( 0 )
end , { description = " mute client " , group = " client " } )
) )
return widget
end