big update

This commit is contained in:
Yessiest 2022-09-16 22:42:50 +04:00
parent 736ada7652
commit 7d60e5625f
17 changed files with 601 additions and 32 deletions

View File

@ -23,6 +23,11 @@ The installation process is not much different from the previous iteration, exce
3. Install AwesomeWM (version 4.3 as of right now) 3. Install AwesomeWM (version 4.3 as of right now)
4. (Optional) Read additional installation steps in `extra/README.md` 4. (Optional) Read additional installation steps in `extra/README.md`
## Keybindings and user guide
- press win+s
- read extra/README.md
- enjoy
## Roadmap ## Roadmap
- [x] Port widgets from original config to AWMTK2 - [x] Port widgets from original config to AWMTK2

View File

@ -26,11 +26,20 @@ modkey+b = ":client.below"
modkey+f = ":client.fullscreen" modkey+f = ":client.fullscreen"
modkey+n = ":client.minimize" modkey+n = ":client.minimize"
modkey+m = ":client.maximize" modkey+m = ":client.maximize"
modkey+p = ":client.pin"
modkey+y = ":client.toggle_titlebars"
# Widget keys # Widget keys
modkey+r = ":dismal.run" modkey+r = ":dismal.run"
modkey+s = ":help.show" modkey+s = ":help.show"
Control+XF86AudioRaiseVolume = ":client.volume_up"
Control+XF86AudioLowerVolume = ":client.volume_down"
Control+XF86AudioMute = ":client.volume_mute"
XF86MonBrightnessUp = ":battery.brightness_up"
XF86MonBrightnessDown = ":battery.brightness_down"
XF86AudioPlay = ":mpc.play"
XF86AudioPrev = ":mpc.prev"
XF86AudioNext = ":mpc.next"
# Custom keys # Custom keys
Print = "flameshot gui" Print = "flameshot gui"
Shift+Print = "flameshot launcher" Shift+Print = "flameshot launcher"

View File

