summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--experiments/volume/utils.lua107
-rw-r--r--experiments/volume/volume-2.svg1
-rw-r--r--experiments/volume/volume.lua172
3 files changed, 280 insertions, 0 deletions
diff --git a/experiments/volume/utils.lua b/experiments/volume/utils.lua
new file mode 100644
index 0000000..02742ec
--- /dev/null
+++ b/experiments/volume/utils.lua
@@ -0,0 +1,107 @@
+
+local json = require("json")
+
+local utils = {}
+
+local function split(string_to_split, separator)
+ if separator == nil then separator = "%s" end
+ local t = {}
+
+ for str in string.gmatch(string_to_split, "([^".. separator .."]+)") do
+ table.insert(t, str)
+ end
+
+ return t
+end
+
+function utils.extract_sinks_and_sources(pacmd_output)
+ local sinks = {}
+ local sources = {}
+ local device
+ local properties
+ local ports
+ local in_sink = false
+ local in_source = false
+ local in_device = false
+ local in_properties = false
+ local in_ports = false
+ for line in pacmd_output:gmatch("[^\r\n]+") do
+ if string.match(line, 'source%(s%) available.') then
+ in_sink = false
+ in_source = true
+ end
+ if string.match(line, 'sink%(s%) available.') then
+ in_sink = true
+ in_source = false
+ end
+
+ if string.match(line, 'index:') then
+ in_device = true
+ in_properties = false
+ device = {
+ id = line:match(': (%d+)'),
+ is_default = string.match(line, '*') ~= nil
+ }
+ if in_sink then
+ table.insert(sinks, device)
+ elseif in_source then
+ table.insert(sources, device)
+ end
+ end
+
+ if string.match(line, '^\tproperties:') then
+ in_device = false
+ in_properties = true
+ properties = {}
+ device['properties'] = properties
+ end
+
+ if string.match(line, 'ports:') then
+ in_device = false
+ in_properties = false
+ in_ports = true
+ ports = {}
+ device['ports'] = ports
+ end
+
+ if string.match(line, 'active port:') then
+ in_device = false
+ in_properties = false
+ in_ports = false
+ device['active_port'] = line:match(': (.+)'):gsub('<',''):gsub('>','')
+ end
+
+ if in_device then
+ local t = split(line, ': ')
+ local key = t[1]:gsub('\t+', ''):lower()
+ local value = t[2]:gsub('^<', ''):gsub('>$', '')
+ device[key] = value
+ end
+
+ if in_properties then
+ local t = split(line, '=')
+ local key = t[1]:gsub('\t+', ''):gsub('%.', '_'):gsub('-', '_'):gsub(':', ''):gsub("%s+$", "")
+ local value
+ if t[2] == nil then
+ value = t[2]
+ else
+ value = t[2]:gsub('"', ''):gsub("^%s+", ""):gsub(' Analog Stereo', '')
+ end
+ properties[key] = value
+ end
+
+ if in_ports then
+ local t = split(line, ': ')
+ local key = t[1]
+ if key ~= nil then
+ key = key:gsub('\t+', '')
+ end
+ ports[key] = t[2]
+ end
+ end
+ print(json.encode(sources))
+
+ return sinks, sources
+end
+
+return utils \ No newline at end of file
diff --git a/experiments/volume/volume-2.svg b/experiments/volume/volume-2.svg
new file mode 100644
index 0000000..10f1c67
--- /dev/null
+++ b/experiments/volume/volume-2.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-volume-2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> \ No newline at end of file
diff --git a/experiments/volume/volume.lua b/experiments/volume/volume.lua
new file mode 100644
index 0000000..beaa119
--- /dev/null
+++ b/experiments/volume/volume.lua
@@ -0,0 +1,172 @@
+-------------------------------------------------
+-- The Ultimate Volume Widget for Awesome Window Manager
+-- More details could be found here:
+-- https://github.com/streetturtle/awesome-wm-widgets/tree/master/volume-widget
+
+-- @author Pavel Makhov
+-- @copyright 2020 Pavel Makhov
+-------------------------------------------------
+
+local awful = require("awful")
+local wibox = require("wibox")
+local spawn = require("awful.spawn")
+local gears = require("gears")
+local beautiful = require("beautiful")
+local utils = require("awesome-wm-widgets.experiments.volume.utils")
+
+
+local HOME_DIR = os.getenv("HOME")
+local WIDGET_DIR = HOME_DIR .. '/.config/awesome/awesome-wm-widgets/experiments/volume'
+local LIST_DEVICES_CMD = [[sh -c "pacmd list-sinks; pacmd list-sources"]]
+
+
+local rows = { layout = wibox.layout.fixed.vertical }
+local volume_widget = wibox.widget {
+ {
+ {
+ id = "icon",
+ image = WIDGET_DIR .. '/volume-2.svg',
+ widget = wibox.widget.imagebox
+ },
+ id = "margin",
+ margins = 4,
+ layout = wibox.container.margin
+ },
+ {
+ id = "txt",
+ widget = wibox.widget.textbox
+ },
+ layout = wibox.layout.fixed.horizontal,
+ set_text = function(self, new_value)
+ self.txt.text = new_value
+ end,
+ set_icon = function(self, new_value)
+ self.margin.icon.image = new_value
+ end
+}
+
+
+local popup = awful.popup{
+ bg = beautiful.bg_normal,
+ ontop = true,
+ visible = false,
+ shape = gears.shape.rounded_rect,
+ border_width = 1,
+ border_color = beautiful.bg_focus,
+ maximum_width = 400,
+ offset = { y = 5 },
+ widget = {}
+}
+
+local function build_main_line(device)
+ if device.active_port ~= nil and device.ports[device.active_port] ~= nil then
+ return device.properties.device_description .. ' - ' .. device.ports[device.active_port]
+ else
+ return device.properties.device_description
+ end
+end
+
+local function build_rows(devices, on_checkbox_click, device_type)
+ local device_rows = { layout = wibox.layout.fixed.vertical }
+ for _, device in pairs(devices) do
+
+ local checkbox = wibox.widget {
+ checked = device.is_default,
+ color = beautiful.bg_normal,
+ paddings = 2,
+ shape = gears.shape.circle,
+ forced_width = 20,
+ forced_height = 20,
+ check_color = beautiful.fg_urgent,
+ widget = wibox.widget.checkbox
+ }
+
+ checkbox:connect_signal("button::press", function(c)
+ print(string.format([[sh -c 'pacmd set-default-%s "%s"']], device_type, device.name))
+ spawn.easy_async(string.format([[sh -c 'pacmd set-default-%s "%s"']], device_type, device.name), function()
+ on_checkbox_click()
+ end)
+ end)
+
+ local row = wibox.widget {
+ {
+ {
+ {
+ checkbox,
+ valign = 'center',
+ layout = wibox.container.place,
+ },
+ {
+ {
+ text = build_main_line(device),
+ align = 'left',
+ widget = wibox.widget.textbox
+ },
+ left = 10,
+ layout = wibox.container.margin
+ },
+ spacing = 8,
+ layout = wibox.layout.align.horizontal
+ },
+ margins = 8,
+ layout = wibox.container.margin
+ },
+ bg = beautiful.bg_normal,
+ widget = wibox.container.background
+ }
+
+ table.insert(device_rows, row)
+ end
+
+ return device_rows
+end
+
+local function build_header_row(text)
+ return wibox.widget{
+ {
+ markup = "<b>" .. text .. "</b>",
+ align = 'center',
+ widget = wibox.widget.textbox
+ },
+ bg = beautiful.bg_normal,
+ widget = wibox.container.background
+ }
+end
+
+local function rebuild_popup()
+ spawn.easy_async(LIST_DEVICES_CMD, function(stdout)
+
+ local sinks, sources = utils.extract_sinks_and_sources(stdout)
+
+ for i = 0, #rows do rows[i]=nil end
+
+ table.insert(rows, build_header_row("SINKS"))
+ table.insert(rows, build_rows(sinks, function() rebuild_popup() end, "sink"))
+ table.insert(rows, build_header_row("SOURCES"))
+ table.insert(rows, build_rows(sources, function() rebuild_popup() end, "source"))
+
+ popup:setup(rows)
+ end)
+
+end
+
+
+local function worker(args)
+
+ volume_widget:buttons(
+ awful.util.table.join(
+ awful.button({}, 1, function()
+ if popup.visible then
+ popup.visible = not popup.visible
+ else
+ rebuild_popup()
+ popup:move_next_to(mouse.current_widget_geometry)
+ end
+ end)
+ )
+ )
+
+ return volume_widget
+end
+
+return setmetatable(volume_widget, { __call = function(_, ...) return worker(...) end })