diff --git a/awesome/components/pastel/top-panel.lua b/awesome/components/pastel/top-panel.lua
index 17faa60..465c97c 100644
--- a/awesome/components/pastel/top-panel.lua
+++ b/awesome/components/pastel/top-panel.lua
@@ -16,6 +16,8 @@ local wibox = require("wibox")
local theme = require("theme")
local lain = require("lain")
local dpi = beautiful.xresources.apply_dpi
+local battery_widget = require("widgets.battery-widget.battery")
+local volume_widget = require("widgets.volume-widget.volume")
-- import widgets
local task_list = require("widgets.task-list")
@@ -67,9 +69,17 @@ top_panel.create = function(s)
{
layout = wibox.layout.fixed.horizontal,
wibox.layout.margin(wibox.widget.systray(), dpi(5), dpi(5), dpi(5), dpi(5)),
+ wibox.widget.textbox(' '),
+ volume_widget({widget_type='arc'}),
+ wibox.widget.textbox(' '),
+ battery_widget({font=theme.font, display_notification=true}),
wibox.widget.textbox(' | '),
updates_indicator,
wibox.widget.textbox(' | '),
+ awful.widget.watch('bash -c "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"', 5),
+ wibox.widget.textbox(' | '),
+ wibox.layout.margin(require("widgets.layout-box"), dpi(5), dpi(5), dpi(5), dpi(5)),
+ wibox.widget.textbox(' | '),
lain.widget.mem({
settings = function()
widget:set_markup(lain.util.markup.font(theme.font, mem_now.used .. " MB"))
@@ -85,10 +95,7 @@ top_panel.create = function(s)
awful.widget.watch('bash -c "/usr/bin/cat /proc/cpuinfo | grep MHz | cut -b 12- | sort -r | head -n 1 | xargs printf \'%f / 1000\n\' | bc -l | cut -b -4"', 1),
wibox.widget.textbox('GHz | '),
awful.widget.watch('bash -c "sensors | grep Package | cut -f 5 -d \' \' | cut -c 2-"', 5),
- wibox.widget.textbox(' | '),
- awful.widget.watch('bash -c "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"', 5),
- wibox.widget.textbox(' | '),
- wibox.layout.margin(require("widgets.layout-box"), dpi(5), dpi(5), dpi(5), dpi(5)),
+ wibox.widget.textbox(' '),
}
}
diff --git a/awesome/components/volume-adjust.lua b/awesome/components/volume-adjust.lua
deleted file mode 100644
index e456e5c..0000000
--- a/awesome/components/volume-adjust.lua
+++ /dev/null
@@ -1,95 +0,0 @@
--- ██╗ ██╗ ██████╗ ██╗ ██╗ ██╗███╗ ███╗███████╗
--- ██║ ██║██╔═══██╗██║ ██║ ██║████╗ ████║██╔════╝
--- ██║ ██║██║ ██║██║ ██║ ██║██╔████╔██║█████╗
--- ╚██╗ ██╔╝██║ ██║██║ ██║ ██║██║╚██╔╝██║██╔══╝
--- ╚████╔╝ ╚██████╔╝███████╗╚██████╔╝██║ ╚═╝ ██║███████╗
--- ╚═══╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
-
-
--- ===================================================================
--- Initialization
--- ===================================================================
-
-
-local wibox = require("wibox")
-local awful = require("awful")
-local gears = require("gears")
-local beautiful = require("beautiful")
-local dpi = beautiful.xresources.apply_dpi
-
-local offsetx = dpi(56)
-local offsety = dpi(300)
-local screen = awful.screen.focused()
-local icon_dir = gears.filesystem.get_configuration_dir() .. "/icons/volume/" .. beautiful.name .. "/"
-
-
--- ===================================================================
--- Appearance & Functionality
--- ===================================================================
-
-
--- create the volume_adjust component
-local volume_adjust = wibox({
- screen = awful.screen.focused(),
- x = screen.geometry.width - offsetx,
- y = (screen.geometry.height / 2) - (offsety / 2),
- width = dpi(48),
- height = offsety,
- shape = gears.shape.rounded_rect,
- visible = false,
- ontop = true
-})
-
-local volume_bar = wibox.widget{
- widget = wibox.widget.progressbar,
- shape = gears.shape.rounded_bar,
- color = "#efefef",
- background_color = beautiful.bg_focus,
- max_value = 100,
- value = 0
-}
-
-volume_adjust:setup {
- layout = wibox.layout.align.vertical,
- {
- wibox.container.margin(
- volume_bar, dpi(14), dpi(20), dpi(20), dpi(20)
- ),
- forced_height = offsety,
- direction = "east",
- layout = wibox.container.rotate
- },
-}
-
--- create a 4 second timer to hide the volume adjust
--- component whenever the timer is started
-local hide_volume_adjust = gears.timer {
- timeout = 4,
- autostart = true,
- callback = function()
- volume_adjust.visible = false
- end
-}
-
--- show volume-adjust when "volume_change" signal is emitted
-awesome.connect_signal("volume_change",
- function()
- -- set new volume value
- awful.spawn.easy_async_with_shell(
- "amixer sget Master | grep 'Right:' | awk -F '[][]' '{print $2}'| sed 's/[^0-9]//g'",
- function(stdout)
- local volume_level = tonumber(stdout)
- volume_bar.value = volume_level
- end,
- false
- )
-
- -- make volume_adjust component visible
- if volume_adjust.visible then
- hide_volume_adjust:again()
- else
- volume_adjust.visible = true
- hide_volume_adjust:start()
- end
- end
-)
diff --git a/awesome/keys.lua b/awesome/keys.lua
index 64c7b8e..c503cba 100644
--- a/awesome/keys.lua
+++ b/awesome/keys.lua
@@ -15,6 +15,7 @@ local awful = require("awful")
local gears = require("gears")
local naughty = require("naughty")
local beautiful = require("beautiful")
+local volume_widget = require("widgets.volume-widget.volume")
local dpi = beautiful.xresources.apply_dpi
-- Define mod keys
@@ -188,22 +189,19 @@ keys.globalkeys = gears.table.join(
-- ALSA volume control
awful.key({}, "XF86AudioRaiseVolume",
function()
- awful.spawn("amixer -D pulse sset Master 5%+", false)
- awesome.emit_signal("volume_change")
+ volume_widget:inc(5)
end,
{description = "volume up", group = "hotkeys"}
),
awful.key({}, "XF86AudioLowerVolume",
function()
- awful.spawn("amixer -D pulse sset Master 5%-", false)
- awesome.emit_signal("volume_change")
+ volume_widget:dec(5)
end,
{description = "volume down", group = "hotkeys"}
),
awful.key({}, "XF86AudioMute",
function()
- awful.spawn("amixer -D pulse set Master 1+ toggle", false)
- awesome.emit_signal("volume_change")
+ volume_widget:toggle()
end,
{description = "toggle mute", group = "hotkeys"}
),
diff --git a/awesome/pastel.lua b/awesome/pastel.lua
index 3fa5a61..b223082 100644
--- a/awesome/pastel.lua
+++ b/awesome/pastel.lua
@@ -27,7 +27,6 @@ pastel.initialize = function()
-- Import components
require("components.pastel.wallpaper")
require("components.exit-screen")
- require("components.volume-adjust")
-- Import panels
local top_panel = require("components.pastel.top-panel")
diff --git a/awesome/rc.lua b/awesome/rc.lua
index e03f192..288cc18 100644
--- a/awesome/rc.lua
+++ b/awesome/rc.lua
@@ -38,7 +38,8 @@ network_interfaces = {
-- List of apps to run on start-up
local run_on_start_up = {
"numlockx on",
- "bluetoothctl power on"
+ "bluetoothctl power on",
+ "nm-applet",
}
-- ===================================================================
diff --git a/awesome/theme.lua b/awesome/theme.lua
index 103274b..bbe3edf 100644
--- a/awesome/theme.lua
+++ b/awesome/theme.lua
@@ -1,4 +1,4 @@
--- ████████╗██╗ ██╗███████╗███╗ ███╗███████╗
+-- ████████╗██╗ ██╗███████╗███╗ ███╗███████╗theme
-- ╚══██╔══╝██║ ██║██╔════╝████╗ ████║██╔════╝
-- ██║ ███████║█████╗ ██╔████╔██║█████╗
-- ██║ ██╔══██║██╔══╝ ██║╚██╔╝██║██╔══╝
@@ -95,7 +95,7 @@ theme.titlebars_enabled = false
-- theme.layout_floating = "~/.config/awesome/icons/layouts/floating.png"
-- theme.layout_max = "~/.config/awesome/icons/layouts/maximized.png"
-theme.icon_theme = "Tela-dark"
+theme.icon_theme = "Papirus"
-- return theme
return theme
diff --git a/awesome/widgets/battery-widget/README.md b/awesome/widgets/battery-widget/README.md
new file mode 100644
index 0000000..b15aac6
--- /dev/null
+++ b/awesome/widgets/battery-widget/README.md
@@ -0,0 +1,75 @@
+# Battery widget
+
+Simple and easy-to-install widget for Awesome Window Manager.
+
+This widget consists of:
+
+ - an icon which shows the battery level:
+ 
+ - a pop-up window, which shows up when you hover over an icon:
+ 
+ Alternatively you can use a tooltip (check the code):
+ 
+ - a pop-up warning message which appears on bottom right corner when battery level is less that 15% (you can get the image [here](https://vk.com/images/stickers/1933/512.png)):
+ 
+
+Note that widget uses the Arc icon theme, so it should be [installed](https://github.com/horst3180/arc-icon-theme#installation) first under **/usr/share/icons/Arc/** folder.
+
+## Customization
+
+It is possible to customize widget by providing a table with all or some of the following config parameters:
+
+| Name | Default | Description |
+|---|---|---|
+| `font` | Play 8 | Fond |
+| `path_to_icons` | `/usr/share/icons/Arc/status/symbolic/` | Path to the folder with icons* |
+| `show_current_level`| false | Show current charge level |
+| `margin_right`|0| The right margin of the widget|
+| `margin_left`|0| The left margin of the widget|
+| `display_notification` | `false` | Display a notification on mouseover |
+| `notification_position` | `top_right` | The notification position |
+| `timeout` | 10 | How often in seconds the widget refreshes |
+| `warning_msg_title` | _Huston, we have a problem_ | Title of the warning popup |
+| `warning_msg_text` | _Battery is dying_ | Text of the warning popup |
+| `warning_msg_position` | `bottom_right` | Position of the warning popup |
+| `warning_msg_icon` | ~/.config/awesome/awesome-wm-widgets/battery-widget/spaceman.jpg | Icon of the warning popup |
+| `enable_battery_warning` | `true` | Display low battery warning |
+
+*Note: the widget expects following icons to be present in the folder:
+
+ - battery-caution-charging-symbolic.svg
+ - battery-empty-charging-symbolic.svg
+ - battery-full-charged-symbolic.svg
+ - battery-full-symbolic.svg
+ - battery-good-symbolic.svg
+ - battery-low-symbolic.svg
+ - battery-caution-symbolic.svg
+ - battery-empty-symbolic.svg
+ - battery-full-charging-symbolic.svg
+ - battery-good-charging-symbolic.svg
+ - battery-low-charging-symbolic.svg
+ - battery-missing-symbolic.svg
+
+## Installation
+
+This widget reads the output of acpi tool.
+
+- install `acpi` and check the output:
+
+```bash
+$ sudo apt-get install acpi
+$ acpi
+Battery 0: Discharging, 66%, 02:34:06 remaining
+```
+
+```lua
+local battery_widget = require("awesome-wm-widgets.battery-widget.battery")
+
+...
+s.mytasklist, -- Middle widget
+ { -- Right widgets
+ layout = wibox.layout.fixed.horizontal,
+ ...
+ battery_widget(),
+ ...
+```
diff --git a/awesome/widgets/battery-widget/bat-wid-1.png b/awesome/widgets/battery-widget/bat-wid-1.png
new file mode 100644
index 0000000..00e1618
Binary files /dev/null and b/awesome/widgets/battery-widget/bat-wid-1.png differ
diff --git a/awesome/widgets/battery-widget/bat-wid-2.png b/awesome/widgets/battery-widget/bat-wid-2.png
new file mode 100644
index 0000000..ae20af2
Binary files /dev/null and b/awesome/widgets/battery-widget/bat-wid-2.png differ
diff --git a/awesome/widgets/battery-widget/bat-wid-22.png b/awesome/widgets/battery-widget/bat-wid-22.png
new file mode 100644
index 0000000..38761f7
Binary files /dev/null and b/awesome/widgets/battery-widget/bat-wid-22.png differ
diff --git a/awesome/widgets/battery-widget/bat-wid-3.png b/awesome/widgets/battery-widget/bat-wid-3.png
new file mode 100644
index 0000000..352b496
Binary files /dev/null and b/awesome/widgets/battery-widget/bat-wid-3.png differ
diff --git a/awesome/widgets/battery-widget/battery.lua b/awesome/widgets/battery-widget/battery.lua
new file mode 100644
index 0000000..452d7ef
--- /dev/null
+++ b/awesome/widgets/battery-widget/battery.lua
@@ -0,0 +1,200 @@
+-------------------------------------------------
+-- Battery Widget for Awesome Window Manager
+-- Shows the battery status using the ACPI tool
+-- More details could be found here:
+-- https://github.com/streetturtle/awesome-wm-widgets/tree/master/battery-widget
+
+-- @author Pavel Makhov
+-- @copyright 2017 Pavel Makhov
+-------------------------------------------------
+
+local awful = require("awful")
+local naughty = require("naughty")
+local watch = require("awful.widget.watch")
+local wibox = require("wibox")
+local gfs = require("gears.filesystem")
+local dpi = require('beautiful').xresources.apply_dpi
+
+-- acpi sample outputs
+-- Battery 0: Discharging, 75%, 01:51:38 remaining
+-- Battery 0: Charging, 53%, 00:57:43 until charged
+
+local HOME = os.getenv("HOME")
+local WIDGET_DIR = HOME .. '/.config/awesome/awesome-wm-widgets/battery-widget'
+
+local battery_widget = {}
+local function worker(user_args)
+ local args = user_args or {}
+
+ local font = args.font or 'Play 8'
+ local path_to_icons = args.path_to_icons or "/usr/share/icons/Arc/status/symbolic/"
+ local show_current_level = args.show_current_level or false
+ local margin_left = args.margin_left or 0
+ local margin_right = args.margin_right or 0
+
+ local display_notification = args.display_notification or false
+ local display_notification_onClick = args.display_notification_onClick or true
+ local position = args.notification_position or "top_right"
+ local timeout = args.timeout or 10
+
+ local warning_msg_title = args.warning_msg_title or 'Huston, we have a problem'
+ local warning_msg_text = args.warning_msg_text or 'Battery is dying'
+ local warning_msg_position = args.warning_msg_position or 'bottom_right'
+ local warning_msg_icon = args.warning_msg_icon or WIDGET_DIR .. '/spaceman.jpg'
+ local enable_battery_warning = args.enable_battery_warning
+ if enable_battery_warning == nil then
+ enable_battery_warning = true
+ end
+
+ if not gfs.dir_readable(path_to_icons) then
+ naughty.notify{
+ title = "Battery Widget",
+ text = "Folder with icons doesn't exist: " .. path_to_icons,
+ preset = naughty.config.presets.critical
+ }
+ end
+
+ local icon_widget = wibox.widget {
+ {
+ id = "icon",
+ widget = wibox.widget.imagebox,
+ resize = false
+ },
+ valign = 'center',
+ layout = wibox.container.place,
+ }
+ local level_widget = wibox.widget {
+ font = font,
+ widget = wibox.widget.textbox
+ }
+
+ battery_widget = wibox.widget {
+ icon_widget,
+ level_widget,
+ layout = wibox.layout.fixed.horizontal,
+ }
+ -- Popup with battery info
+ -- One way of creating a pop-up notification - naughty.notify
+ local notification
+ local function show_battery_status(batteryType)
+ awful.spawn.easy_async([[bash -c 'acpi']],
+ function(stdout, _, _, _)
+ naughty.destroy(notification)
+ notification = naughty.notify{
+ text = stdout,
+ title = "Battery status",
+ icon = path_to_icons .. batteryType .. ".svg",
+ icon_size = dpi(16),
+ position = position,
+ timeout = 5, hover_timeout = 0.5,
+ width = 200,
+ screen = mouse.screen
+ }
+ end
+ )
+ end
+
+ -- Alternative to naughty.notify - tooltip. You can compare both and choose the preferred one
+ --battery_popup = awful.tooltip({objects = {battery_widget}})
+
+ -- To use colors from beautiful theme put
+ -- following lines in rc.lua before require("battery"):
+ -- beautiful.tooltip_fg = beautiful.fg_normal
+ -- beautiful.tooltip_bg = beautiful.bg_normal
+
+ local function show_battery_warning()
+ naughty.notify {
+ icon = warning_msg_icon,
+ icon_size = 100,
+ text = warning_msg_text,
+ title = warning_msg_title,
+ timeout = 25, -- show the warning for a longer time
+ hover_timeout = 0.5,
+ position = warning_msg_position,
+ bg = "#F06060",
+ fg = "#EEE9EF",
+ width = 300,
+ screen = mouse.screen
+ }
+ end
+ local last_battery_check = os.time()
+ local batteryType = "battery-good-symbolic"
+
+ watch("acpi -i", timeout,
+ function(widget, stdout)
+ local battery_info = {}
+ local capacities = {}
+ for s in stdout:gmatch("[^\r\n]+") do
+ local status, charge_str, _ = string.match(s, '.+: ([%a%s]+), (%d?%d?%d)%%,?(.*)')
+ if status ~= nil then
+ table.insert(battery_info, {status = status, charge = tonumber(charge_str)})
+ else
+ local cap_str = string.match(s, '.+:.+last full capacity (%d+)')
+ table.insert(capacities, tonumber(cap_str))
+ end
+ end
+
+ local capacity = 0
+ for _, cap in ipairs(capacities) do
+ capacity = capacity + cap
+ end
+
+ local charge = 0
+ local status
+ for i, batt in ipairs(battery_info) do
+ if capacities[i] ~= nil then
+ if batt.charge >= charge then
+ status = batt.status -- use most charged battery status
+ -- this is arbitrary, and maybe another metric should be used
+ end
+
+ charge = charge + batt.charge * capacities[i]
+ end
+ end
+ charge = charge / capacity
+
+ if show_current_level then
+ level_widget.text = string.format('%d%%', charge)
+ end
+
+ if (charge >= 1 and charge < 15) then
+ batteryType = "battery-empty%s-symbolic"
+ if enable_battery_warning and status ~= 'Charging' and os.difftime(os.time(), last_battery_check) > 300 then
+ -- if 5 minutes have elapsed since the last warning
+ last_battery_check = os.time()
+
+ show_battery_warning()
+ end
+ elseif (charge >= 15 and charge < 40) then batteryType = "battery-caution%s-symbolic"
+ elseif (charge >= 40 and charge < 60) then batteryType = "battery-low%s-symbolic"
+ elseif (charge >= 60 and charge < 80) then batteryType = "battery-good%s-symbolic"
+ elseif (charge >= 80 and charge <= 100) then batteryType = "battery-full%s-symbolic"
+ end
+
+ if status == 'Charging' then
+ batteryType = string.format(batteryType, '-charging')
+ else
+ batteryType = string.format(batteryType, '')
+ end
+
+ widget.icon:set_image(path_to_icons .. batteryType .. ".svg")
+
+ -- Update popup text
+ -- battery_popup.text = string.gsub(stdout, "\n$", "")
+ end,
+ icon_widget)
+
+ if display_notification then
+ battery_widget:connect_signal("mouse::enter", function() show_battery_status(batteryType) end)
+ battery_widget:connect_signal("mouse::leave", function() naughty.destroy(notification) end)
+ elseif display_notification_onClick then
+ battery_widget:connect_signal("button::press", function(_,_,_,button)
+ if (button == 3) then show_battery_status(batteryType) end
+ end)
+ battery_widget:connect_signal("mouse::leave", function() naughty.destroy(notification) end)
+ end
+
+ return wibox.container.margin(battery_widget, margin_left, margin_right)
+end
+
+return setmetatable(battery_widget, { __call = function(_, ...) return worker(...) end })
diff --git a/awesome/widgets/battery-widget/spaceman.jpg b/awesome/widgets/battery-widget/spaceman.jpg
new file mode 100644
index 0000000..73ddaf3
Binary files /dev/null and b/awesome/widgets/battery-widget/spaceman.jpg differ
diff --git a/awesome/widgets/volume-widget/README.md b/awesome/widgets/volume-widget/README.md
new file mode 100644
index 0000000..4fc7f55
--- /dev/null
+++ b/awesome/widgets/volume-widget/README.md
@@ -0,0 +1,119 @@
+# Volume widget
+
+Volume widget based on [amixer](https://linux.die.net/man/1/amixer) (is used for controlling the audio volume) and [pacmd](https://linux.die.net/man/1/pacmd) (is used for selecting a sink/source). Also, the widget provides an easy way to customize how it looks, following types are supported out-of-the-box:
+
+
+
+From left to right: `horizontal_bar`, `vertical_bar`, `icon`, `icon_and_text`, `arc`
+
+A right-click on the widget opens a popup where you can choose a sink/source:
+
+
+Left click toggles mute and middle click opens a mixer ([pavucontrol](https://freedesktop.org/software/pulseaudio/pavucontrol/) by default).
+
+### Features
+
+ - switch between sinks/sources by right click on the widget;
+ - more responsive than previous versions of volume widget, which were refreshed once a second;
+ - 5 predefined customizable looks;
+
+## Installation
+
+Clone the repo under **~/.config/awesome/** and add widget in **rc.lua**:
+
+```lua
+local volume_widget = require('awesome-wm-widgets.volume-widget.volume')
+...
+s.mytasklist, -- Middle widget
+ { -- Right widgets
+ layout = wibox.layout.fixed.horizontal,
+ ...
+ -- default
+ volume_widget(),
+ -- customized
+ volume_widget{
+ widget_type = 'arc'
+ },
+```
+
+Note that widget uses following command the get the current volume: `amixer -D pulse sget Master`, so please make sure that it works for you, otherwise you need to set parameter `device = 'default'`.
+
+### Shortcuts
+
+To improve responsiveness of the widget when volume level is changed by a shortcut use corresponding methods of the widget:
+
+```lua
+awful.key({ modkey }, "]", function() volume_widget:inc(5) end),
+awful.key({ modkey }, "[", function() volume_widget:dec(5) end),
+awful.key({ modkey }, "\\", function() volume_widget:toggle() end),
+```
+
+## Customization
+
+It is possible to customize the widget by providing a table with all or some of the following config parameters:
+
+### Generic parameter
+
+| Name | Default | Description |
+|---|---|---|
+| `mixer_cmd` | `pavucontrol` | command to run on middle click (e.g. a mixer program) |
+| `step` | `5` | How much the volume is raised or lowered at once (in %) |
+| `widget_type`| `icon_and_text`| Widget type, one of `horizontal_bar`, `vertical_bar`, `icon`, `icon_and_text`, `arc` |
+| `device` | `pulse` | Select the device name to control |
+
+Depends on the chosen widget type add parameters from the corresponding section below:
+
+#### `icon` parameters
+
+| Name | Default | Description |
+|---|---|---|
+| `icon_dir`| `./icons`| Path to the folder with icons |
+
+_Note:_ if you are changing icons, the folder should contain following .svg images:
+ - audio-volume-high-symbolic
+ - audio-volume-medium-symbolic
+ - audio-volume-low-symbolic
+ - audio-volume-muted-symbolic
+
+#### `icon_and_text` parameters
+
+| Name | Default | Description |
+|---|---|---|
+| `icon_dir`| `./icons`| Path to the folder with icons |
+| `font` | `beautiful.font` | Font name and size, like `Play 12` |
+
+#### `arc` parameters
+
+| Name | Default | Description |
+|---|---|---|
+| `thickness` | 2 | Thickness of the arc |
+| `main_color` | `beautiful.fg_color` | Color of the arc |
+| `bg_color` | `#ffffff11` | Color of the arc's background |
+| `mute_color` | `beautiful.fg_urgent` | Color of the arc when mute |
+| `size` | 18 | Size of the widget |
+
+#### `horizontal_bar` parameters
+
+| Name | Default | Description |
+|---|---|---|
+| `main_color` | `beautiful.fg_normal` | Color of the bar |
+| `mute_color` | `beautiful.fg_urgent` | Color of the bar when mute |
+| `bg_color` | `'#ffffff11'` | Color of the bar's background |
+| `width` | `50` | The bar width |
+| `margins` | `10` | Top and bottom margins (if your wibar is 22 px high, bar will be 2 px = 22 - 2*10) |
+| `shape` | `'bar'` | [gears.shape](https://awesomewm.org/doc/api/libraries/gears.shape.html), could be `octogon`, `hexagon`, `powerline`, etc |
+| `with_icon` | `true` | Show volume icon|
+
+_Note:_ I didn't figure out how does the `forced_height` property of progressbar widget work (maybe it doesn't work at all), thus there is a workaround with margins.
+
+#### `vertical_bar` parameters
+
+| Name | Default | Description |
+|---|---|---|
+| `main_color` | `beautiful.fg_normal` | Color of the bar |
+| `mute_color` | `beautiful.fg_urgent` | Color of the bar when mute |
+| `bg_color` | `'#ffffff11'` | Color of the bar's background |
+| `width` | `10` | The bar width |
+| `margins` | `20` | Top and bottom margins (if your wibar is 22 px high, bar will be 2 px = 22 - 2*10) |
+| `shape` | `'bar'` | [gears.shape](https://awesomewm.org/doc/api/libraries/gears.shape.html), could be `octogon`, `hexagon`, `powerline`, etc |
+| `with_icon` | `true` | Show volume icon|
diff --git a/awesome/widgets/volume-widget/icons/audio-volume-high-symbolic.svg b/awesome/widgets/volume-widget/icons/audio-volume-high-symbolic.svg
new file mode 100644
index 0000000..985c107
--- /dev/null
+++ b/awesome/widgets/volume-widget/icons/audio-volume-high-symbolic.svg
@@ -0,0 +1,88 @@
+
+
diff --git a/awesome/widgets/volume-widget/icons/audio-volume-low-symbolic.svg b/awesome/widgets/volume-widget/icons/audio-volume-low-symbolic.svg
new file mode 100644
index 0000000..7eb4531
--- /dev/null
+++ b/awesome/widgets/volume-widget/icons/audio-volume-low-symbolic.svg
@@ -0,0 +1,88 @@
+
+
diff --git a/awesome/widgets/volume-widget/icons/audio-volume-medium-symbolic.svg b/awesome/widgets/volume-widget/icons/audio-volume-medium-symbolic.svg
new file mode 100644
index 0000000..11e44fe
--- /dev/null
+++ b/awesome/widgets/volume-widget/icons/audio-volume-medium-symbolic.svg
@@ -0,0 +1,88 @@
+
+
diff --git a/awesome/widgets/volume-widget/icons/audio-volume-muted-symbolic.svg b/awesome/widgets/volume-widget/icons/audio-volume-muted-symbolic.svg
new file mode 100644
index 0000000..e577d05
--- /dev/null
+++ b/awesome/widgets/volume-widget/icons/audio-volume-muted-symbolic.svg
@@ -0,0 +1,88 @@
+
+
diff --git a/awesome/widgets/volume-widget/screenshots/variations.png b/awesome/widgets/volume-widget/screenshots/variations.png
new file mode 100644
index 0000000..21d7ead
Binary files /dev/null and b/awesome/widgets/volume-widget/screenshots/variations.png differ
diff --git a/awesome/widgets/volume-widget/screenshots/volume-sink-sources.png b/awesome/widgets/volume-widget/screenshots/volume-sink-sources.png
new file mode 100644
index 0000000..7d010bc
Binary files /dev/null and b/awesome/widgets/volume-widget/screenshots/volume-sink-sources.png differ
diff --git a/awesome/widgets/volume-widget/utils.lua b/awesome/widgets/volume-widget/utils.lua
new file mode 100644
index 0000000..417a666
--- /dev/null
+++ b/awesome/widgets/volume-widget/utils.lua
@@ -0,0 +1,105 @@
+
+
+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
+
+ return sinks, sources
+end
+
+return utils
\ No newline at end of file
diff --git a/awesome/widgets/volume-widget/volume-2.svg b/awesome/widgets/volume-widget/volume-2.svg
new file mode 100644
index 0000000..10f1c67
--- /dev/null
+++ b/awesome/widgets/volume-widget/volume-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/awesome/widgets/volume-widget/volume.lua b/awesome/widgets/volume-widget/volume.lua
new file mode 100644
index 0000000..2561eb9
--- /dev/null
+++ b/awesome/widgets/volume-widget/volume.lua
@@ -0,0 +1,228 @@
+-------------------------------------------------
+-- The Ultimate Volume Widget for Awesome Window Manager
+-- More details could be found here:
+-- https://github.com/streetturtle/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 watch = require("awful.widget.watch")
+local utils = require("widgets.volume-widget.utils")
+
+
+local LIST_DEVICES_CMD = [[sh -c "pacmd list-sinks; pacmd list-sources"]]
+local function GET_VOLUME_CMD(device) return 'amixer -D ' .. device .. ' sget Master' end
+local function INC_VOLUME_CMD(device, step) return 'amixer -D ' .. device .. ' sset Master ' .. step .. '%+' end
+local function DEC_VOLUME_CMD(device, step) return 'amixer -D ' .. device .. ' sset Master ' .. step .. '%-' end
+local function TOG_VOLUME_CMD(device) return 'amixer -D ' .. device .. ' sset Master toggle' end
+
+
+local widget_types = {
+ icon_and_text = require("widgets.volume-widget.widgets.icon-and-text-widget"),
+ icon = require("widgets.volume-widget.widgets.icon-widget"),
+ arc = require("widgets.volume-widget.widgets.arc-widget"),
+ horizontal_bar = require("widgets.volume-widget.widgets.horizontal-bar-widget"),
+ vertical_bar = require("widgets.volume-widget.widgets.vertical-bar-widget")
+}
+local volume = {}
+
+local rows = { layout = wibox.layout.fixed.vertical }
+
+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()
+ 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 = 4,
+ layout = wibox.container.margin
+ },
+ bg = beautiful.bg_normal,
+ widget = wibox.container.background
+ }
+
+ row:connect_signal("mouse::enter", function(c) c:set_bg(beautiful.bg_focus) end)
+ row:connect_signal("mouse::leave", function(c) c:set_bg(beautiful.bg_normal) end)
+
+ local old_cursor, old_wibox
+ row:connect_signal("mouse::enter", function()
+ local wb = mouse.current_wibox
+ old_cursor, old_wibox = wb.cursor, wb
+ wb.cursor = "hand1"
+ end)
+ row:connect_signal("mouse::leave", function()
+ if old_wibox then
+ old_wibox.cursor = old_cursor
+ old_wibox = nil
+ end
+ end)
+
+ row:connect_signal("button::press", function()
+ spawn.easy_async(string.format([[sh -c 'pacmd set-default-%s "%s"']], device_type, device.name), function()
+ on_checkbox_click()
+ end)
+ end)
+
+ table.insert(device_rows, row)
+ end
+
+ return device_rows
+end
+
+local function build_header_row(text)
+ return wibox.widget{
+ {
+ markup = "" .. text .. "",
+ 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(user_args)
+
+ local args = user_args or {}
+
+ local mixer_cmd = args.mixer_cmd or 'pavucontrol'
+ local widget_type = args.widget_type
+ local refresh_rate = args.refresh_rate or 1
+ local step = args.step or 5
+ local device = args.device or 'pulse'
+
+ if widget_types[widget_type] == nil then
+ volume.widget = widget_types['icon_and_text'].get_widget(args.icon_and_text_args)
+ else
+ volume.widget = widget_types[widget_type].get_widget(args)
+ end
+
+ local function update_graphic(widget, stdout)
+ local mute = string.match(stdout, "%[(o%D%D?)%]") -- \[(o\D\D?)\] - [on] or [off]
+ if mute == 'off' then widget:mute()
+ elseif mute == 'on' then widget:unmute()
+ end
+ local volume_level = string.match(stdout, "(%d?%d?%d)%%") -- (\d?\d?\d)\%)
+ volume_level = string.format("% 3d", volume_level)
+ widget:set_volume_level(volume_level)
+ end
+
+ function volume:inc(s)
+ spawn.easy_async(INC_VOLUME_CMD(device, s or step), function(stdout) update_graphic(volume.widget, stdout) end)
+ end
+
+ function volume:dec(s)
+ spawn.easy_async(DEC_VOLUME_CMD(device, s or step), function(stdout) update_graphic(volume.widget, stdout) end)
+ end
+
+ function volume:toggle()
+ spawn.easy_async(TOG_VOLUME_CMD(device), function(stdout) update_graphic(volume.widget, stdout) end)
+ end
+
+ function volume:mixer()
+ if mixer_cmd then
+ spawn.easy_async(mixer_cmd)
+ end
+ end
+
+ volume.widget:buttons(
+ awful.util.table.join(
+ awful.button({}, 3, function()
+ if popup.visible then
+ popup.visible = not popup.visible
+ else
+ rebuild_popup()
+ popup:move_next_to(mouse.current_widget_geometry)
+ end
+ end),
+ awful.button({}, 4, function() volume:inc() end),
+ awful.button({}, 5, function() volume:dec() end),
+ awful.button({}, 2, function() volume:mixer() end),
+ awful.button({}, 1, function() volume:toggle() end)
+ )
+ )
+
+ watch(GET_VOLUME_CMD(device), refresh_rate, update_graphic, volume.widget)
+
+ return volume.widget
+end
+
+return setmetatable(volume, { __call = function(_, ...) return worker(...) end })
diff --git a/awesome/widgets/volume-widget/widgets/arc-widget.lua b/awesome/widgets/volume-widget/widgets/arc-widget.lua
new file mode 100644
index 0000000..7ecef78
--- /dev/null
+++ b/awesome/widgets/volume-widget/widgets/arc-widget.lua
@@ -0,0 +1,46 @@
+local wibox = require("wibox")
+local beautiful = require('beautiful')
+
+local ICON_DIR = os.getenv("HOME") .. '/.config/awesome/widgets/volume-widget/icons/'
+
+local widget = {}
+
+function widget.get_widget(widgets_args)
+ local args = widgets_args or {}
+
+ local thickness = args.thickness or 2
+ local main_color = args.main_color or beautiful.fg_color
+ local bg_color = args.bg_color or '#ffffff11'
+ local mute_color = args.mute_color or beautiful.fg_urgent
+ local size = args.size or 18
+
+ return wibox.widget {
+ {
+ id = "icon",
+ image = ICON_DIR .. 'audio-volume-high-symbolic.svg',
+ resize = true,
+ widget = wibox.widget.imagebox,
+ },
+ max_value = 100,
+ thickness = thickness,
+ start_angle = 4.71238898, -- 2pi*3/4
+ forced_height = size,
+ forced_width = size,
+ bg = bg_color,
+ paddings = 2,
+ widget = wibox.container.arcchart,
+ set_volume_level = function(self, new_value)
+ self.value = new_value
+ end,
+ mute = function(self)
+ self.colors = { mute_color }
+ end,
+ unmute = function(self)
+ self.colors = { main_color }
+ end
+ }
+
+end
+
+
+return widget
diff --git a/awesome/widgets/volume-widget/widgets/horizontal-bar-widget.lua b/awesome/widgets/volume-widget/widgets/horizontal-bar-widget.lua
new file mode 100644
index 0000000..1484f91
--- /dev/null
+++ b/awesome/widgets/volume-widget/widgets/horizontal-bar-widget.lua
@@ -0,0 +1,58 @@
+local wibox = require("wibox")
+local beautiful = require('beautiful')
+local gears = require("gears")
+
+local ICON_DIR = os.getenv("HOME") .. '/.config/awesome/widgets/volume-widget/icons/'
+
+local widget = {}
+
+function widget.get_widget(widgets_args)
+ local args = widgets_args or {}
+
+ local main_color = args.main_color or beautiful.fg_normal
+ local mute_color = args.mute_color or beautiful.fg_urgent
+ local bg_color = args.bg_color or '#ffffff11'
+ local width = args.width or 50
+ local margins = args.margins or 10
+ local shape = args.shape or 'bar'
+ local with_icon = args.with_icon == true and true or false
+
+ local bar = wibox.widget {
+ {
+ {
+ id = "icon",
+ image = ICON_DIR .. 'audio-volume-high-symbolic.svg',
+ resize = false,
+ widget = wibox.widget.imagebox,
+ },
+ valign = 'center',
+ visible = with_icon,
+ layout = wibox.container.place,
+ },
+ {
+ id = 'bar',
+ max_value = 100,
+ forced_width = width,
+ color = main_color,
+ margins = { top = margins, bottom = margins },
+ background_color = bg_color,
+ shape = gears.shape[shape],
+ widget = wibox.widget.progressbar,
+ },
+ spacing = 4,
+ layout = wibox.layout.fixed.horizontal,
+ set_volume_level = function(self, new_value)
+ self:get_children_by_id('bar')[1]:set_value(tonumber(new_value))
+ end,
+ mute = function(self)
+ self:get_children_by_id('bar')[1]:set_color(mute_color)
+ end,
+ unmute = function(self)
+ self:get_children_by_id('bar')[1]:set_color(main_color)
+ end
+ }
+
+ return bar
+end
+
+return widget
diff --git a/awesome/widgets/volume-widget/widgets/icon-and-text-widget.lua b/awesome/widgets/volume-widget/widgets/icon-and-text-widget.lua
new file mode 100644
index 0000000..a37af77
--- /dev/null
+++ b/awesome/widgets/volume-widget/widgets/icon-and-text-widget.lua
@@ -0,0 +1,59 @@
+local wibox = require("wibox")
+local beautiful = require('beautiful')
+
+local widget = {}
+
+local ICON_DIR = os.getenv("HOME") .. '/.config/awesome/widgets/volume-widget/icons/'
+
+function widget.get_widget(widgets_args)
+ local args = widgets_args or {}
+
+ local font = args.font or beautiful.font
+ local icon_dir = args.icon_dir or ICON_DIR
+
+ return wibox.widget {
+ {
+ {
+ id = "icon",
+ resize = false,
+ widget = wibox.widget.imagebox,
+ },
+ valign = 'center',
+ layout = wibox.container.place
+ },
+ {
+ id = 'txt',
+ font = font,
+ widget = wibox.widget.textbox
+ },
+ layout = wibox.layout.fixed.horizontal,
+ set_volume_level = function(self, new_value)
+ self:get_children_by_id('txt')[1]:set_text(new_value)
+ local volume_icon_name
+ if self.is_muted then
+ volume_icon_name = 'audio-volume-muted-symbolic'
+ else
+ local new_value_num = tonumber(new_value)
+ if (new_value_num >= 0 and new_value_num < 33) then
+ volume_icon_name="audio-volume-low-symbolic"
+ elseif (new_value_num < 66) then
+ volume_icon_name="audio-volume-medium-symbolic"
+ else
+ volume_icon_name="audio-volume-high-symbolic"
+ end
+ end
+ self:get_children_by_id('icon')[1]:set_image(icon_dir .. volume_icon_name .. '.svg')
+ end,
+ mute = function(self)
+ self.is_muted = true
+ self:get_children_by_id('icon')[1]:set_image(icon_dir .. 'audio-volume-muted-symbolic.svg')
+ end,
+ unmute = function(self)
+ self.is_muted = false
+ end
+ }
+
+end
+
+
+return widget
diff --git a/awesome/widgets/volume-widget/widgets/icon-widget.lua b/awesome/widgets/volume-widget/widgets/icon-widget.lua
new file mode 100644
index 0000000..ddd52f2
--- /dev/null
+++ b/awesome/widgets/volume-widget/widgets/icon-widget.lua
@@ -0,0 +1,46 @@
+local wibox = require("wibox")
+
+local widget = {}
+
+local ICON_DIR = os.getenv("HOME") .. '/.config/awesome/widgets/volume-widget/icons/'
+
+function widget.get_widget(widgets_args)
+ local args = widgets_args or {}
+
+ local icon_dir = args.icon_dir or ICON_DIR
+
+ return wibox.widget {
+ {
+ id = "icon",
+ resize = false,
+ widget = wibox.widget.imagebox,
+ },
+ valign = 'center',
+ layout = wibox.container.place,
+ set_volume_level = function(self, new_value)
+ local volume_icon_name
+ if self.is_muted then
+ volume_icon_name = 'audio-volume-muted-symbolic'
+ else
+ local new_value_num = tonumber(new_value)
+ if (new_value_num >= 0 and new_value_num < 33) then
+ volume_icon_name="audio-volume-low-symbolic"
+ elseif (new_value_num < 66) then
+ volume_icon_name="audio-volume-medium-symbolic"
+ else
+ volume_icon_name="audio-volume-high-symbolic"
+ end
+ end
+ self:get_children_by_id('icon')[1]:set_image(icon_dir .. volume_icon_name .. '.svg')
+ end,
+ mute = function(self)
+ self.is_muted = true
+ self:get_children_by_id('icon')[1]:set_image(icon_dir .. 'audio-volume-muted-symbolic.svg')
+ end,
+ unmute = function(self)
+ self.is_muted = false
+ end
+ }
+end
+
+return widget
diff --git a/awesome/widgets/volume-widget/widgets/vertical-bar-widget.lua b/awesome/widgets/volume-widget/widgets/vertical-bar-widget.lua
new file mode 100644
index 0000000..acc60f2
--- /dev/null
+++ b/awesome/widgets/volume-widget/widgets/vertical-bar-widget.lua
@@ -0,0 +1,64 @@
+local wibox = require("wibox")
+local beautiful = require('beautiful')
+local gears = require("gears")
+
+local ICON_DIR = os.getenv("HOME") .. '/.config/awesome/widgets/volume-widget/icons/'
+
+local widget = {}
+
+function widget.get_widget(widgets_args)
+ local args = widgets_args or {}
+
+ local main_color = args.main_color or beautiful.fg_normal
+ local mute_color = args.mute_color or beautiful.fg_urgent
+ local bg_color = args.bg_color or '#ffffff11'
+ local width = args.width or 10
+ local margins = args.height or 2
+ local shape = args.shape or 'bar'
+ local with_icon = args.with_icon == true and true or false
+
+ local bar = wibox.widget {
+ {
+ {
+ id = "icon",
+ image = ICON_DIR .. 'audio-volume-high-symbolic.svg',
+ resize = false,
+ widget = wibox.widget.imagebox,
+ },
+ valign = 'center',
+ visible = with_icon,
+ layout = wibox.container.place,
+ },
+ {
+ {
+ id = 'bar',
+ max_value = 100,
+ forced_width = width,
+ forced_height = 5,
+ margins = { top = margins, bottom = margins },
+ color = main_color,
+ background_color = bg_color,
+ shape = gears.shape[shape],
+ widget = wibox.widget.progressbar,
+ },
+ forced_width = width,
+ direction = 'east',
+ layout = wibox.container.rotate,
+ },
+ spacing = 4,
+ layout = wibox.layout.fixed.horizontal,
+ set_volume_level = function(self, new_value)
+ self:get_children_by_id('bar')[1]:set_value(tonumber(new_value))
+ end,
+ mute = function(self)
+ self:get_children_by_id('bar')[1]:set_color(mute_color)
+ end,
+ unmute = function(self)
+ self:get_children_by_id('bar')[1]:set_color(main_color)
+ end
+ }
+
+ return bar
+end
+
+return widget