@ -12,6 +12,12 @@ local asckey = {
} }
local awful = require("awful") local awful = require("awful")
asckey.set_keymap = function(keymap)
for k,v in pairs(keymap) do
asckey.keymap[v] = k
end
end
asckey.get_keycomb = function(name) asckey.get_keycomb = function(name)
local modifiers = {} local modifiers = {}
name = name:gsub("[^%s%+]+%+",function(v) name = name:gsub("[^%s%+]+%+",function(v)

View File

@ -162,6 +162,9 @@ awmtk.proto_style.center = awmtk.create_delta("center", {
awmtk.proto_style.slider = awmtk.create_delta("slider", { awmtk.proto_style.slider = awmtk.create_delta("slider", {
margins = 1 margins = 1
}, awmtk.proto_style,awmtk.proto_style.base) }, awmtk.proto_style,awmtk.proto_style.base)
awmtk.proto_style.checkbox = awmtk.create_delta("checkbox", {
}, awmtk.proto_style,awmtk.proto_style.base)
-- }}} -- }}}
-- {{{ Generic templates -- {{{ Generic templates
@ -407,6 +410,19 @@ awmtk.proto_templates = {
widget = wibox.widget.slider widget = wibox.widget.slider
},args or {}) },args or {})
end end
end,
checkbox = function(style)
return function(args)
return awmtk.merge({
color = style.checkbox.bg_focus,
padding = 2,
shape = style.checkbox.shape,
border_width = style.checkbox.shape_border_width,
bg = style.checkbox.shape_border_color,
widget = wibox.widget.checkbox
},args or {})
end
end end
} }

View File

@ -7,6 +7,15 @@
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>. -- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
local parsers = require("parsers") local parsers = require("parsers")
function rprint(t,ident)
ident = ident or 0
for k,v in pairs(t) do
print((" "):rep(ident)..k,v)
if type(v) == "table" then
rprint(v,ident+4)
end
end
end
-- Conf parser -- Conf parser
local data = [[ local data = [[
# Global variables # Global variables
@ -45,3 +54,163 @@ for k,v in pairs(parsers.conf(data)) do
print(kk,vv) print(kk,vv)
end end
end end
-- yaml-like (pactl) parser
data = [[
Sink Input #75
Driver: PipeWire
Owner Module: n/a
Client: 58
Sink: 46
Sample Specification: float32le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
balance 0.00
Buffer Latency: 0 usec
Sink Latency: 0 usec
Resample method: PipeWire
Properties:
client.api = "pipewire-pulse"
pulse.server.type = "unix"
application.name = "LibreWolf"
application.process.id = "1756"
application.process.user = "yessiest"
application.process.host = "architect"
application.process.binary = "librewolf"
application.language = "C.UTF-8"
window.x11.display = ":0"
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
application.process.session_id = "1"
media.name = "AudioStream"
node.rate = "1/44100"
node.latency = "3307/44100"
stream.is-live = "true"
node.name = "LibreWolf"
node.autoconnect = "true"
node.want-driver = "true"
media.class = "Stream/Output/Audio"
adapt.follower.spa-node = ""
object.register = "false"
factory.id = "6"
clock.quantum-limit = "8192"
factory.mode = "split"
audio.adapt.follower = ""
library.name = "audioconvert/libspa-audioconvert"
client.id = "56"
object.id = "57"
object.serial = "75"
pulse.attr.maxlength = "4194304"
pulse.attr.tlength = "44104"
pulse.attr.prebuf = "35296"
pulse.attr.minreq = "8816"
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
]]
rprint(parsers.yaml_pseudo(data))
-- Fast incomplete yaml parser
data = [[
Sink Input #73
Driver: PipeWire
Owner Module: n/a
Client: 58
Sink: 46
Sample Specification: float32le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
balance 0.00
Buffer Latency: 0 usec
Sink Latency: 0 usec
Resample method: PipeWire
Properties:
client.api = "pipewire-pulse"
pulse.server.type = "unix"
application.name = "LibreWolf"
application.process.id = "1756"
application.process.user = "yessiest"
application.process.host = "architect"
application.process.binary = "librewolf"
application.language = "C.UTF-8"
window.x11.display = ":0"
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
application.process.session_id = "1"
media.name = "AudioStream"
node.rate = "1/44100"
node.latency = "3307/44100"
stream.is-live = "true"
node.name = "LibreWolf"
node.autoconnect = "true"
node.want-driver = "true"
media.class = "Stream/Output/Audio"
adapt.follower.spa-node = ""
object.register = "false"
factory.id = "6"
clock.quantum-limit = "8192"
factory.mode = "split"
audio.adapt.follower = ""
library.name = "audioconvert/libspa-audioconvert"
client.id = "56"
object.id = "57"
object.serial = "75"
pulse.attr.maxlength = "4194304"
pulse.attr.tlength = "44104"
pulse.attr.prebuf = "35296"
pulse.attr.minreq = "8816"
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
Sink Input #75
Driver: PipeWire
Owner Module: n/a
Client: 58
Sink: 46
Sample Specification: float32le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
balance 0.00
Buffer Latency: 0 usec
Sink Latency: 0 usec
Resample method: PipeWire
Properties:
client.api = "pipewire-pulse"
pulse.server.type = "unix"
application.name = "LibreWolf"
application.process.id = "1756"
application.process.user = "yessiest"
application.process.host = "architect"
application.process.binary = "librewolf"
application.language = "C.UTF-8"
window.x11.display = ":0"
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
application.process.session_id = "1"
media.name = "AudioStream"
node.rate = "1/44100"
node.latency = "3307/44100"
stream.is-live = "true"
node.name = "LibreWolf"
node.autoconnect = "true"
node.want-driver = "true"
media.class = "Stream/Output/Audio"
adapt.follower.spa-node = ""
object.register = "false"
factory.id = "6"
clock.quantum-limit = "8192"
factory.mode = "split"
audio.adapt.follower = ""
library.name = "audioconvert/libspa-audioconvert"
client.id = "56"
object.id = "57"
object.serial = "75"
pulse.attr.maxlength = "4194304"
pulse.attr.tlength = "44104"
pulse.attr.prebuf = "35296"
pulse.attr.minreq = "8816"
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
]]
rprint(parsers.fast_split_yaml(data))

View File

@ -34,7 +34,60 @@ local function split_strings(text)
return split return split
end end
parsers.fast_split_yaml = function(cfgtext)
-- Fast yaml splitter - incomplete parsing, only first layer is parsed
-- Used within timers to find objects while decreasing CPU usage
local items = {}
local replacements = 1
cfgtext = cfgtext:gsub("^%s*","")
while replacements > 0 do
cfgtext,replacements = cfgtext:gsub("^(.-\n)(%S+)",function(struct,n)
table.insert(items,struct)
return ""..n
end)
end
table.insert(items,cfgtext)
return items
end
parsers.yaml_pseudo = function(cfgtext)
-- Somewhat yaml-like structure used by pactl
local struct = {}
local lines = {}
cfgtext:gsub("(%s*)([^\n]*)",function(spacing,line)
table.insert(lines,
{
spacing:len(),
-- key
line:match("^([^:=]-)%s*[:=]") or line,
-- value
line:match(":%s*(.-)%s*$") or
line:match("=%s*(.-)%s*$")
}
)
end)
local history = {struct}
local spacing_width = 0
for k,v in pairs(lines) do
if v[1] > spacing_width then
history[#history][lines[k-1][2]] = {
[lines[k-1][2]] = lines[k-1][3]
}
history[#history+1] = history[#history][lines[k-1][2]]
elseif v[1] < spacing_width then
history[#history] = nil
end
if v[3] and v[3]:match("^%s*\".*\"%s*$") then
history[#history][v[2]] = v[3]:match("^%s*\"(.*)\"%s*$")
else
history[#history][v[2]] = v[3]
end
spacing_width = v[1]
end
return struct
end
parsers.conf = function(cfgtext) parsers.conf = function(cfgtext)
-- Conf style parser (not exactly TOML)
cfgtext = cfgtext:gsub("#[^\n]*","") cfgtext = cfgtext:gsub("#[^\n]*","")
local split_by_strings,err = split_strings(cfgtext) local split_by_strings,err = split_strings(cfgtext)
if not split_by_strings then if not split_by_strings then

View File

@ -10,7 +10,8 @@
local syscontrol = { local syscontrol = {
power_supply = {}, power_supply = {},
backlight = {}, backlight = {},
hwmon = {} hwmon = {},
pulse = {}
} }
syscontrol.backlight.enumerate = function() syscontrol.backlight.enumerate = function()
local lshandler = io.popen("ls -1 /sys/class/backlight","r") local lshandler = io.popen("ls -1 /sys/class/backlight","r")

View File

@ -11,7 +11,7 @@ local gears = require("gears")
local ask = require("asckey") local ask = require("asckey")
global.modkey = global.modkey or "Mod4" global.modkey = global.modkey or "Mod4"
ask.keymap = keybindings ask.set_keymap(config.keys)
local custom_keys = ask.custom_binds() local custom_keys = ask.custom_binds()
local k = ask.k local k = ask.k
@ -101,7 +101,22 @@ local clientkeys = gears.table.join(
c.maximized = not c.maximized c.maximized = not c.maximized
c:raise() c:raise()
end , end ,
{description = "(un)maximize", group = "client"})) {description = "(un)maximize", group = "client"}),
k(":client.pin",
function (c)
c.sticky = not c.sticky
end ,
{description = "(un)pin", group = "client"}),
k(":client.toggle_titlebars",
function (c)
if (not c.titlebar_top.visible) then
c:emit_signal("titlebar::unhide")
else
c:emit_signal("titlebar::hide")
end
end ,
{description = "(un)hide titlebars", group = "client"}))
awful.rules.rules[1].properties.keys = clientkeys awful.rules.rules[1].properties.keys = clientkeys
local clientbuttons = gears.table.join( local clientbuttons = gears.table.join(

View File

@ -156,7 +156,7 @@ client.connect_signal("request::titlebars",function(c)
fg_focus = style[v].fg_focus, fg_focus = style[v].fg_focus,
font = style[v].font font = style[v].font
}) })
titlebar:setup(t.titlebar(contents)) c[v] = titlebar:setup(t.titlebar(contents))
awful.rules.rules[1].properties.placement(c) awful.rules.rules[1].properties.placement(c)
if style[v].onfocus then if style[v].onfocus then
c:connect_signal("focus",function() c:connect_signal("focus",function()
@ -168,6 +168,12 @@ client.connect_signal("request::titlebars",function(c)
style[v].onunfocus(titlebar) style[v].onunfocus(titlebar)
end) end)
end end
c:connect_signal("titlebar::hide",function(c)
c[v].visible = false
end)
c:connect_signal("titlebar::unhide",function(c)
c[v].visible = true
end)
end end
end) end)
end --}}} end --}}}

