From 1ad1e4120700148359b271566ca70aed7ae79321 Mon Sep 17 00:00:00 2001 From: Tim Keller Date: Sun, 17 Nov 2024 22:48:47 -0600 Subject: pavolctld refactor and audio dropdown initial --- buttons.lua | 10 +-- keybindings.lua | 54 +++++++------ lib/pavolctld.lua | 107 +++++++++++++++++++++++++ theme.lua | 16 +++- widgets/audio.lua | 21 ----- widgets/pavolctld.lua | 212 ++++++++++++++++++++++++++++++-------------------- 6 files changed, 285 insertions(+), 135 deletions(-) create mode 100644 lib/pavolctld.lua delete mode 100644 widgets/audio.lua diff --git a/buttons.lua b/buttons.lua index 8b4a867..ff87aae 100644 --- a/buttons.lua +++ b/buttons.lua @@ -1,6 +1,6 @@ local awful = require("awful") local gears = require("gears") -local volume_control = require("widgets.pavolctld") +local pavolctld = require("lib.pavolctld") local super = "Mod4" local alt = "Mod1" @@ -22,7 +22,8 @@ client_buttons = gears.table.join( -- wibar widgets layout_buttons = gears.table.join( - awful.button({ }, 1, function() awful.layout.inc(1, awful.screen.focused().tags[0]) end) + awful.button({ }, 1, function() awful.layout.inc(1, awful.screen.focused().tags[0]) end), + awful.button({ }, 2, function() if client.focus and not client.focus.prevent_kill then client.focus:kill() end end) ) taglist_buttons = gears.table.join( @@ -42,7 +43,6 @@ tasklist_buttons = gears.table.join( ) volume_buttons = gears.table.join( - awful.button({ }, 4, function() volume_control.volume_inc(5) end), - awful.button({ }, 5, function() volume_control.volume_dec(5) end) + awful.button({ }, 4, function() pavolctld.volume_inc(5) end), + awful.button({ }, 5, function() pavolctld.volume_dec(5) end) ) - diff --git a/keybindings.lua b/keybindings.lua index 9800bbd..e731788 100644 --- a/keybindings.lua +++ b/keybindings.lua @@ -2,6 +2,7 @@ local awful = require("awful") local gears = require("gears") local hotkeys_popup = require("awful.hotkeys_popup") local menubar = require("menubar") +local pavolctld = require("lib.pavolctld") -- Enable hotkeys help widget for VIM and other apps -- when client with a matching name is opened: -- require("awful.hotkeys_popup.keys") @@ -45,7 +46,7 @@ globalkeys = gears.table.join( key(w , "F10", awesome.restart , { group = "awesome" , description = "reload awesome" }), key(ws, "F10", awesome.quit , { group = "awesome" , description = "quit awesome" }), key(w , "Tab", awful.tag.history.restore , { group = "tag" , description = "go back" }), - -- + -- Window Management key(w , "j", function () awful.client.focus.byidx( 1) end, { group = "client" , description = "focus next by index" }), key(w , "k", function () awful.client.focus.byidx(-1) end, { group = "client" , description = "focus previous by index" }), key(ws, "j", function () awful.client.swap.byidx( 1) end, { group = "client" , description = "swap with next client by index" }), @@ -73,24 +74,23 @@ globalkeys = gears.table.join( key(w , "F12", function () awful.spawn("shutdownprompt Shutdown") end, { group = "system" , description = "shutdown system" }), key(a , "m", function () awful.spawn("mounter -m") end, { group = "system" , description = "mount drive prompt" }), key(as, "m", function () awful.spawn("mounter -u") end, { group = "system" , description = "unmount drive prompt" }), - key(w , "F5", function () awful.spawn("bl set 0") end, { group = "system" , description = "set brightness to 0%" }), - key(w , "F6", function () awful.spawn("bl set 25") end, { group = "system" , description = "set brightness to 25%" }), - key(w , "F7", function () awful.spawn("bl set 50") end, { group = "system" , description = "set brightness to 50%" }), - key(w , "F8", function () awful.spawn("bl set 100") end, { group = "system" , description = "set brightness to 100%" }), -- Screenshots key({}, "Print", function () awful.spawn("screenshot -x") end, { group = "misc" , description = "screenshot, copy to clipboard" }), key(a , "Print", function () awful.spawn("screenshot -xc") end, { group = "misc" , description = "screenshot, crop, copy to clipboard" }), key(w , "Print", function () awful.spawn("screenshot") end, { group = "misc" , description = "screenshot, save to screenshots dir" }), - key(wa, "Print", function () awful.spawn("screenshot -c") end, { group = "misc" , description = "screenshot, crop, save to screenshots dir" }) + key(wa, "Print", function () awful.spawn("screenshot -c") end, { group = "misc" , description = "screenshot, crop, save to screenshots dir" }), + -- Monitor Controls + key(w , "F5", function () awful.spawn("bl set 0") end, { group = "monitor" , description = "set monitor brightness to 0%" }), + key(w , "F6", function () awful.spawn("bl set 25") end, { group = "monitor" , description = "set monitor brightness to 25%" }), + key(w , "F7", function () awful.spawn("bl set 50") end, { group = "monitor" , description = "set monitor brightness to 50%" }), + key(w , "F8", function () awful.spawn("bl set 100") end, { group = "monitor" , description = "set monitor brightness to 100%" }), + key({}, "XF86MonBrightnessUp", function () awful.spawn("bl inc 10") end, { group = "monitor" , description = "increase monitor brightness by 10%" }), + key({}, "XF86MonBrightnessDown", function () awful.spawn("bl dec 10") end, { group = "monitor" , description = "decrease monitor brightness by 10%" }), + -- Volume Controls + key({}, "XF86AudioRaiseVolume", function () pavolctld.volume_inc(5) end, { group = "volume" , description = "increase volume by 5%" }), + key({}, "XF86AudioLowerVolume", function () pavolctld.volume_dec(5) end, { group = "volume" , description = "decrease volume by 5%" }), + key({}, "XF86AudioMute", pavolctld.mute_toggle , { group = "volume" , description = "toggle audio mute" }) ) ---Audio Raise Volume Increase volume ---Audio Lower Volume Decrease volume ---Audio Mute Toggle mute ---Backlight Controls: ---Mon Brightness Up Increase brightness ---Mon Brightness Down Decrease brightness ---Alt + Mon Brightness Up Increase brightness by half-step ---Alt + Mon Brightness Down Decrease brightness by half-step -- Client protection @@ -104,20 +104,30 @@ local function unProtectClient(c) c.screen:emit_signal("arrange") end local function killClient(c) - if c.prevent_kill == nil or not c.prevent_kill then + if not c.prevent_kill then c:kill() end end +-- Client swap with master (more similar to dwm) +local function masterswap(c) + local m = awful.client.getmaster() + if m ~= c then + c:swap(awful.client.getmaster()) + else + c:swap(awful.client.next(1, c)) + end +end + -- Client keys clientkeys = gears.table.join( - key(w , "q", killClient , { description = "close", group = "client" }), - key(w , "x", protectClient , { description = "close", group = "client" }), - key(ws, "x", unProtectClient , { description = "close", group = "client" }), - key(wc, "space", awful.client.floating.toggle , { description = "toggle floating", group = "client" }), - key(w , "Return", function (c) c:swap(awful.client.getmaster()) end, { description = "move to master", group = "client" }), - key(ws, ",", function (c) c:move_to_screen(c.screen.index - 1) end, { description = "move to previous screen", group = "client" }), - key(ws, ",", function (c) c:move_to_screen(c.screen.index + 1) end, { description = "move to next screen", group = "client" }) + key(w , "q", killClient , { description = "close", group = "client" }), + key(w , "x", protectClient , { description = "prevent closing", group = "client" }), + key(ws, "x", unProtectClient , { description = "allow closing", group = "client" }), + key(wc, "space", awful.client.floating.toggle , { description = "toggle floating", group = "client" }), + key(w , "Return", masterswap , { description = "move to master", group = "client" }), + key(ws, ",", function (c) c:move_to_screen(c.screen.index - 1) end, { description = "move to left screen", group = "client" }), + key(ws, ".", function (c) c:move_to_screen() end, { description = "move to right screen", group = "client" }) ) -- Bind all key numbers to tags. diff --git a/lib/pavolctld.lua b/lib/pavolctld.lua new file mode 100644 index 0000000..b6b6237 --- /dev/null +++ b/lib/pavolctld.lua @@ -0,0 +1,107 @@ +local awful = require("awful") +local naughty = require("naughty") + +-- Gio is used to handle the subprocess instead of awful.spawn. +-- Gio is more flexible and allows writing to stdin. +-- also, awful.spawn.with_line_callback does not play nicely with pavolctld, as +-- it seems to feed the stdout back into its stdin and kill performance. +local lgi = require("lgi") +local Gio = lgi.Gio + +-- start subprocess +local p = Gio.Subprocess.new({ "pavolctld" }, Gio.SubprocessFlags.STDIN_PIPE + Gio.SubprocessFlags.STDOUT_PIPE) + +local stdout = p:get_stdout_pipe() +local stdin = p:get_stdin_pipe() + +-- state vars +local sinks = { + default = nil, + command = nil, -- sink being modified by commands + sinks = {}, +} + +function sinks.get(i) + if sinks.sinks[i] == nil then + sinks.sinks[i] = { + vol = 0, + db = 0.0, + mute = 0, + name = "", + desc = "", + } + end + return sinks.sinks[i] +end + +-- return table +local pavolctld = { _cb = {}, sinks = sinks } + +-- callbacks +function pavolctld.set_volume_change_callback(cb) pavolctld._cb['v'] = cb end +function pavolctld.set_sink_change_callback(cb) pavolctld._cb['s'] = cb end +function pavolctld.set_default_sink_change_callback(cb) pavolctld._cb['f'] = cb end +function pavolctld.set_sink_remove_callback(cb) pavolctld._cb['x'] = cb end + +-- parse output +function parse_csv(csv) + return (csv .. ","):gmatch("(.-),") +end + +awful.spawn.read_lines(stdout, function(s) + local cmd = s:sub(1, 1) -- first char of output + -- volume change + if cmd == 'v' then + local v = parse_csv(s:sub(2)) + local i = tonumber(v()) + local sink = sinks.get(i) + sink.vol = tonumber(v()) + sink.db = tonumber(v()) + sink.mute = tonumber(v()) + -- sink description change + elseif cmd == 's' then + local v = parse_csv(s:sub(2)) + local i = tonumber(v()) + local sink = sinks.get(i) + sink.name = v() + sink.desc = v() + -- default sink change + elseif cmd == 'f' then + local f = tonumber(s:sub(2)) + sinks.default = sinks.get(f) + -- set command sink to default sink for now TODO change later + send_cmd("s") + -- sink removed + elseif cmd == 'x' then + local x = tonumber(s:sub(2)) + table.remove(sinks.sinks, x) + -- unrecognized cmd + else + naughty.notify({ + preset = naughty.config.presets.critical, + title = "pavolctld error", + text = s + }) + end + -- run command callback + if pavolctld._cb[cmd] then + pavolctld._cb[cmd]() + end +end) + +-- pavolctld takes commands in using stdin +function send_cmd(cmd) + local _, err = stdin:write_all(cmd .. "\n", nil) + + if err then return nil end + return true +end + +function pavolctld.volume_inc(vol) return send_cmd("v+" .. vol) end +function pavolctld.volume_dec(vol) return send_cmd("v-" .. vol) end +function pavolctld.volume_set(vol) return send_cmd("v" .. vol) end +function pavolctld.mute_set(muted) return send_cmd("m" .. muted and 1 or 0) end +function pavolctld.mute_toggle() return send_cmd("m") end +function pavolctld.default_sink_set(i) return send_cmd("f" .. i) end + +return pavolctld diff --git a/theme.lua b/theme.lua index 9393030..6df5649 100644 --- a/theme.lua +++ b/theme.lua @@ -16,14 +16,14 @@ beautiful.gap_single_client = false -- overrides beautiful.tasklist_font_focus = beautiful.font -- prevent bold -beautiful.wibar_height = math.floor(beautiful.get_font_height(beautiful.font) * 1.1) +beautiful.wibar_height = math.floor(beautiful.get_font_height(beautiful.font) * 1.15) beautiful.wibar_bg = darkgray --beautiful.layoutlist_font = "Monospace 8" --beautiful.font = "Tamzen 10" --beautiful.tasklist_align = "center" -- does nothing? beautiful.font_sans = beautiful.font -beautiful.font_mono = "CommitMono 10" +beautiful.font_mono = "monospace 10" beautiful.font = beautiful.font_mono --beautiful.layoutlist_font = beautiful.font_mono @@ -41,6 +41,18 @@ beautiful.hotkeys_label_fg = white beautiful.hotkeys_modifiers_fg = lightgray beautiful.hotkeys_label_bg = darkgray -- ??? +-- slider +beautiful.slider_bar_height = 5 +beautiful.slider_bar_shape = gears.shape.rounded_rect +beautiful.slider_handle_width = 17.5 +beautiful.slider_handle_shape = gears.shape.circle +beautiful.slider_handle_color = beautiful.border_color +beautiful.slider_handle_border_color = "#333333" +beautiful.slider_handle_border_width = 1 + +-- progressbar +beautiful.progressbar_fg = beautiful.border_focus + -- set border on clients client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end) client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end) diff --git a/widgets/audio.lua b/widgets/audio.lua deleted file mode 100644 index 002c556..0000000 --- a/widgets/audio.lua +++ /dev/null @@ -1,21 +0,0 @@ -local awful = require("awful") -local wibox = require("wibox") -local gears = require("gears") - -local audio_dropdown = awful.popup { - widget = { - margins = 10, - widget = wibox.container.margin, - { - layout = wibox.layout.fixed.horizontal, - wibox.widget.textbox "AUDIO", - }, - }, - ontop = true, - placement = awful.placement.centered, - border_width = 1, - border_color = "#ff0000", - visible = true, -} - -return audio_dropdown diff --git a/widgets/pavolctld.lua b/widgets/pavolctld.lua index 1c57bb9..3697465 100644 --- a/widgets/pavolctld.lua +++ b/widgets/pavolctld.lua @@ -1,105 +1,147 @@ local awful = require("awful") +local gears = require("gears") local wibox = require("wibox") -local naughty = require("naughty") - --- Gio is used to handle the subprocess instead of awful.spawn. --- Gio is more flexible and allows writing to stdin. --- also, awful.spawn.with_line_callback does not play nicely with pavolctld, as --- it seems to feed the stdout back into its stdin and kill performance. -local lgi = require("lgi") -local Gio = lgi.Gio +local beautiful = require("beautiful") +local menubar = require("menubar") +local pavolctld = require("lib.pavolctld") -- return table local widget = {} widget.textbox = wibox.widget.textbox() +widget.dbmeter = wibox.widget.textbox() +widget.vslider = wibox.widget { + { + { + { + value = 50, + max_value = 150, + shape = beautiful.slider_bar_shape, + bg = beautiful.slider_handle_color, + widget = wibox.widget.progressbar, + }, + height = 5, + widget = wibox.container.constraint + }, + halign = "center", + widget = wibox.container.place, + }, + { + bar_height = 0, + widget = wibox.widget.slider, + }, + layout = wibox.layout.stack, +} +widget.defsink = wibox.widget.textbox() widget.tooltip = awful.tooltip { objects = {widget.textbox}, delay_show = 1 } --- start subprocess -local p = Gio.Subprocess.new({ "pavolctld" }, Gio.SubprocessFlags.STDIN_PIPE + Gio.SubprocessFlags.STDOUT_PIPE) +-- widget callbacks +pavolctld.set_volume_change_callback(function() + widget.vslider.value = pavolctld.sinks.default.vol + widget.textbox:set_text(pavolctld.sinks.default.vol) + widget.dbmeter:set_text(pavolctld.sinks.default.db .. " dB") +end) -local stdout = p:get_stdout_pipe() -local stdin = p:get_stdin_pipe() +pavolctld.set_sink_change_callback(function() + widget.tooltip:set_text(pavolctld.sinks.default.desc) +end) --- state vars -local sinks = { - default = nil, - command = nil, -- sink being modified by commands - sinks = {}, -} +pavolctld.set_default_sink_change_callback(function() + widget.vslider.value = pavolctld.sinks.default.vol + widget.textbox:set_text(pavolctld.sinks.default.vol) + widget.dbmeter:set_text(pavolctld.sinks.default.db .. " dB") + widget.tooltip:set_text(pavolctld.sinks.default.desc) + widget.defsink:set_text(pavolctld.sinks.default.desc) +end) -function sinks.get(i) - if sinks.sinks[i] == nil then - sinks.sinks[i] = { - vol = 0, - db = 0.0, - mute = 0, - name = "", - desc = "", - } - end - return sinks.sinks[i] -end +-- slider +widget.vslider.maximum = 150 -- max in pavolctld +widget.vslider:buttons(volume_buttons) +widget.vslider:connect_signal("property::value", function() + if pavolctld.sinks.default.vol == widget.vslider.value then return end -- help prevent overloading daemon + pavolctld.volume_set(widget.vslider.value) +end) --- parse output -function parse_csv(csv) - return (csv .. ","):gmatch("(.-),") -end +-- scrollbox for default sink select +local default_sink_scrollbox = wibox.widget { + widget.defsink, + step_function = wibox.container.scroll.step_functions.linear_increase, + speed = 25, + extra_space = 25, -- space between repetition + pause = true, -- start paused + widget = wibox.container.scroll.horizontal, +} -awful.spawn.read_lines(stdout, function(s) - local cmd = s:sub(1, 1) -- first char of output - -- volume change - if cmd == 'v' then - local v = parse_csv(s:sub(2)) - local i = tonumber(v()) - local sink = sinks.get(i) - sink.vol = tonumber(v()) - sink.db = tonumber(v()) - sink.mute = tonumber(v()) +-- scroll when hovering +default_sink_scrollbox:connect_signal("mouse::enter", function() + default_sink_scrollbox:continue() +end) - if sink == sinks.default then - widget.textbox:set_text(sink.vol) - end - -- sink description change - elseif cmd == 's' then - local v = parse_csv(s:sub(2)) - local i = tonumber(v()) - local sink = sinks.get(i) - sink.name = v() - sink.desc = v() - -- default sink change - elseif cmd == 'f' then - local f = tonumber(s:sub(2)) - sinks.default = sinks.get(f) - widget.textbox:set_text(sinks.default.vol) - widget.tooltip:set_text(sinks.default.desc) - -- set command sink to default sink for now TODO change later - pavolctld_cmd("s") - -- sink removed - elseif cmd == 'x' then - local x = tonumber(s:sub(2)) - table.remove(sinks.sinks, x) - else - naughty.notify({ - preset = naughty.config.presets.critical, - title = "pavolctld error", - text = s - }) - end +default_sink_scrollbox:connect_signal("mouse::leave", function() + default_sink_scrollbox:pause() + default_sink_scrollbox:reset_scrolling() end) --- pavolctld takes commands in using stdin -function pavolctld_cmd(cmd) - local _, err = stdin:write_all(cmd .. "\n", nil) +-- widget dropdown +local volume_dropdown = wibox.widget { + { + { + { + default_sink_scrollbox, + margins = 5, + widget = wibox.container.margin, + }, + bg = "#333333", + widget = wibox.container.background, + }, + margins = beautiful.border_width, + color = "#222222", + widget = wibox.container.margin, + }, + { + { + text = "M", + widget = wibox.widget.textbox, + }, + { + widget.vslider, + left = 10, + right = 10, + widget = wibox.container.margin, + layout = wibox.layout.stack, + }, + { + widget.dbmeter, + forced_width = 125, + halign = "right", + widget = wibox.container.place, + }, + layout = wibox.layout.align.horizontal, + }, + forced_num_cols = 1, + forced_num_rows = 2, + expand = true, + forced_width = 500, + forced_height = 100, + layout = wibox.layout.grid, +} +widget.dropdown = awful.popup { + widget = { + volume_dropdown, + margins = 15, + widget = wibox.container.margin, + }, + border_color = beautiful.border_focus, + border_width = beautiful.border_width, + shape = gears.shape.rounded_rect, + ontop = true, + hide_on_right_click = true, + preferred_positions = "bottom", + preferred_anchors = 'back', + visible = false, + offset = { y = 5 }, +} - if err then return nil end - return true -end +widget.dropdown:bind_to_widget(widget.textbox) -function widget.volume_inc(vol) return pavolctld_cmd("v+" .. vol) end -function widget.volume_dec(vol) return pavolctld_cmd("v-" .. vol) end -function widget.volume_set(vol) return pavolctld_cmd("v" .. vol) end -function widget.mute_set(muted) return pavolctld_cmd("m" .. muted and 1 or 0) end -function widget.mute_toggle() return pavolctld_cmd("m") end -function widget.default_sink_set(i) return pavolctld_cmd("f" .. i) end return widget -- cgit v1.2.3