Dump nixos config after scrubing
This commit is contained in:
commit
5fa4c76c24
854 changed files with 30072 additions and 0 deletions
54
home-modules/explicit-configs/awesome/deficient/README.md
Normal file
54
home-modules/explicit-configs/awesome/deficient/README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
A collection of lightweight widgets for [awesome wm](https://awesomewm.org/),
|
||||
compatible with awesome 4.x that were previously hosted as separate repositories.
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
Clone this repository into your awesome config folder:
|
||||
|
||||
```bash
|
||||
cd ~/.config/awesome
|
||||
git clone https://github.com/deficient/deficient
|
||||
```
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
The widgets can usually be instanciated similar to this:
|
||||
|
||||
```lua
|
||||
local deficient = require("deficient")
|
||||
|
||||
local calendar = deficient.calendar {}
|
||||
local quicklaunch = deficient.quicklaunch {}
|
||||
-- etc
|
||||
```
|
||||
|
||||
For more detailed usage instructions, refer to the individual widgets:
|
||||
|
||||
- [battery-widget](./battery-widget)
|
||||
- [brightness](./brightness)
|
||||
- [calendar](./calendar)
|
||||
- [cpuinfo](./cpuinfo)
|
||||
- [keyboard-layout-indicator](./keyboard-layout-indicator)
|
||||
- [leds-widget](./leds-widget)
|
||||
- [quicklaunch](./quicklaunch)
|
||||
- [screensaver](./screensaver)
|
||||
- [volume-control](./volume-control)
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
Some of the widgets also have individual dependencies if you want to use them.
|
||||
|
||||
- *battery indicator*:
|
||||
* `acpid` (recommended)
|
||||
- *brightness control*:
|
||||
* [acpilight](https://archlinux.org/packages/extra/any/acpilight/) or
|
||||
[xorg-xbacklight](https://archlinux.org/packages/extra/x86_64/xorg-xbacklight/) for `xbacklight`
|
||||
* [brightnessctl](https://archlinux.org/packages/extra/x86_64/brightnessctl/) for `brightnessctl`
|
||||
- *screensaver*:
|
||||
* `xorg-xset`
|
||||
- *volume control*:
|
||||
* `pavucontrol` (recommended)
|
||||
* `acpid` (recommended)
|
||||
24
home-modules/explicit-configs/awesome/deficient/UNLICENSE
Normal file
24
home-modules/explicit-configs/awesome/deficient/UNLICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
## awesome.battery-widget
|
||||
|
||||
Battery indicator widget for awesome window manager.
|
||||
|
||||

|
||||
|
||||
Displays status information from `/sys/class/power_supply`.
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
Optionally, in order to receive status updates, you will also need `acpid`:
|
||||
|
||||
```bash
|
||||
pacman -S acpid
|
||||
systemctl enable --now acpid
|
||||
```
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
In order to add a battery widget to your wibox, you have to import the module
|
||||
and then instanciate a widget with the desired options like this:
|
||||
|
||||
```lua
|
||||
local deficient = require("deficient")
|
||||
|
||||
|
||||
-- instanciate widget
|
||||
local battery_widget = deficient.battery_widget {
|
||||
-- pass options here
|
||||
}
|
||||
|
||||
|
||||
-- add to wibox
|
||||
s.mywibox:setup {
|
||||
...,
|
||||
{ -- Right widgets
|
||||
...,
|
||||
battery_widget,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If you pass an adapter name using the `adapter = "..."` option, a widget for
|
||||
that specific battery adapter will be instanciated. If the `adapter` option is
|
||||
not specified, the call will return a table containing widgets for each of the
|
||||
battery adapters in `/sys/class/power_supply`. In that case if there are no
|
||||
batteries an empty table will be returned and no error will occur on machines
|
||||
without batteries.
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
The behaviour and appearance of the widget can be tweaked using a few options.
|
||||
This is an example using all available options:
|
||||
|
||||
```lua
|
||||
battery_widget {
|
||||
ac = "AC",
|
||||
adapter = "BAT0",
|
||||
ac_prefix = "AC: ",
|
||||
battery_prefix = "Bat: ",
|
||||
percent_colors = {
|
||||
{ 25, "red" },
|
||||
{ 50, "orange"},
|
||||
{999, "green" },
|
||||
},
|
||||
listen = true,
|
||||
timeout = 10,
|
||||
widget_text = "${AC_BAT}${color_on}${percent}%${color_off}",
|
||||
widget_font = "Deja Vu Sans Mono 16",
|
||||
tooltip_text = "Battery ${state}${time_est}\nCapacity: ${capacity_percent}%",
|
||||
alert_threshold = 5,
|
||||
alert_timeout = 0,
|
||||
alert_title = "Low battery !",
|
||||
alert_text = "${AC_BAT}${time_est}",
|
||||
alert_icon = "~/Downloads/low_battery_icon.png",
|
||||
warn_full_battery = true,
|
||||
full_battery_icon = "~/Downloads/full_battery_icon.png",
|
||||
}
|
||||
```
|
||||
|
||||
`adapter`
|
||||
The name of the directory entry in `/sys/class/power_supply` corresponding to the requested battery adapter.
|
||||
|
||||
`ac`
|
||||
The name of the directory entry in `/sys/class/power_supply` corresponding to your AC status.
|
||||
|
||||
`ac_prefix`
|
||||
The prefix to populate `${AC_BAT}` when your computer is using ac power. If your font supports unicode characters, you could use "🔌".
|
||||
|
||||
`battery_prefix`
|
||||
The prefix to populate `${AC_BAT}` when your computer is using battery power. If your font supports unicode characters, you could use "🔋". Can also be configured as a table like `percent_colors` to show different prefixes at different battery percentages.
|
||||
|
||||
`percent_colors` (`limits` for backwards compatibility)
|
||||
The colors that the percentage changes to, as well as the upper-bound limit of when it will change. Ex. `{100, "green"}` means any percentage lower than 100 is colored green.
|
||||
|
||||
`listen`
|
||||
Tells the widget to listen to updates via `acpi_listen`. When an event is fired, the widget updates.
|
||||
|
||||
`timeout`
|
||||
The time interval that the widget waits before it updates itself, in seconds.
|
||||
|
||||
`widget_text`, `tooltip_text`
|
||||
The text which shows up on the toolbar and when you highlight the widget, respectively. Please refer to function `battery_widget:update()` for other interpolatable variables.
|
||||
|
||||
`widget_font`
|
||||
The font description used for the widget text, for instance "Deja Vu Sans Mono 16". If this is empty or unspecified, the default font will be used.
|
||||
|
||||
`alert_threshold`
|
||||
The percentage used as the maximum value at which an alert will be generated, `-1` to disable alerts. Once the alert is dismissed (or expired) it will not show up again until the battery has been charging.
|
||||
|
||||
`alert_timeout`
|
||||
The time after which the alert expire, `0` for no timeout.
|
||||
|
||||
`alert_title`, `alert_text`, `alert_icon`
|
||||
The text which shows up on the alert notification, respectively the title, body text and image path.
|
||||
|
||||
`warn_full_battery`, boolean
|
||||
Whether a notification should be displayed when the battery gets fully charged.
|
||||
|
||||
`full_battery_icon`
|
||||
Path to the image, which should be shown as part of the notification when battery gets fully charged (depends on `warn_full_battery`).
|
||||
|
||||
### Usage Examples
|
||||
|
||||
Percentage tables can be used for `ac_prefix`, `battery_prefix`, and `percent_colors` to show different things depending on the battery charge level, e.g.:
|
||||
|
||||
```lua
|
||||
battery_widget {
|
||||
-- Show different prefixes when charging on AC
|
||||
ac_prefix = {
|
||||
{ 25, "not charged" },
|
||||
{ 50, "1/4 charged" },
|
||||
{ 75, "2/4 charged" },
|
||||
{ 95, "3/4 charged" },
|
||||
{100, "fully charged" },
|
||||
},
|
||||
|
||||
-- Show a visual indicator of charge level when on battery power
|
||||
battery_prefix = {
|
||||
{ 25, "#--- "},
|
||||
{ 50, "##-- "},
|
||||
{ 75, "###- "},
|
||||
{100, "#### "},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ac_prefix`, `battery_prefix`, and `widget_text` can be further customized with spans to specify colors or fonts, e.g.:
|
||||
|
||||
```lua
|
||||
battery_widget {
|
||||
-- Use different colors for ac_prefix and battery_prefix
|
||||
ac_prefix = '<span color="red">AC: </span>',
|
||||
battery_prefix = '<span color="green">Bat: </span>',
|
||||
|
||||
-- Use a bold font for both prefixes (overrides widget_font)
|
||||
widget_text = '<span font="Deja Vu Sans Bold 16">${AC_BAT}</span>${color_on}${percent}%${color_off}'
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
-- Battery widget
|
||||
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local wibox = require("wibox")
|
||||
local naughty = require("naughty")
|
||||
|
||||
local timer = gears.timer or timer
|
||||
local watch = awful.spawn and awful.spawn.with_line_callback
|
||||
|
||||
------------------------------------------
|
||||
-- Private utility functions
|
||||
------------------------------------------
|
||||
|
||||
local tolower = string.lower
|
||||
|
||||
local function file_exists(command)
|
||||
local f = io.open(command)
|
||||
if f then f:close() end
|
||||
return f and true or false
|
||||
end
|
||||
|
||||
local function readfile(command)
|
||||
local file = io.open(command)
|
||||
if not file then return nil end
|
||||
local text = file:read('*all')
|
||||
file:close()
|
||||
return text
|
||||
end
|
||||
|
||||
local function color_tags(color)
|
||||
if color
|
||||
then return '<span color="' .. color .. '">', '</span>'
|
||||
else return '', ''
|
||||
end
|
||||
end
|
||||
|
||||
local function round(value)
|
||||
return math.floor(value + 0.5)
|
||||
end
|
||||
|
||||
local function trim(s)
|
||||
if not s then return nil end
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
local function read_trim(filename)
|
||||
return trim(readfile(filename)) or ""
|
||||
end
|
||||
|
||||
local function substitute(template, context)
|
||||
if type(template) == "string" then
|
||||
return (template:gsub("%${([%w_]+)}", function(key)
|
||||
return tostring(context[key] or "Err!")
|
||||
end))
|
||||
else
|
||||
-- function / functor:
|
||||
return template(context)
|
||||
end
|
||||
end
|
||||
|
||||
local function lookup_by_limits(limits, value)
|
||||
if type(limits) == "table" then
|
||||
local last = nil
|
||||
if value then
|
||||
for k, v in ipairs(limits) do
|
||||
if (value <= v[1]) then
|
||||
return v[2]
|
||||
end
|
||||
last = v[2]
|
||||
end
|
||||
end
|
||||
return last
|
||||
else
|
||||
return limits
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------
|
||||
-- Battery widget interface
|
||||
------------------------------------------
|
||||
|
||||
local battery_widget = {}
|
||||
local sysfs_names = {
|
||||
charging = {
|
||||
present = "present",
|
||||
state = "status",
|
||||
rate = "current_now",
|
||||
charge = "charge_now",
|
||||
capacity = "charge_full",
|
||||
design = "charge_full_design",
|
||||
percent = "capacity",
|
||||
},
|
||||
discharging = {
|
||||
present = "present",
|
||||
state = "status",
|
||||
rate = "power_now",
|
||||
charge = "energy_now",
|
||||
capacity = "energy_full",
|
||||
design = "energy_full_design",
|
||||
percent = "capacity"
|
||||
},
|
||||
}
|
||||
|
||||
function battery_widget:new(args)
|
||||
if args.adapter then
|
||||
return setmetatable({}, {__index = self}):init(args)
|
||||
end
|
||||
-- creates an empty container wibox, which can be added to your panel even if its empty
|
||||
local widgets = { layout = wibox.layout.fixed.horizontal }
|
||||
local batteries, mains, usb, ups = self:discover()
|
||||
local ac = mains[1] or usb[1] or ups[1]
|
||||
for i, adapter in ipairs(batteries) do
|
||||
local _args = setmetatable({adapter = adapter, ac = ac}, {__index = args})
|
||||
table.insert(widgets, self(_args).widget)
|
||||
end
|
||||
return widgets
|
||||
end
|
||||
|
||||
function battery_widget:discover()
|
||||
local pow = "/sys/class/power_supply/"
|
||||
local adapters = { Battery = {}, UPS = {}, Mains = {}, USB = {} }
|
||||
for adapter in io.popen("ls -1 " .. pow):lines() do
|
||||
local type = read_trim(pow .. adapter .. "/type")
|
||||
table.insert(adapters[type], adapter)
|
||||
end
|
||||
return adapters.Battery, adapters.Mains, adapters.USB, adapters.UPS
|
||||
end
|
||||
|
||||
function battery_widget:init(args)
|
||||
self.ac = args.ac or "AC"
|
||||
self.adapter = args.adapter or "BAT0"
|
||||
self.ac_prefix = args.ac_prefix or "AC: "
|
||||
self.battery_prefix = args.battery_prefix or "Bat: "
|
||||
self.percent_colors = args.percent_colors or args.limits or {
|
||||
{ 25, "red" },
|
||||
{ 50, "orange"},
|
||||
{999, "green" },
|
||||
}
|
||||
|
||||
self.widget_text = args.widget_text or (
|
||||
"${AC_BAT}${color_on}${percent}%${color_off}")
|
||||
self.tooltip_text = args.tooltip_text or (
|
||||
"Battery ${state}${time_est}\nCapacity: ${capacity_percent}%")
|
||||
|
||||
self.alert_threshold = args.alert_threshold or 5
|
||||
self.alert_timeout = args.alert_timeout or 0
|
||||
self.alert_title = args.alert_title or "Low battery !"
|
||||
self.alert_text = args.alert_text or "${AC_BAT}${time_est}"
|
||||
self.alert_icon = args.alert_icon or nil
|
||||
|
||||
self.widget = wibox.widget.textbox()
|
||||
self.widget.font = args.widget_font
|
||||
self.tooltip = awful.tooltip({objects={self.widget}})
|
||||
|
||||
self.warn_full_battery = args.warn_full_battery
|
||||
self.full_battery_icon = args.full_battery_icon or nil
|
||||
|
||||
self.widget:buttons(awful.util.table.join(
|
||||
awful.button({ }, 1, function() self:update() end),
|
||||
awful.button({ }, 3, function() self:update() end)
|
||||
))
|
||||
|
||||
self.timer = timer({ timeout = args.timeout or 10 })
|
||||
self.timer:connect_signal("timeout", function() self:update() end)
|
||||
self.timer:start()
|
||||
self:update()
|
||||
|
||||
if (args.listen or args.listen == nil) and watch then
|
||||
self.listener = watch("acpi_listen", {
|
||||
stdout = function(line) self:update() end,
|
||||
})
|
||||
awesome.connect_signal("exit", function()
|
||||
awesome.kill(self.listener, awesome.unix_signal.SIGTERM)
|
||||
end)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function battery_widget:get_state()
|
||||
local pow = "/sys/class/power_supply/"
|
||||
local ac = pow .. self.ac
|
||||
local bat = pow .. self.adapter
|
||||
local sysfs = (file_exists(bat.."/"..sysfs_names.charging.rate)
|
||||
and sysfs_names.charging
|
||||
or sysfs_names.discharging)
|
||||
|
||||
-- If there is no battery on this machine.
|
||||
if not sysfs.state then return nil end
|
||||
|
||||
-- return value
|
||||
local r = {
|
||||
state = tolower (read_trim(bat.."/"..sysfs.state)),
|
||||
present = tonumber(read_trim(bat.."/"..sysfs.present)),
|
||||
rate = tonumber(read_trim(bat.."/"..sysfs.rate)),
|
||||
charge = tonumber(read_trim(bat.."/"..sysfs.charge)),
|
||||
capacity = tonumber(read_trim(bat.."/"..sysfs.capacity)),
|
||||
design = tonumber(read_trim(bat.."/"..sysfs.design)),
|
||||
percent = tonumber(read_trim(bat.."/"..sysfs.percent)),
|
||||
}
|
||||
|
||||
r.ac_state = tonumber(read_trim(ac.."/online"))
|
||||
|
||||
if r.state == "unknown" then
|
||||
r.state = "charged"
|
||||
end
|
||||
|
||||
if r.percent == nil and r.charge and r.capacity then
|
||||
r.percent = round(r.charge * 100 / r.capacity)
|
||||
end
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
function battery_widget:update()
|
||||
local ctx = self:get_state()
|
||||
|
||||
-- If there is no battery on this machine.
|
||||
if not ctx then return nil end
|
||||
|
||||
-- AC/battery prefix
|
||||
ctx.AC_BAT = (ctx.ac_state == 1
|
||||
and lookup_by_limits(self.ac_prefix, ctx.percent)
|
||||
or lookup_by_limits(self.battery_prefix, ctx.percent)
|
||||
or "Err!")
|
||||
|
||||
-- Colors
|
||||
ctx.color_on, ctx.color_off = color_tags(
|
||||
lookup_by_limits(self.percent_colors, ctx.percent))
|
||||
|
||||
-- estimate time
|
||||
ctx.charge_dir = 0 -- +1|0|-1 -> charging|static|discharging
|
||||
ctx.time_left = nil -- time until charging/discharging complete
|
||||
ctx.time_text = ""
|
||||
ctx.time_est = ""
|
||||
|
||||
if ctx.rate and ctx.rate ~= 0 then
|
||||
if not ctx.state or ctx.state == "discharging" then
|
||||
ctx.charge_dir = -1
|
||||
ctx.time_left = ctx.charge / ctx.rate
|
||||
elseif ctx.state == "charging" then
|
||||
ctx.charge_dir = 1
|
||||
ctx.time_left = (ctx.capacity - ctx.charge) / ctx.rate
|
||||
end
|
||||
end
|
||||
|
||||
if ctx.time_left then
|
||||
ctx.hours = math.floor((ctx.time_left))
|
||||
ctx.minutes = math.floor((ctx.time_left - ctx.hours) * 60)
|
||||
if ctx.hours > 0
|
||||
then ctx.time_text = ctx.hours .. "h " .. ctx.minutes .. "m"
|
||||
else ctx.time_text = ctx.minutes .. "m"
|
||||
end
|
||||
ctx.time_est = ": " .. ctx.time_text .. " remaining"
|
||||
end
|
||||
|
||||
-- capacity text
|
||||
if ctx.capacity and ctx.design then
|
||||
ctx.capacity_percent = round(ctx.capacity/ctx.design*100)
|
||||
end
|
||||
|
||||
-- for use in functions
|
||||
ctx.obj = self
|
||||
|
||||
-- update text
|
||||
self.widget:set_markup(substitute(self.widget_text, ctx))
|
||||
self.tooltip:set_text(substitute(self.tooltip_text, ctx))
|
||||
|
||||
-- low battery notification
|
||||
if naughty then
|
||||
if (ctx.state == "discharging" and
|
||||
ctx.percent and ctx.percent <= self.alert_threshold) then
|
||||
self:notify(substitute(self.alert_title, ctx),
|
||||
substitute(self.alert_text, ctx),
|
||||
self.alert_icon)
|
||||
elseif ctx.state == "full" and self.warn_full_battery then
|
||||
self:notify('Battery Full!', 'Remove power cord', self.full_battery_icon)
|
||||
else
|
||||
if self.alert then
|
||||
naughty.destroy(
|
||||
self.alert,
|
||||
naughty.notificationClosedReason.dismissedByCommand)
|
||||
self.alert = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function battery_widget:notify(title, text, icon)
|
||||
if self.alert then
|
||||
naughty.replace_text(self.alert, title, text)
|
||||
else
|
||||
self.alert = naughty.notify({
|
||||
title = title,
|
||||
text = text,
|
||||
icon = icon,
|
||||
preset = naughty.config.presets.critical,
|
||||
timeout = self.alert_timeout
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(battery_widget, {
|
||||
__call = battery_widget.new,
|
||||
})
|
||||
|
|
@ -0,0 +1 @@
|
|||
battery-widget.lua
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
|
|
@ -0,0 +1,93 @@
|
|||
## awesome-brightness
|
||||
|
||||
Brightness indicator/control widget for [awesome wm](https://awesomewm.org/)
|
||||
based on ``xbacklight`` or ``brightnessctl``.
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
The module requires either `xbacklight` or `brightnessctl` to work.
|
||||
Thus, on archlinux, you'll need to install at least one of the following
|
||||
system packages:
|
||||
|
||||
- [acpilight](https://archlinux.org/packages/extra/any/acpilight/) or
|
||||
[xorg-xbacklight](https://archlinux.org/packages/extra/x86_64/xorg-xbacklight/) for `xbacklight`
|
||||
- [brightnessctl](https://archlinux.org/packages/extra/x86_64/brightnessctl/) for `brightnessctl`
|
||||
|
||||
I've experienced `xorg-xbacklight` not work on certain laptops. So, if you
|
||||
find that the widget is not working, try a different backend.
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
In your `~/.config/awesome/rc.lua`:
|
||||
|
||||
```lua
|
||||
local deficient = require("deficient")
|
||||
|
||||
|
||||
-- instanciate widget
|
||||
local brightness_ctrl = deficient.brightness {
|
||||
-- pass options here
|
||||
}
|
||||
|
||||
|
||||
-- add widget to the wibox:
|
||||
s.mywibox:setup {
|
||||
...,
|
||||
{ -- Right widgets
|
||||
...,
|
||||
brightness_ctrl.widget,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Note that you need to pass `.widget` to the wibox, not the instance itself!
|
||||
|
||||
The flag `brightness_ctrl.is_valid` indicates successful initialization.
|
||||
|
||||
|
||||
### Usage options
|
||||
|
||||
Full example:
|
||||
|
||||
```lua
|
||||
local brightness_ctrl = require("deficient.brightness") {
|
||||
backend = nil,
|
||||
step = 5,
|
||||
timeout = 3,
|
||||
levels = {1, 25, 50, 75, 100},
|
||||
}
|
||||
```
|
||||
|
||||
`backend`
|
||||
Picks command with which to perform brightness queries and updates.
|
||||
Allowed values are `nil` (meaning *autodetect*), `"xbacklight"` or
|
||||
`"brightnessctl"`. Default: `nil`.
|
||||
|
||||
`step`
|
||||
How many percentage points to increase or decrease the brightness level when
|
||||
clicking the widget. Default: 3.
|
||||
|
||||
`timeout`
|
||||
Interval in seconds at which to check the current brightness level and update
|
||||
the widget text. Default: 5.
|
||||
|
||||
`levels`
|
||||
Cycle through these brightness percentages on middle-click.
|
||||
Default: ``{1, 25, 50, 75, 100}`.
|
||||
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If you get errors on startup, try executing `xbacklight -get` or
|
||||
`brightnessctl -c backlight get` in a terminal.
|
||||
|
||||
If you get the error "No outputs have backlight property", make sure you have
|
||||
installed an appropriate display driver, e.g. for intel cards:
|
||||
|
||||
```bash
|
||||
sudo pacman -S xf86-video-intel
|
||||
```
|
||||
|
||||
You may need to restart afterwards.
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
--[[
|
||||
|
||||
Brightness control
|
||||
==================
|
||||
|
||||
based on `xbacklight`!
|
||||
|
||||
alternative ways to control brightness:
|
||||
sudo setpci -s 00:02.0 F4.B=80
|
||||
xgamma -gamma .75
|
||||
xrandr --output LVDS1 --brightness 0.9
|
||||
echo X > /sys/class/backlight/intel_backlight/brightness
|
||||
xbacklight
|
||||
|
||||
--]]
|
||||
|
||||
local awful = require("awful")
|
||||
local wibox = require("wibox")
|
||||
local gears = require("gears")
|
||||
local gtable = require("gears.table")
|
||||
local naughty = require("naughty")
|
||||
|
||||
local timer = gears.timer or timer
|
||||
local exec = awful.spawn.easy_async
|
||||
|
||||
|
||||
------------------------------------------
|
||||
-- Private utility functions
|
||||
------------------------------------------
|
||||
|
||||
local function warning(text)
|
||||
if not naughty then return end
|
||||
local args = {
|
||||
title = "Brightness Control",
|
||||
preset = naughty.config.presets.normal,
|
||||
}
|
||||
if naughty.notification then
|
||||
args.message = text
|
||||
naughty.notification(args)
|
||||
else
|
||||
args.text = text
|
||||
naughty.notify(args)
|
||||
end
|
||||
end
|
||||
|
||||
local function readcommand(command)
|
||||
-- I know, you should *never* use `io.popen`, but it's called at most once
|
||||
-- per backend through the whole awesome session… I promise!
|
||||
local file = io.popen(command)
|
||||
local text = file:read('*all')
|
||||
file:close()
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------
|
||||
-- Backend: brightnessctl
|
||||
------------------------------------------
|
||||
|
||||
local backends = {}
|
||||
|
||||
backends.brightnessctl = {
|
||||
cmd = "brightnessctl",
|
||||
_max = nil,
|
||||
|
||||
supported = function(self)
|
||||
return tonumber(readcommand("brightnessctl --class=backlight max")) ~= nil
|
||||
end,
|
||||
|
||||
parse_output = function(self, output)
|
||||
-- dev,class,curr,percent,max
|
||||
local _, _, _, percent, _ = output:match("(.*),(.*),(%d*),(%d*)%%,(%d*)")
|
||||
return tonumber(percent)
|
||||
end,
|
||||
|
||||
get = function(self, callback)
|
||||
exec({ self.cmd, "--class=backlight", "-m", "info" }, function(output)
|
||||
callback(self:parse_output(output))
|
||||
end)
|
||||
end,
|
||||
|
||||
set = function(self, percent, callback)
|
||||
exec({ self.cmd, "--class=backlight", "-m", "set", percent .. "%" }, function(output)
|
||||
callback(self:parse_output(output))
|
||||
end)
|
||||
end,
|
||||
|
||||
up = function(self, step, callback)
|
||||
exec({ self.cmd, "--class=backlight", "-m", "set", step .. "%+" }, function(output)
|
||||
callback(self:parse_output(output))
|
||||
end)
|
||||
end,
|
||||
|
||||
down = function(self, step, callback)
|
||||
exec({ self.cmd, "--class=backlight", "-m", "set", step .. "%-" }, function(output)
|
||||
callback(self:parse_output(output))
|
||||
end)
|
||||
end,
|
||||
}
|
||||
|
||||
------------------------------------------
|
||||
-- Backend: xbacklight
|
||||
------------------------------------------
|
||||
|
||||
backends.xbacklight = {
|
||||
cmd = "xbacklight",
|
||||
|
||||
supported = function(self)
|
||||
return tonumber(readcommand("xbacklight -get")) ~= nil
|
||||
end,
|
||||
|
||||
get = function(self, callback)
|
||||
exec({self.cmd, "-get"}, function(output)
|
||||
callback(tonumber(output))
|
||||
end)
|
||||
end,
|
||||
|
||||
set = function(self, value, callback)
|
||||
exec({self.cmd, "-set", tostring(value)}, callback)
|
||||
end,
|
||||
|
||||
up = function(self, step, callback)
|
||||
exec({self.cmd, "-inc", tostring(step)}, callback)
|
||||
end,
|
||||
|
||||
down = function(self, step, callback)
|
||||
exec({self.cmd, "-dec", tostring(step)}, callback)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
------------------------------------------
|
||||
-- Brightness control interface
|
||||
------------------------------------------
|
||||
local bcontrol = { backends = backends }
|
||||
|
||||
function bcontrol:new(args)
|
||||
return setmetatable({}, {__index = self}):init(args)
|
||||
end
|
||||
|
||||
function bcontrol:init(args)
|
||||
-- determine backend
|
||||
local backend = args.backend
|
||||
|
||||
if type(backend) == "string" then
|
||||
backend = backends[backend]
|
||||
if backend == nil then
|
||||
warning("Unknown backend: " .. args.backend)
|
||||
end
|
||||
end
|
||||
|
||||
if backend == nil then
|
||||
if backends.brightnessctl:supported() then
|
||||
backend = backends.brightnessctl
|
||||
elseif backends.xbacklight:supported() then
|
||||
backend = backends.xbacklight
|
||||
else
|
||||
backend = nil
|
||||
warning("Neither brightnessctl nor xbacklight seems to work")
|
||||
end
|
||||
end
|
||||
|
||||
self.is_valid = backend ~= nil
|
||||
self.backend = backend
|
||||
self.step = tonumber(args.step or '5')
|
||||
self.levels = args.levels or {1, 25, 50, 75, 100}
|
||||
|
||||
self.widget = wibox.widget.textbox()
|
||||
|
||||
if self.is_valid then
|
||||
self.widget:buttons(gtable.join(
|
||||
awful.button({ }, 1, function() self:up() end),
|
||||
awful.button({ }, 3, function() self:down() end),
|
||||
awful.button({ }, 2, function() self:toggle() end),
|
||||
awful.button({ }, 4, function() self:up(1) end),
|
||||
awful.button({ }, 5, function() self:down(1) end)
|
||||
))
|
||||
|
||||
self.timer = timer({
|
||||
timeout = args.timeout or 3,
|
||||
callback = function() self:update() end,
|
||||
autostart = true,
|
||||
call_now = true
|
||||
})
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function bcontrol:set_text(value)
|
||||
local brightness = math.floor(0.5 + value)
|
||||
self.widget:set_text(string.format(" [%3d] ", brightness))
|
||||
end
|
||||
|
||||
function bcontrol:update(opt_value)
|
||||
if opt_value and string.match(opt_value, "%S+") then
|
||||
self:set_text(opt_value)
|
||||
else
|
||||
self.backend:get(function(...) self:set_text(...) end)
|
||||
end
|
||||
end
|
||||
|
||||
function bcontrol:set(brightness, callback)
|
||||
self.backend:set(brightness, callback or function(...) self:update(...) end)
|
||||
end
|
||||
|
||||
function bcontrol:up(step, callback)
|
||||
self.backend:up(step or self.step, callback or function(...) self:update(...) end)
|
||||
end
|
||||
|
||||
function bcontrol:down(step, callback)
|
||||
self.backend:down(step or self.step, callback or function(...) self:update(...) end)
|
||||
end
|
||||
|
||||
function bcontrol:toggle()
|
||||
self.backend:get(function(value)
|
||||
local ilevel = 1
|
||||
for i, lv in ipairs(self.levels) do
|
||||
if math.abs(lv - value) < math.abs(self.levels[ilevel] - value) then
|
||||
ilevel = i
|
||||
end
|
||||
end
|
||||
self:set(self.levels[ilevel % #(self.levels) + 1])
|
||||
end)
|
||||
end
|
||||
|
||||
return setmetatable(bcontrol, {
|
||||
__call = bcontrol.new,
|
||||
})
|
||||
-- vim: set ts=4 sw=4 et:
|
||||
|
|
@ -0,0 +1 @@
|
|||
brightness.lua
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
## awesome-calendar
|
||||
|
||||
Small calendar popup for awesome window manager.
|
||||
|
||||

|
||||
|
||||
This is a polished up and improved module based on the `calendar2.lua` module
|
||||
by Bernd Zeimetz and Marc Dequènes.
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
In your `rc.lua`:
|
||||
|
||||
```lua
|
||||
local deficient = require("deficient")
|
||||
|
||||
|
||||
-- instanciate widget
|
||||
local calendar_widget = deficient.calendar({})
|
||||
|
||||
-- attach it as popup to your text clock widget
|
||||
calendar_widget:attach(mytextclock)
|
||||
```
|
||||
|
||||
You can also add some options to customize the widget's display. For instance:
|
||||
|
||||
```
|
||||
calendar_widget = deficient.calendar({
|
||||
fdow = 7, -- Set Sunday as first day of the week (default is
|
||||
-- 1 = Monday)
|
||||
position = "bottom_right", -- Useful if you prefer your wibox at the bottomn
|
||||
-- of the screen
|
||||
})
|
||||
calendar_widget:attach(mytextclock)
|
||||
```
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
-- original code made by Bzed and published on http://awesome.naquadah.org/wiki/Calendar_widget
|
||||
-- modified by Marc Dequènes (Duck) <Duck@DuckCorp.org> (2009-12-29), under the same licence,
|
||||
-- and with the following changes:
|
||||
-- + transformed to module
|
||||
-- + the current day formating is customizable
|
||||
|
||||
-- captures
|
||||
local os = os
|
||||
local capi = {
|
||||
mouse = mouse,
|
||||
screen = screen,
|
||||
}
|
||||
local awful = require("awful")
|
||||
local naughty = require("naughty")
|
||||
|
||||
local version_major, version_minor = awesome.version:match("(%d+)%.(%d+)")
|
||||
version_major = tonumber(version_major)
|
||||
version_minor = tonumber(version_minor)
|
||||
local can_update_size = version_major and version_minor and version_major >= 4 and version_minor >= 2
|
||||
|
||||
------------------------------------------
|
||||
-- utility functions
|
||||
------------------------------------------
|
||||
|
||||
local function format_date(format, date)
|
||||
return os.date(format, os.time(date))
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------
|
||||
-- calendar popup widget
|
||||
------------------------------------------
|
||||
|
||||
local calendar = {}
|
||||
|
||||
function calendar:new(args)
|
||||
return setmetatable({}, {__index = self}):init(args)
|
||||
end
|
||||
|
||||
function calendar:init(args)
|
||||
self.num_lines = 0
|
||||
self.today_color = args.today_color or "#00ff00"
|
||||
-- first day of week: monday=1, …, sunday=7
|
||||
self.fdow = args.fdow or 1
|
||||
-- notification area:
|
||||
self.html = args.html or '<span font_desc="monospace">\n%s</span>'
|
||||
-- highlight current date:
|
||||
self.today = args.today or '<b><span color="' .. self.today_color .. '">%2i</span></b>'
|
||||
self.anyday = args.anyday or '%2i'
|
||||
self.page_title = args.page_title or '%B %Y' -- month year
|
||||
self.col_title = args.col_title or '%a ' -- weekday
|
||||
-- Date equality check is based on day_id. We deliberately ignore the year
|
||||
-- to highlight the same day in different years:
|
||||
self.day_id = args.day_id or '%m-%d'
|
||||
self.empty_sep = args.empty_sep or " -"
|
||||
self.week_col = args.week_col or " %V"
|
||||
self.days_style = args.days_style or {}
|
||||
self.position = args.position or naughty.config.defaults.position
|
||||
return self
|
||||
end
|
||||
|
||||
function calendar:day_style(day_of_week)
|
||||
return self.days_style[day_of_week] or '%s'
|
||||
end
|
||||
|
||||
function calendar:page(month, year)
|
||||
|
||||
local today = format_date(self.day_id)
|
||||
|
||||
-- 2001 started with a monday:
|
||||
local d0 = format_date("*t", {year=2001, month=1, day=self.fdow })
|
||||
local dA = format_date("*t", {year=year, month=month, day=1 })
|
||||
local dB = format_date("*t", {year=year, month=month+1, day=0 })
|
||||
local tA = {year=year, month=month, day=1 }
|
||||
local colA = (dA.wday - d0.wday) % 7
|
||||
|
||||
local page_title = format_date(self.page_title, tA)
|
||||
|
||||
-- print column titles (weekday)
|
||||
local page = " "
|
||||
for d = 0, 6 do
|
||||
page = page .. self:day_style(d+1):format(format_date(self.col_title, {
|
||||
year = d0.year,
|
||||
month = d0.month,
|
||||
day = d0.day + d,
|
||||
}))
|
||||
end
|
||||
|
||||
-- print empty space before first day
|
||||
page = page .. "\n" .. format_date(self.week_col, tA)
|
||||
for column = 1, colA do
|
||||
page = page .. self.empty_sep
|
||||
end
|
||||
|
||||
-- iterate all days of the month
|
||||
local nLines = 1
|
||||
local column = colA
|
||||
for day = 1, dB.day do
|
||||
if column == 7 then
|
||||
column = 0
|
||||
nLines = nLines + 1
|
||||
page = page .. "\n" .. format_date(self.week_col, {year=year, month=month, day=day})
|
||||
end
|
||||
if today == format_date(self.day_id, {day=day, month=month, year=year}) then
|
||||
page = page .. " " .. self.today:format(day)
|
||||
else
|
||||
page = page .. " " .. self:day_style(column+1):format(self.anyday:format(day))
|
||||
end
|
||||
column = column + 1
|
||||
end
|
||||
|
||||
for column = column, 6 do
|
||||
page = page .. self.empty_sep
|
||||
end
|
||||
|
||||
return page_title, self.html:format(page)
|
||||
end
|
||||
|
||||
function calendar:switch(months)
|
||||
self:show(self.year, self.month+months)
|
||||
end
|
||||
|
||||
function calendar:show(year, month)
|
||||
local today = os.time()
|
||||
self.month = month or os.date('%m', today)
|
||||
self.year = year or os.date('%Y', today)
|
||||
local title, text = self:page(self.month, self.year)
|
||||
|
||||
-- NOTE: `naughty.replace_text` does not update bounds and can therefore
|
||||
-- not be used when the size increases (before #1756 was merged):
|
||||
local num_lines = select(2, text:gsub('\n', ''))
|
||||
local will_fit = can_update_size or num_lines <= self.num_lines
|
||||
if naughty.replace_text and self.notification and will_fit then
|
||||
naughty.replace_text(self.notification, title, text)
|
||||
else
|
||||
self:hide()
|
||||
self.notification = naughty.notify({
|
||||
title = title,
|
||||
text = text,
|
||||
timeout = 0,
|
||||
hover_timeout = 0.5,
|
||||
screen = capi.mouse.screen,
|
||||
position = self.position,
|
||||
})
|
||||
self.num_lines = num_lines
|
||||
end
|
||||
end
|
||||
|
||||
function calendar:hide()
|
||||
if self.notification then
|
||||
naughty.destroy(self.notification)
|
||||
self.notification = nil
|
||||
self.num_lines = 0
|
||||
end
|
||||
end
|
||||
|
||||
function calendar:attach(widget)
|
||||
widget:connect_signal('mouse::enter', function() self:show() end)
|
||||
widget:connect_signal('mouse::leave', function() self:hide() end)
|
||||
widget:buttons(awful.util.table.join(
|
||||
awful.button({ }, 1, function() self:switch( -1) end),
|
||||
awful.button({ }, 3, function() self:switch( 1) end),
|
||||
awful.button({ }, 4, function() self:switch( -1) end),
|
||||
awful.button({ }, 5, function() self:switch( 1) end),
|
||||
awful.button({ 'Shift' }, 1, function() self:switch(-12) end),
|
||||
awful.button({ 'Shift' }, 3, function() self:switch( 12) end),
|
||||
awful.button({ 'Shift' }, 4, function() self:switch(-12) end),
|
||||
awful.button({ 'Shift' }, 5, function() self:switch( 12) end)
|
||||
))
|
||||
end
|
||||
|
||||
return setmetatable(calendar, {
|
||||
__call = calendar.new,
|
||||
})
|
||||
|
|
@ -0,0 +1 @@
|
|||
calendar.lua
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
8
home-modules/explicit-configs/awesome/deficient/init.lua
Normal file
8
home-modules/explicit-configs/awesome/deficient/init.lua
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
local modname = ...
|
||||
local function load_submodule(table, key)
|
||||
local submodule = require(modname .. "." .. key:gsub("_", "-"))
|
||||
table[key] = submodule
|
||||
return submodule
|
||||
end
|
||||
|
||||
return setmetatable({}, {__index = load_submodule});
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
## awesome.volume-control
|
||||
|
||||
Volume indicator+control widget for awesome window manager.
|
||||
|
||||

|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
Optional but recommended dependencies:
|
||||
|
||||
* pavucontrol (optional)
|
||||
* acpid (optional)
|
||||
|
||||
```bash
|
||||
pacman -S pavucontrol # open volume manager with middle/right click
|
||||
pacman -S acpid # instant status updates (acpi_listen)
|
||||
systemctl enable acpid
|
||||
```
|
||||
|
||||
You will also need `amixer` and `alsactl`, most likely your distro has a
|
||||
package called `alsa-utils` that contains them.
|
||||
|
||||
If you are using `pipewire`, you have to configure it to manage clients
|
||||
using the userspace component of ALSA. For example on Arch Linux, this can
|
||||
be done by installing the package `pipewire-alsa`. For Debian, you can
|
||||
follow the instructions provided in the
|
||||
[Debian Wiki](https://wiki.debian.org/PipeWire#For_ALSA).
|
||||
|
||||
Similarly, if you are using `pulseaudio`, you need to configure it to manage
|
||||
clients using the userspace component of ALSA. For Arch Linux, that means
|
||||
installing the package `pulseaudio-alsa`.
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
In your `~/.config/awesome/rc.lua`:
|
||||
|
||||
```lua
|
||||
local deficient = require("deficient")
|
||||
|
||||
|
||||
-- instanciate volume control, using default settings:
|
||||
volumecfg = deficient.volume_control({})
|
||||
|
||||
|
||||
-- add the widget to your wibox
|
||||
...
|
||||
right_layout:add(volumecfg.widget)
|
||||
...
|
||||
|
||||
|
||||
-- add key bindings
|
||||
local globalkeys = awful.util.table.join(
|
||||
...
|
||||
awful.key({}, "XF86AudioRaiseVolume", function() volumecfg:up() end),
|
||||
awful.key({}, "XF86AudioLowerVolume", function() volumecfg:down() end),
|
||||
awful.key({}, "XF86AudioMute", function() volumecfg:toggle() end),
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### Known issues
|
||||
|
||||
One common pitfall is using the wrong sound device. On systems with pulseaudio,
|
||||
it's usually best to create the control with:
|
||||
|
||||
```lua
|
||||
volumecfg = deficient.volume_control {device="pulse"}
|
||||
```
|
||||
|
||||
On some systems, clicking the widget will mute audio, however clicking it again
|
||||
will only unmute *Master* while leaving other subsystems (Speaker, …) muted,
|
||||
see e.g. [#10](https://github.com/deficient/volume-control/pull/10). This may
|
||||
be fixed by setting the device to *pulse*, as described above.
|
||||
|
||||
For pre-2019 `alsa-utils`, if you have the `listen` enabled, unplugging USB
|
||||
headphones sometimes causes the process that monitors for audio status changes
|
||||
(`alsactl monitor`) to spin at 100% CPU, see
|
||||
[#11](https://github.com/deficient/volume-control/issues/11). When this
|
||||
happens, you can safely kill the process or restart awesome (`Mod4 + Control +
|
||||
R`). This bug was fixed in `alsa-utils 1.1.7`.
|
||||
|
||||
### Constructor
|
||||
|
||||
You can specify any subset of the following arguments to the constructor.
|
||||
The default values are as follows:
|
||||
|
||||
```lua
|
||||
volumecfg = deficient.volume_control({
|
||||
device = nil, -- e.g.: "default", "pulse"
|
||||
cardid = nil, -- e.g.: 0, 1, ...
|
||||
channel = "Master",
|
||||
step = '5%', -- step size for up/down
|
||||
lclick = "toggle", -- mouse actions described below
|
||||
mclick = "pavucontrol",
|
||||
rclick = "pavucontrol",
|
||||
listen = false, -- enable/disable listening for audio status changes
|
||||
widget = nil, -- use this instead of creating a awful.widget.textbox
|
||||
font = nil, -- font used for the widget's text
|
||||
callback = nil, -- called to update the widget: `callback(self, state)`
|
||||
widget_text = {
|
||||
on = '% 3d%% ', -- three digits, fill with leading spaces
|
||||
off = '% 3dM ',
|
||||
},
|
||||
tooltip_text = [[
|
||||
Volume: ${volume}% ${state}
|
||||
Channel: ${channel}
|
||||
Device: ${device}
|
||||
Card: ${card}]],
|
||||
})
|
||||
```
|
||||
|
||||
### Mouse actions
|
||||
|
||||
The easiest way to customize what happens on left/right/middle click is to
|
||||
specify additional arguments to the constructor. These can be of any of the
|
||||
following kinds:
|
||||
|
||||
- name of a member function: `"up"`, `"down"`, `"toggle"`, `"mute"`, `"get"`
|
||||
- command string to execute
|
||||
- a callable that will be called with the volume control as first parameter
|
||||
|
||||
E.g.:
|
||||
|
||||
```lua
|
||||
volumecfg = deficient.volume_control({
|
||||
lclick="toggle", -- name of member function
|
||||
mclick=TERMINAL .. " -x alsamixer", -- command to execute
|
||||
rclick=function(self) self:mute() end, -- callable, equivalent to "mute"
|
||||
})
|
||||
```
|
||||
|
||||
### Icon widget
|
||||
|
||||
You can use the module as a basis to implement your own volume widget. For
|
||||
example, an icon widget can be created as follows:
|
||||
|
||||
```lua
|
||||
local function get_image(volume, state)
|
||||
local icondir = os.getenv("HOME") .. "/.local/share/icons/"
|
||||
if volume == 0 or state == "off" then return icondir .. "audio_mute.png"
|
||||
elseif volume <= 33 then return icondir .. "audio_low.png"
|
||||
elseif volume <= 66 then return icondir .. "audio_med.png"
|
||||
else return icondir .. "audio_high.png"
|
||||
end
|
||||
end
|
||||
|
||||
local volume_widget = deficient.volume_control {
|
||||
tooltip = true,
|
||||
widget = wibox.widget.imagebox(),
|
||||
callback = function(self, setting)
|
||||
self.widget:set_image(
|
||||
get_image(setting.volume, setting.state))
|
||||
end,
|
||||
}
|
||||
```
|
||||
|
||||
However, in this case, I recommend to use
|
||||
[pasystray](https://github.com/christophgysin/pasystray) instead.
|
||||
|
||||
|
||||
### Alternatives
|
||||
|
||||
If you like a volume control with an icon instead of text, I suggest to use
|
||||
[pasystray](https://github.com/christophgysin/pasystray), which is a more
|
||||
comprehensive solution and built for the systray (not awesome widget) with a
|
||||
much nicer menu.
|
||||
|
|
@ -0,0 +1 @@
|
|||
volume-control.lua
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
|
|
@ -0,0 +1,262 @@
|
|||
-- Volume Control
|
||||
local awful = require("awful")
|
||||
local wibox = require("wibox")
|
||||
local gears = require("gears")
|
||||
local naughty = require("naughty")
|
||||
|
||||
-- compatibility fallbacks for 3.5:
|
||||
local timer = gears.timer or timer
|
||||
local spawn = awful.spawn or awful.util.spawn
|
||||
local watch = awful.spawn and awful.spawn.with_line_callback
|
||||
|
||||
local function exec(command, callback)
|
||||
awful.spawn.easy_async(command, callback or function() end)
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------
|
||||
-- Private utility functions
|
||||
------------------------------------------
|
||||
|
||||
local function substitute(template, context)
|
||||
if type(template) == "string" then
|
||||
return (template:gsub("%${([%w_]+)}", function(key)
|
||||
return tostring(context[key] or "default")
|
||||
end))
|
||||
else
|
||||
-- function / functor:
|
||||
return template(context)
|
||||
end
|
||||
end
|
||||
|
||||
local function new(self, ...)
|
||||
local instance = setmetatable({}, {__index = self})
|
||||
return instance:init(...) or instance
|
||||
end
|
||||
|
||||
local function class(base)
|
||||
return setmetatable({new = new}, {
|
||||
__call = new,
|
||||
__index = base,
|
||||
})
|
||||
end
|
||||
|
||||
------------------------------------------
|
||||
-- Volume control interface
|
||||
------------------------------------------
|
||||
|
||||
local vcontrol = class()
|
||||
|
||||
function vcontrol:init(args)
|
||||
self.callbacks = {}
|
||||
self.cmd = "amixer"
|
||||
self.device = args.device or nil
|
||||
self.cardid = args.cardid or nil
|
||||
self.channel = args.channel or "Master"
|
||||
self.step = args.step or '5%'
|
||||
|
||||
self.timer = timer({ timeout = args.timeout or 0.5 })
|
||||
self.timer:connect_signal("timeout", function() self:get() end)
|
||||
self.timer:start()
|
||||
|
||||
if args.listen and watch then
|
||||
self.listener = watch({'stdbuf', '-oL', 'alsactl', 'monitor'}, {
|
||||
stdout = function(line) self:get() end,
|
||||
})
|
||||
awesome.connect_signal("exit", function()
|
||||
awesome.kill(self.listener, awesome.unix_signal.SIGTERM)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function vcontrol:register(callback)
|
||||
if callback then
|
||||
table.insert(self.callbacks, callback)
|
||||
end
|
||||
end
|
||||
|
||||
function vcontrol:action(action)
|
||||
if self[action] then self[action](self)
|
||||
elseif type(action) == "function" then action(self)
|
||||
elseif type(action) == "string" then spawn(action)
|
||||
end
|
||||
end
|
||||
|
||||
function vcontrol:update(status)
|
||||
local volume = status:match("(%d?%d?%d)%%")
|
||||
local state = status:match("%[(o[nf]*)%]")
|
||||
if volume and state then
|
||||
local volume = tonumber(volume)
|
||||
local state = state:lower()
|
||||
local muted = state == "off"
|
||||
for _, callback in ipairs(self.callbacks) do
|
||||
callback(self, {
|
||||
volume = volume,
|
||||
state = state,
|
||||
muted = muted,
|
||||
on = not muted,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function vcontrol:mixercommand(args, callback)
|
||||
local args = awful.util.table.join(
|
||||
{self.cmd},
|
||||
(self.cmd == "amixer") and {"-M"} or {},
|
||||
self.device and {"-D", self.device} or {},
|
||||
self.cardid and {"-c", self.cardid} or {},
|
||||
args)
|
||||
exec(args, callback or function(output)
|
||||
self:update(output)
|
||||
end)
|
||||
end
|
||||
|
||||
function vcontrol:get()
|
||||
self:mixercommand({ "get", self.channel })
|
||||
end
|
||||
|
||||
function vcontrol:up()
|
||||
self:mixercommand({ "set", self.channel, self.step .. "+" })
|
||||
end
|
||||
|
||||
function vcontrol:down()
|
||||
self:mixercommand({ "set", self.channel, self.step .. "-" })
|
||||
end
|
||||
|
||||
function vcontrol:toggle()
|
||||
self:mixercommand({ "set", self.channel, "toggle" })
|
||||
end
|
||||
|
||||
function vcontrol:mute()
|
||||
self:mixercommand({ "set", "Master", "mute" })
|
||||
end
|
||||
|
||||
function vcontrol:unmute()
|
||||
self:mixercommand({ "set", "Master", "unmute" })
|
||||
end
|
||||
|
||||
function vcontrol:list_sinks(callback)
|
||||
exec("env LC_ALL=C pactl list sinks", function(output)
|
||||
local sinks = {}
|
||||
local sink
|
||||
for line in output:gmatch("[^\r\n]+") do
|
||||
if line:match("Sink #%d+") then
|
||||
sink = {}
|
||||
table.insert(sinks, sink)
|
||||
else
|
||||
local k, v = line:match("^%s*(%S+):%s*(.-)%s*$")
|
||||
if k and v then sink[k:lower()] = v end
|
||||
end
|
||||
end
|
||||
callback(sinks)
|
||||
end)
|
||||
end
|
||||
|
||||
function vcontrol:set_default_sink(name, callback)
|
||||
exec({"pactl set-default-sink", name}, callback)
|
||||
end
|
||||
|
||||
------------------------------------------
|
||||
-- Volume control widget
|
||||
------------------------------------------
|
||||
|
||||
-- derive so that users can still call up/down/mute etc
|
||||
local vwidget = class(vcontrol)
|
||||
|
||||
function vwidget:init(args)
|
||||
vcontrol.init(self, args)
|
||||
|
||||
self.lclick = args.lclick or "toggle"
|
||||
self.mclick = args.mclick or "pavucontrol"
|
||||
self.rclick = args.rclick or self.show_menu
|
||||
|
||||
self.font = args.font or nil
|
||||
self.widget = args.widget or (self:create_widget(args) or self.widget)
|
||||
self.tooltip = args.tooltip and (self:create_tooltip(args) or self.tooltip)
|
||||
|
||||
self:register(args.callback or self.update_widget)
|
||||
self:register(args.tooltip and self.update_tooltip)
|
||||
|
||||
self.widget:buttons(awful.util.table.join(
|
||||
awful.button({}, 1, function() self:action(self.lclick) end),
|
||||
awful.button({}, 2, function() self:action(self.mclick) end),
|
||||
awful.button({}, 3, function() self:action(self.rclick) end),
|
||||
awful.button({}, 4, function() self:up() end),
|
||||
awful.button({}, 5, function() self:down() end)
|
||||
))
|
||||
|
||||
self:get()
|
||||
end
|
||||
|
||||
-- text widget
|
||||
function vwidget:create_widget(args)
|
||||
self.widget_text = args.widget_text or {
|
||||
on = '% 3d%% ',
|
||||
off = '% 3dM ',
|
||||
}
|
||||
self.widget = wibox.widget.textbox()
|
||||
if self.font then
|
||||
self.widget.font = self.font
|
||||
end
|
||||
end
|
||||
|
||||
function vwidget:create_menu(callback)
|
||||
self:list_sinks(function(sinks)
|
||||
local sinks_submenu = {}
|
||||
for i, sink in ipairs(sinks) do
|
||||
table.insert(sinks_submenu, {sink.description, function()
|
||||
self:set_default_sink(sink.name)
|
||||
end})
|
||||
end
|
||||
callback(awful.menu { items = {
|
||||
{ "mute", function() self:mute() end },
|
||||
{ "unmute", function() self:unmute() end },
|
||||
{ "Default Sink", sinks_submenu },
|
||||
{ "pavucontrol", function() self:action("pavucontrol") end },
|
||||
} })
|
||||
end)
|
||||
end
|
||||
|
||||
function vwidget:show_menu()
|
||||
if self.menu then
|
||||
self.menu:hide()
|
||||
else
|
||||
self:create_menu(function(menu)
|
||||
self.menu = menu
|
||||
self.menu:show()
|
||||
self.menu.wibox:connect_signal("property::visible", function()
|
||||
self.menu = nil
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function vwidget:update_widget(setting)
|
||||
self.widget:set_markup(
|
||||
self.widget_text[setting.state]:format(setting.volume))
|
||||
end
|
||||
|
||||
-- tooltip
|
||||
function vwidget:create_tooltip(args)
|
||||
self.tooltip_text = args.tooltip_text or [[
|
||||
Volume: ${volume}% ${state}
|
||||
Channel: ${channel}
|
||||
Device: ${device}
|
||||
Card: ${card}]]
|
||||
self.tooltip = args.tooltip and awful.tooltip({objects={self.widget}})
|
||||
end
|
||||
|
||||
function vwidget:update_tooltip(setting)
|
||||
self.tooltip:set_text(substitute(self.tooltip_text, {
|
||||
volume = setting.volume,
|
||||
state = setting.state,
|
||||
device = self.device,
|
||||
card = self.card,
|
||||
channel = self.channel,
|
||||
}))
|
||||
end
|
||||
|
||||
-- provide direct access to the control class
|
||||
vwidget.control = vcontrol
|
||||
return vwidget
|
||||
Loading…
Add table
Add a link
Reference in a new issue