View File

@ -12,16 +12,8 @@ local conf_file = io.open(root_path.."/desktop.conf","r")
if not conf_file then if not conf_file then
error("desktop.conf is missing or not readable") error("desktop.conf is missing or not readable")
end end
local config = conf(conf_file:read("*a")) config = conf(conf_file:read("*a"))
conf_file:close() conf_file:close()
global = config.global global = config.global
global.terminal = envsub(global.terminal) global.terminal = envsub(global.terminal)
global.browser = envsub(global.browser) global.browser = envsub(global.browser)
local function invert(t)
local new_t = {}
for k,v in pairs(t) do
new_t[v] = k
end
return new_t
end
keybindings = invert(config.keys)

63
modules/powermanX.lua Normal file
View File

@ -0,0 +1,63 @@
-- 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/>.
-- Powerman X - second generation of the power management daemon
local sysctl = require("syscontrol")
local naughty = require("naughty")
local gears = require("gears")
local awful = require("awful")
local batteries = sysctl.power_supply.enumerate()
local state_tracking = {}
-- Configuration variables
local cfg = config.powerman or {}
local quality_min = cfg.battery_quality_min or 33
local capacity_min = cfg.battery_capacity_min or 15
-- Main loop
local update_loop = gears.timer({
timeout = 2,
autostart = true,
callback = function()
for k,v in pairs(batteries) do
local data,err = sysctl.power_supply.read_attribs(v)
state_tracking[v] = state_tracking[v] or {}
if data.type == "Battery" then
if (tonumber(data.quality) < quality_min) and
(not state_tracking[v].quality_notification) then
naughty.notify({
title = "Critical battery condition",
text = "Battery "..data.name.." has reached critically low condition, seek a suitable replacement"
})
state_tracking[v].quality_notification = true
end
if (tonumber(data.capacity) <= capacity_min) and
(not data.charging) and
(not state_tracking[v].capacity_notification) then
naughty.notify({
title = "Battery capacity low",
text = "Battery "..data.name.." capacity is at "..tostring(data.capacity).."%"
})
state_tracking[v].capacity_notification = true
end
if (tonumber(data.capacity) > capacity_min) then
state_tracking[v].capacity_notification = false
end
if (data.capacity == "100") and
(data.charging) and
(not state_tracking[v].charged_notification) then
naughty.notify({
title = "Battery is completely charged",
text = "Disconnect the charger from the power grid to avoid passive electricity usage."
})
end
if (not data.charging) then
state_tracking[v].charged_notification = false
end
end
end
end
})

