pulsebar.lua 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. --[[
  2. Licensed under GNU General Public License v2
  3. * (c) 2013, Luca CPZ
  4. * (c) 2013, Rman
  5. --]]
  6. local helpers = require("lain.helpers")
  7. local awful = require("awful")
  8. local naughty = require("naughty")
  9. local wibox = require("wibox")
  10. local math = math
  11. local string = string
  12. local type = type
  13. local tonumber = tonumber
  14. -- PulseAudio volume bar
  15. -- lain.widget.pulsebar
  16. local function factory(args)
  17. local pulsebar = {
  18. colors = {
  19. background = "#000000",
  20. mute_background = "#000000",
  21. mute = "#EB8F8F",
  22. unmute = "#A4CE8A"
  23. },
  24. _current_level = 0,
  25. _mute = "no",
  26. device = "N/A"
  27. }
  28. args = args or {}
  29. local timeout = args.timeout or 5
  30. local settings = args.settings or function() end
  31. local width = args.width or 63
  32. local height = args.height or 1
  33. local margins = args.margins or 1
  34. local paddings = args.paddings or 1
  35. local ticks = args.ticks or false
  36. local ticks_size = args.ticks_size or 7
  37. local tick = args.tick or "|"
  38. local tick_pre = args.tick_pre or "["
  39. local tick_post = args.tick_post or "]"
  40. local tick_none = args.tick_none or " "
  41. pulsebar.colors = args.colors or pulsebar.colors
  42. pulsebar.followtag = args.followtag or false
  43. pulsebar.notification_preset = args.notification_preset
  44. pulsebar.devicetype = args.devicetype or "sink"
  45. pulsebar.cmd = args.cmd or "pacmd list-" .. pulsebar.devicetype .. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
  46. if not pulsebar.notification_preset then
  47. pulsebar.notification_preset = {
  48. font = "Monospace 10"
  49. }
  50. end
  51. pulsebar.bar = wibox.widget {
  52. color = pulsebar.colors.unmute,
  53. background_color = pulsebar.colors.background,
  54. forced_height = height,
  55. forced_width = width,
  56. margins = margins,
  57. paddings = paddings,
  58. ticks = ticks,
  59. ticks_size = ticks_size,
  60. widget = wibox.widget.progressbar,
  61. }
  62. pulsebar.tooltip = awful.tooltip({ objects = { pulsebar.bar } })
  63. function pulsebar.update(callback)
  64. helpers.async({ awful.util.shell, "-c", type(pulsebar.cmd) == "string" and pulsebar.cmd or pulsebar.cmd() },
  65. function(s)
  66. volume_now = {
  67. index = string.match(s, "index: (%S+)") or "N/A",
  68. device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
  69. muted = string.match(s, "muted: (%S+)") or "N/A"
  70. }
  71. pulsebar.device = volume_now.index
  72. local ch = 1
  73. volume_now.channel = {}
  74. for v in string.gmatch(s, ":.-(%d+)%%") do
  75. volume_now.channel[ch] = v
  76. ch = ch + 1
  77. end
  78. volume_now.left = volume_now.channel[1] or "N/A"
  79. volume_now.right = volume_now.channel[2] or "N/A"
  80. local volu = volume_now.left
  81. local mute = volume_now.muted
  82. if volu:match("N/A") or mute:match("N/A") then return end
  83. if volu ~= pulsebar._current_level or mute ~= pulsebar._mute then
  84. pulsebar._current_level = tonumber(volu)
  85. pulsebar.bar:set_value(pulsebar._current_level / 100)
  86. if pulsebar._current_level == 0 or mute == "yes" then
  87. pulsebar._mute = mute
  88. pulsebar.tooltip:set_text ("[muted]")
  89. pulsebar.bar.color = pulsebar.colors.mute
  90. pulsebar.bar.background_color = pulsebar.colors.mute_background
  91. else
  92. pulsebar._mute = "no"
  93. pulsebar.tooltip:set_text(string.format("%s %s: %s", pulsebar.devicetype, pulsebar.device, volu))
  94. pulsebar.bar.color = pulsebar.colors.unmute
  95. pulsebar.bar.background_color = pulsebar.colors.background
  96. end
  97. settings()
  98. if type(callback) == "function" then callback() end
  99. end
  100. end)
  101. end
  102. function pulsebar.notify()
  103. pulsebar.update(function()
  104. local preset = pulsebar.notification_preset
  105. preset.title = string.format("%s %s - %s%%", pulsebar.devicetype, pulsebar.device, pulsebar._current_level)
  106. if pulsebar._mute == "yes" then
  107. preset.title = preset.title .. " muted"
  108. end
  109. -- tot is the maximum number of ticks to display in the notification
  110. -- fallback: default horizontal wibox height
  111. local wib, tot = awful.screen.focused().mywibox, 20
  112. -- if we can grab mywibox, tot is defined as its height if
  113. -- horizontal, or width otherwise
  114. if wib then
  115. if wib.position == "left" or wib.position == "right" then
  116. tot = wib.width
  117. else
  118. tot = wib.height
  119. end
  120. end
  121. local int = math.modf((pulsebar._current_level / 100) * tot)
  122. preset.text = string.format(
  123. "%s%s%s%s",
  124. tick_pre,
  125. string.rep(tick, int),
  126. string.rep(tick_none, tot - int),
  127. tick_post
  128. )
  129. if pulsebar.followtag then preset.screen = awful.screen.focused() end
  130. if not pulsebar.notification then
  131. pulsebar.notification = naughty.notify {
  132. preset = preset,
  133. destroy = function() pulsebar.notification = nil end
  134. }
  135. else
  136. naughty.replace_text(pulsebar.notification, preset.title, preset.text)
  137. end
  138. end)
  139. end
  140. helpers.newtimer(string.format("pulsebar-%s-%s", pulsebar.devicetype, pulsebar.device), timeout, pulsebar.update)
  141. return pulsebar
  142. end
  143. return factory