Reno is the second iteration of the AWMTK-powered AwesomeWM config.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
5.6 KiB

2 years ago
  1. -- This file is part of Reno desktop.
  2. --
  3. -- 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.
  4. --
  5. -- 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.
  6. --
  7. -- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
  8. -- Pulseaudio per-client volume setting
  9. local awful = require("awful")
  10. local gears = require("gears")
  11. local wibox = require("wibox")
  12. local awmtk2 = require("awmtk2")
  13. local fastyaml = require("parsers").fast_split_yaml
  14. local beautiful = require("beautiful")
  15. local ask = require("asckey")
  16. local pactl_data = {}
  17. local test_pactl = os.execute("pactl --version")
  18. local result = test_pactl
  19. if _VERSION:match("5.1") then
  20. result = (test_pactl == 0)
  21. end
  22. if not result then
  23. return
  24. end
  25. local function get_icon(percent)
  26. if percent >= 66 then
  27. return beautiful["volume-high-symbolic"]
  28. elseif percent >= 33 then
  29. return beautiful["volume-medium-symbolic"]
  30. elseif percent > 0 then
  31. return beautiful["volume-low-symbolic"]
  32. else
  33. return beautiful["volume-muted-symbolic"]
  34. end
  35. end
  36. return function(args)
  37. local style = awmtk2.create_style("client_volume",
  38. awmtk2.generic.oneline_widget, args.style)
  39. local templates = awmtk2.create_template_lib("client_volume",awmtk2.templates,args.templates)
  40. local t = awmtk2.build_templates(templates,style)
  41. local widget = wibox.widget(t.container({
  42. t.center({
  43. id = "client_volume_icon",
  44. widget = wibox.widget.imagebox
  45. }),
  46. t.textbox({
  47. id = "error"
  48. }),
  49. t.slider({
  50. minimum = 0,
  51. maximum = 100,
  52. id = "client_volume",
  53. value = -1
  54. }),
  55. layout = wibox.layout.fixed.horizontal
  56. }))
  57. local errorbox = widget:get_children_by_id("error")[1]
  58. local icon = widget:get_children_by_id("client_volume_icon")[1]
  59. local slider = widget:get_children_by_id("client_volume")[1]
  60. -- Local tracking value to prevent zero volume on start
  61. local slider_touched = false
  62. -- Get initial pactl data
  63. awful.spawn.easy_async("pactl list sink-inputs",function(stdout)
  64. local pactl_data = fastyaml(stdout)
  65. end)
  66. -- Attach to focus change
  67. client.connect_signal("update_volume",function(c)
  68. awful.spawn.easy_async("pactl list sink-inputs",function(stdout)
  69. local pactl_data = fastyaml(stdout)
  70. local cl
  71. for k,v in pairs(pactl_data) do
  72. if not c then return end
  73. if v:match("application.process.id = \""..tostring(c.pid).."\"") then
  74. cl = v
  75. end
  76. end
  77. if not cl then
  78. slider.visible = false
  79. errorbox.visible = true
  80. errorbox:set_markup("No sound/Not available")
  81. icon:set_image(beautiful["volume-muted-symbolic"])
  82. return
  83. end
  84. local volume = tonumber(cl:match("Volume:[^\n]-(%d*)%%"))
  85. slider.visible = true
  86. errorbox.visible = false
  87. icon:set_image(get_icon(volume))
  88. slider.value = volume
  89. touched = true
  90. end)
  91. end)
  92. client.connect_signal("focus",function(c)
  93. touched = false
  94. c:emit_signal("update_volume")
  95. end)
  96. local update_timer = gears.timer({
  97. timeout = 0.5,
  98. autostart = true,
  99. callback = function()
  100. if client.focus then
  101. client.focus:emit_signal("update_volume")
  102. end
  103. end
  104. })
  105. -- Async lock to prevent callback interference
  106. local volume_lock = false
  107. -- Function to set client volume
  108. local function volume(volume)
  109. if volume_lock then return end
  110. volume_lock = true
  111. awful.spawn.easy_async("pactl list sink-inputs",function(stdout)
  112. local pactl_data = fastyaml(stdout)
  113. local cl = {}
  114. if not (client.focus and client.focus.pid) then
  115. volume_lock = false
  116. return
  117. end
  118. for k,v in pairs(pactl_data) do
  119. if v:match("application.process.id = \""..tostring(client.focus.pid).."\"") then
  120. local sink_id = v:match("^%s*Sink Input #(%d+)")
  121. if sink_id then
  122. print(sink_id, volume)
  123. awful.spawn("pactl set-sink-input-volume "..tostring(sink_id).." "..tostring(volume).."%")
  124. end
  125. end
  126. end
  127. volume_lock = false
  128. end)
  129. end
  130. -- Attach change to slider
  131. slider:connect_signal("widget::redraw_needed",function(widget)
  132. if touched then
  133. volume(slider.value)
  134. update_timer:again()
  135. end
  136. end)
  137. root.keys(gears.table.join(
  138. root.keys(),
  139. ask.k(":client.volume_up", function()
  140. volume("+5")
  141. end,{description = "increase client volume", group = "client"}),
  142. ask.k(":client.volume_down", function()
  143. volume("-5")
  144. end,{description = "decrease client volume", group = "client"}),
  145. ask.k(":client.volume_mute", function()
  146. volume(0)
  147. end,{description = "mute client", group = "client"})
  148. ))
  149. return widget
  150. end