1
rc.lua
View File

@ -16,6 +16,7 @@ package.cpath = package.cpath
-- Modules list -- Modules list
require("modules.collect_garbage") require("modules.collect_garbage")
require("modules.global") require("modules.global")
require("modules.powermanX")
require("modules.errorlog") require("modules.errorlog")
require("modules.base") require("modules.base")
require("modules.binds") require("modules.binds")

View File

@ -1,5 +1,6 @@
{ {
"list": [ "list": [
{"widget": "widgets.clientvolume"},
{"widget": "widgets.clientcontrols"}, {"widget": "widgets.clientcontrols"},
{"widget": "widgets.clientbuttons"} {"widget": "widgets.clientbuttons"}
], ],

View File

@ -1,6 +1,6 @@
-- Reno98 - a retro replica of a very recognizable theme -- Reno Unity - Unity theme for Reno desktop
--[[ --[[
Reno98 - A theme for Reno desktop Reno Unity - A theme for Reno desktop
Written in 2022 by Yessiest (yessiest@memeware.net) Written in 2022 by Yessiest (yessiest@memeware.net)
@ -306,11 +306,20 @@ theme.widgets = {
end, end,
height = 20, height = 20,
width = 140, width = 140,
bg_focus = theme.bg_normal,
bg_normal = theme.bg_focus,
handle_width = 8, handle_width = 8,
handle_border_color = theme.bg_focus, handle_border_color = theme.bg_normal,
handle_border_width = 2, handle_border_width = 2,
bar_height = 6 bar_height = 6,
} bar_border_color = theme.bg_focus,
bar_border_width = 2
},
checkbox = {
shape = gears.shape.circle,
shape_border_width = 3,
shaoe_border_color = theme.bg_focus
},
}, },
-- }}} -- }}}
-- {{{ Menus -- {{{ Menus

View File

@ -12,6 +12,7 @@ local gears = require("gears")
local wibox = require("wibox") local wibox = require("wibox")
local awmtk2 = require("awmtk2") local awmtk2 = require("awmtk2")
local syscontrol = require("syscontrol") local syscontrol = require("syscontrol")
local ask = require("asckey")
local function get_virtual_icon(data) local function get_virtual_icon(data)
-- Get an icon from a cumulative total of battery percentages and current charging state -- Get an icon from a cumulative total of battery percentages and current charging state
@ -182,14 +183,25 @@ return function(args)
-- }}} -- }}}
-- {{{ Backlight -- {{{ Backlight
local backlight_devices = syscontrol.backlight.enumerate() local backlight_devices = syscontrol.backlight.enumerate()
local default_backlight_device
for k,v in pairs(backlight_devices) do for k,v in pairs(backlight_devices) do
local data = syscontrol.backlight.read_attribs(v) local data = syscontrol.backlight.read_attribs(v)
if data then if data then
widget_map[data.name] = wibox.widget(t.container({ widget_map[data.name] = wibox.widget(t.container({
t.article({ {
icon = beautiful["backlight-symbolic"], t.article({
title = "Backlight", icon = beautiful["backlight-symbolic"],
}), title = "Backlight",
}),
(data.writable and t.checkbox({
checked = true,
id = "checkbox",
forced_height = style.article.icon_size,
forced_width = style.article.icon_size
})),
layout = wibox.layout.fixed.horizontal,
spacing = style.base.spacing
},
t.textbox({ t.textbox({
markup = "Brightness: "..tostring(data.brightness), markup = "Brightness: "..tostring(data.brightness),
id = "brightness_id" id = "brightness_id"
@ -201,7 +213,7 @@ return function(args)
(data.writable and t.slider({ (data.writable and t.slider({
minimum = data.max_brightness*0.05, minimum = data.max_brightness*0.05,
maximum = data.max_brightness, maximum = data.max_brightness,
value = data.brightness, value = tonumber(data.brightness),
id = "slider" id = "slider"
})), })),
layout = wibox.layout.fixed.vertical layout = wibox.layout.fixed.vertical
@ -210,11 +222,22 @@ return function(args)
bgimage = style.container.bgimage_highlight bgimage = style.container.bgimage_highlight
})) }))
if data.writable then if data.writable then
local slider = widget_map[data.name]:get_children_by_id("slider")[1] local w = widget_map[data.name]
local slider = w:get_children_by_id("slider")[1]
slider:connect_signal("widget::redraw_needed",function(self) slider:connect_signal("widget::redraw_needed",function(self)
local value = self.value local value = self.value
syscontrol.backlight.set_brightness(data,math.floor(value)) syscontrol.backlight.set_brightness(data,math.floor(value))
end) end)
slider.value = tonumber(data.brightness)
local checkbox = w:get_children_by_id("checkbox")[1]
checkbox:connect_signal("button::press",function()
if default_backlight_device then
local check2 = widget_map[default_backlight_device.name]
:get_children_by_id("checkbox")[1]
check2.checked = false
end
default_backlight_device = data
end)
end end
layout:add(widget_map[data.name]) layout:add(widget_map[data.name])
end end
@ -236,6 +259,34 @@ return function(args)
end end
end end
}) })
-- Keybindings
root.keys(gears.table.join(
root.keys(),
ask.k(":battery.brightness_up",function()
if default_backlight_device then
local data = default_backlight_device
local s = widget_map[data.name]:get_children_by_id("slider")[1]
local value = s.value+(data.max_brightness*0.05)
if value > data.max_brightness then
value = data.max_brightness
end
syscontrol.backlight.set_brightness(data,math.floor(value))
s.value = math.floor(value)
end
end,{description="increase brightness", group = "widgets"}),
ask.k(":battery.brightness_down",function()
if default_backlight_device then
local data = default_backlight_device
local s = widget_map[data.name]:get_children_by_id("slider")[1]
local value = s.value-(data.max_brightness*0.05)
if value < data.max_brightness*0.05 then
value = data.max_brightness*0.05
end
syscontrol.backlight.set_brightness(data,math.floor(value))
s.value = math.floor(value)
end
end,{description="decrease brightness", group = "widgets"})
))
-- }}} -- }}}
-- We don't need this widget if we don't have anything to show -- We don't need this widget if we don't have anything to show
local function count(t) local function count(t)

154
widgets/clientvolume.lua Normal file
View File

@ -0,0 +1,154 @@
-- 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 pactl_data = {}
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
return
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)
local templates = awmtk2.create_template_lib("client_volume",awmtk2.templates,args.templates)
local t = awmtk2.build_templates(templates,style)
local widget = wibox.widget(t.container({
t.center({
id = "client_volume_icon",
widget = wibox.widget.imagebox
}),
t.textbox({
id = "error"
}),
t.slider({
minimum = 0,
maximum = 100,
id = "client_volume",
value = -1
}),
layout = 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 slider_touched = false
-- Get initial pactl data
awful.spawn.easy_async("pactl list sink-inputs",function(stdout)
local pactl_data = fastyaml(stdout)
end)
-- 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 k,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(volume)
if volume_lock then return end
volume_lock = true
awful.spawn.easy_async("pactl list sink-inputs",function(stdout)
local pactl_data = fastyaml(stdout)
local cl = {}
if not (client.focus and client.focus.pid) then
volume_lock = false
return
end
for k,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, volume)
awful.spawn("pactl set-sink-input-volume "..tostring(sink_id).." "..tostring(volume).."%")
end
end
end
volume_lock = false
end)
end
-- Attach change to slider
slider:connect_signal("widget::redraw_needed",function(widget)
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

View File

@ -12,6 +12,7 @@ local wibox = require("wibox")
local gears = require("gears") local gears = require("gears")
local awful = require("awful") local awful = require("awful")
local beautiful = require("beautiful") local beautiful = require("beautiful")
local ask = require("asckey")
return function(args) return function(args)
local style = awmtk2.create_style("soundclown", local style = awmtk2.create_style("soundclown",
@ -72,15 +73,16 @@ return function(args)
local bprev = widget:get_children_by_id("prev")[1] local bprev = widget:get_children_by_id("prev")[1]
bprev:connect_signal("button::press",style.button.onpress) bprev:connect_signal("button::press",style.button.onpress)
bprev:connect_signal("button::release",style.button.onrelease) bprev:connect_signal("button::release",style.button.onrelease)
bprev:connect_signal("button::press",function() local function prev()
awful.spawn("mpc cdprev") awful.spawn("mpc cdprev")
end) end
bprev:connect_signal("button::press",prev)
local pause_state = true local pause_state = true
local icon = widget:get_children_by_id("statusicon")[1] local icon = widget:get_children_by_id("statusicon")[1]
local bplay = widget:get_children_by_id("play")[1] local bplay = widget:get_children_by_id("play")[1]
bplay:connect_signal("button::press",style.button.onpress) bplay:connect_signal("button::press",style.button.onpress)
bplay:connect_signal("button::release",style.button.onrelease) bplay:connect_signal("button::release",style.button.onrelease)
bplay:connect_signal("button::press",function() local function play()
pause_state = (not pause_state) pause_state = (not pause_state)
if pause_state == false then if pause_state == false then
icon.image = beautiful["mpc-pause-symbolic"] icon.image = beautiful["mpc-pause-symbolic"]
@ -89,13 +91,16 @@ return function(args)
icon.image = beautiful["mpc-play-symbolic"] icon.image = beautiful["mpc-play-symbolic"]
awful.spawn("mpc play") awful.spawn("mpc play")
end end
end) end
bplay:connect_signal("button::press",play)
local bnext = widget:get_children_by_id("next")[1] local bnext = widget:get_children_by_id("next")[1]
bnext:connect_signal("button::press",style.button.onpress) bnext:connect_signal("button::press",style.button.onpress)
bnext:connect_signal("button::release",style.button.onrelease) bnext:connect_signal("button::release",style.button.onrelease)
bnext:connect_signal("button::press",function() local function nextb()
awful.spawn("mpc next") awful.spawn("mpc next")
end) end
bnext:connect_signal("button::press",nextb)
local update_ready = true
local function update_mpd_status() local function update_mpd_status()
awful.spawn.easy_async("mpc",function(out) awful.spawn.easy_async("mpc",function(out)
local status = "" local status = ""
@ -104,6 +109,7 @@ return function(args)
state = true state = true
icon.image = beautiful["mpc-play-symbolic"] icon.image = beautiful["mpc-play-symbolic"]
display:set_markup(status) display:set_markup(status)
update_ready = true
return return
else else
status = status.."[PAUSED] " status = status.."[PAUSED] "
@ -113,6 +119,7 @@ return function(args)
out:match("[^\n]*").." ".. out:match("[^\n]*").." "..
out:match("%d*:%d*/%d*:%d*%s*%(%d*%%%)") out:match("%d*:%d*/%d*:%d*%s*%(%d*%%%)")
display:set_markup(status) display:set_markup(status)
update_ready = true
end) end)
end end
update_mpd_status() update_mpd_status()
@ -120,8 +127,19 @@ return function(args)
timeout = args.polling_delay or 1, timeout = args.polling_delay or 1,
autostart = true, autostart = true,
callback = function() callback = function()
if not update_ready then return end
update_ready = false
update_mpd_status() update_mpd_status()
end end
} }
root.keys(gears.table.join(
root.keys(),
ask.k(":mpc.prev",prev,
{description = "switch to previous MPD track",group="widgets"}),
ask.k(":mpc.play",play,
{description = "play/pause MPD",group="widgets"}),
ask.k(":mpc.next",nextb,
{description = "switch to next MPD track",group="widgets"})
))
return widget return widget
end end