inital commit of clone of old repo

This commit is contained in:
Contegix Support
2015-04-12 18:06:28 -05:00
parent 5529249e29
commit 77e1a1340d
3071 changed files with 157540 additions and 4 deletions

View File

@ -0,0 +1,51 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = { match = string.match }
-- }}}
-- Batacpi: provides state, charge, and remaining time for all batteries using acpitool
module("vicious.contrib.batacpi")
-- {{{ Battery widget type
local function worker(format)
local battery_info = {}
local battery_state = {
["full"] = "",
["unknown"] = "",
["charged"] = "",
["charging"] = "+",
["discharging"] = "-"
}
-- Get data from acpitool
local f = io.popen("acpitool -b")
for line in f:lines() do
-- Check if the battery is present
if string.match(line, "^[%s]+Battery.*") then
-- Store state and charge information
table.insert(battery_info, (battery_state[string.match(line, "([%a]*),") or "unknown"]))
table.insert(battery_info, (tonumber(string.match(line, "([%d]?[%d]?[%d])%.")) or 0))
-- Store remaining time information
table.insert(battery_info, (string.match(line, "%%,%s(.*)") or "N/A"))
else
return {battery_state["unknown"], 0, "N/A"}
end
end
f:close()
return battery_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,78 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local math = {
min = math.min,
floor = math.floor
}
local string = {
find = string.find,
match = string.match,
format = string.format
}
-- }}}
-- Batpmu: provides state, charge and remaining time for a requested battery using PMU
module("vicious.contrib.batpmu")
-- {{{ Battery widget type
local function worker(format, batid)
local battery_state = {
["full"] = "",
["unknown"] = "",
["00000013"] = "+",
["00000011"] = "-"
}
-- Get /proc/pmu/battery* state
local f = io.open("/proc/pmu/" .. batid)
-- Handler for incompetent users
if not f then return {battery_state["unknown"], 0, "N/A"} end
local statefile = f:read("*all")
f:close()
-- Get /proc/pmu/info data
local f = io.open("/proc/pmu/info")
local infofile = f:read("*all")
f:close()
-- Check if the battery is present
if infofile == nil or string.find(infofile, "Battery count[%s]+:[%s]0") then
return {battery_state["unknown"], 0, "N/A"}
end
-- Get capacity and charge information
local capacity = string.match(statefile, "max_charge[%s]+:[%s]([%d]+).*")
local remaining = string.match(statefile, "charge[%s]+:[%s]([%d]+).*")
-- Calculate percentage
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Get timer information
local timer = string.match(statefile, "time rem%.[%s]+:[%s]([%d]+).*")
if timer == "0" then return {battery_state["full"], percent, "N/A"} end
-- Get state information
local state = string.match(statefile, "flags[%s]+:[%s]([%d]+).*")
local state = battery_state[state] or battery_state["unknown"]
-- Calculate remaining (charging or discharging) time
local hoursleft = math.floor(tonumber(timer) / 3600)
local minutesleft = math.floor((tonumber(timer) / 60) % 60)
local time = string.format("%02d:%02d", hoursleft, minutesleft)
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local math = {
min = math.min,
floor = math.floor
}
local string = {
find = string.find,
match = string.match,
format = string.format
}
-- }}}
-- Batproc: provides state, charge, and remaining time for a requested battery using procfs
module("vicious.contrib.batproc")
-- {{{ Battery widget type
local function worker(format, batid)
local battery_state = {
["full"] = "",
["unknown"] = "",
["charged"] = "",
["charging"] = "+",
["discharging"] = "-"
}
-- Get /proc/acpi/battery info
local f = io.open("/proc/acpi/battery/"..batid.."/info")
-- Handler for incompetent users
if not f then return {battery_state["unknown"], 0, "N/A"} end
local infofile = f:read("*all")
f:close()
-- Check if the battery is present
if infofile == nil or string.find(infofile, "present:[%s]+no") then
return {battery_state["unknown"], 0, "N/A"}
end
-- Get capacity information
local capacity = string.match(infofile, "last full capacity:[%s]+([%d]+).*")
-- Get /proc/acpi/battery state
local f = io.open("/proc/acpi/battery/"..batid.."/state")
local statefile = f:read("*all")
f:close()
-- Get state information
local state = string.match(statefile, "charging state:[%s]+([%a]+).*")
local state = battery_state[state] or battery_state["unknown"]
-- Get charge information
local rate = string.match(statefile, "present rate:[%s]+([%d]+).*")
local remaining = string.match(statefile, "remaining capacity:[%s]+([%d]+).*")
-- Calculate percentage (but work around broken BAT/ACPI implementations)
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Calculate remaining (charging or discharging) time
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "-" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, "N/A"}
end
local hoursleft = math.floor(timeleft)
local minutesleft = math.floor((timeleft - hoursleft) * 60 )
local time = string.format("%02d:%02d", hoursleft, minutesleft)
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local ipairs = ipairs
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Disk I/O: provides I/O statistics for requested storage devices
module("vicious.contrib.dio")
-- Initialize function tables
local disk_usage = {}
local disk_total = {}
-- Variable definitions
local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 }
-- {{{ Disk I/O widget type
local function worker(format, disk)
if not disk then return end
local disk_lines = { [disk] = {} }
local disk_stats = helpers.pathtotable("/sys/block/" .. disk)
if disk_stats.stat then
local match = string.gmatch(disk_stats.stat, "[%s]+([%d]+)")
for i = 1, 11 do -- Store disk stats
table.insert(disk_lines[disk], match())
end
end
-- Ensure tables are initialized correctly
local diff_total = { [disk] = {} }
if not disk_total[disk] then
disk_usage[disk] = {}
disk_total[disk] = {}
while #disk_total[disk] < #disk_lines[disk] do
table.insert(disk_total[disk], 0)
end
end
for i, v in ipairs(disk_lines[disk]) do
-- Diskstats are absolute, substract our last reading
diff_total[disk][i] = v - disk_total[disk][i]
-- Store totals
disk_total[disk][i] = v
end
-- Calculate and store I/O
helpers.uformat(disk_usage[disk], "read", diff_total[disk][3], unit)
helpers.uformat(disk_usage[disk], "write", diff_total[disk][7], unit)
helpers.uformat(disk_usage[disk], "total", diff_total[disk][7] + diff_total[disk][3], unit)
-- Store I/O scheduler
if disk_stats.queue and disk_stats.queue.scheduler then
disk_usage[disk]["{sched}"] = string.gmatch(disk_stats.queue.scheduler, "%[([%a]+)%]")
end
return disk_usage[disk]
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,17 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Setup environment
local setmetatable = setmetatable
local wrequire = require("vicious.helpers").wrequire
-- Vicious: widgets for the awesome window manager
module("vicious.contrib")
-- }}}
-- Load modules at runtime as needed
setmetatable(_M, { __index = wrequire })

View File

@ -0,0 +1,47 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { find = string.find }
local helpers = require("vicious.helpers")
-- }}}
-- Mpc: provides the currently playing song in MPD
module("vicious.contrib.mpc")
-- {{{ MPC widget type
local function worker(format, warg)
-- Get data from mpd
local f = io.popen("mpc")
local np = f:read("*line")
f:close()
-- Not installed,
if np == nil or -- off or stoppped.
(string.find(np, "MPD_HOST") or string.find(np, "volume:"))
then
return {"Stopped"}
end
-- Check if we should scroll, or maybe truncate
if warg then
if type(warg) == "table" then
np = helpers.scroll(np, warg[1], warg[2])
else
np = helpers.truncate(np, warg)
end
end
return {helpers.escape(np)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,138 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local tonumber = tonumber
local os = { time = os.time }
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Net: provides usage statistics for all network interfaces
module("vicious.contrib.net")
-- Initialise function tables
local nets = {}
-- Variable definitions
local unit = { ["b"] = 1, ["kb"] = 1024,
["mb"] = 1024^2, ["gb"] = 1024^3
}
-- {{{ Net widget type
local function worker(format, tignorelist)
local args = {}
local tignore = {}
local total_rx = 0
local total_tx = 0
local any_up = 0
if not tignorelist then
tignorelist = {"lo", "wmaster0"}
end
for k, i in pairs(tignorelist) do
tignore[i] = true
end
-- Get NET stats
for line in io.lines("/proc/net/dev") do
-- Match wmaster0 as well as rt0 (multiple leading spaces)
local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):")
if name ~= nil then
-- Received bytes, first value after the name
local recv = tonumber(string.match(line, ":[%s]*([%d]+)"))
-- Transmited bytes, 7 fields from end of the line
local send = tonumber(string.match(line,
"([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$"))
if not tignore[name] then
total_rx = total_rx + recv
total_tx = total_tx + send
end
helpers.uformat(args, name .. " rx", recv, unit)
helpers.uformat(args, name .. " tx", send, unit)
if nets[name] == nil then
-- Default values on the first run
nets[name] = {}
helpers.uformat(args, name .. " down", 0, unit)
helpers.uformat(args, name .. " up", 0, unit)
args["{"..name.." carrier}"] = 0
nets[name].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets[name].time > 0 and
os.time() - nets[name].time or 1
nets[name].time = os.time()
local down = (recv - nets[name][1]) / interval
local up = (send - nets[name][2]) / interval
helpers.uformat(args, name .. " down", down, unit)
helpers.uformat(args, name .. " up", up, unit)
-- Carrier detection
sysnet = helpers.pathtotable("/sys/class/net/" .. name)
if sysnet.carrier then
ccarrier = tonumber(sysnet.carrier)
args["{"..name.." carrier}"] = ccarrier
if ccarrier ~= 0 and not tignore[name] then
any_up = 1
end
else
args["{"..name.." carrier}"] = 0
end
end
-- Store totals
nets[name][1] = recv
nets[name][2] = send
end
end
helpers.uformat(args, "total rx", total_rx, unit)
helpers.uformat(args, "total tx", total_tx, unit)
if nets["total"] == nil then
-- Default values on the first run
nets["total"] = {}
helpers.uformat(args, "total down", 0, unit)
helpers.uformat(args, "total up", 0, unit)
args["{total carrier}"] = 0
nets["total"].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets["total"].time > 0 and
os.time() - nets["total"].time or 1
nets["total"].time = os.time()
local down = (total_rx - nets["total"][1]) / interval
local up = (total_tx - nets["total"][2]) / interval
helpers.uformat(args, "total down", down, unit)
helpers.uformat(args, "total up", up, unit)
args["{total carrier}"] = any_up
end
-- Store totals
nets["total"][1] = total_rx
nets["total"][2] = total_tx
return args
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,34 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Radu A. <admiral0@tuxfamily.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
-- }}}
-- Netcfg: provides active netcfg network profiles
module("vicious.contrib.netcfg")
-- {{{ Netcfg widget type
local function worker(format)
-- Initialize counters
local profiles = {}
local f = io.popen("ls -1 /var/run/network/profiles")
for line in f:lines() do
if line ~= nil then
table.insert(profiles, line)
end
end
f:close()
return profiles
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,53 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
-- }}}
-- Ossvol: provides volume levels of requested OSS mixers
module("vicious.contrib.ossvol")
-- {{{ Volume widget type
local function worker(format, warg)
if not warg then return end
local mixer_state = {
["on"] = "", -- "",
["off"] = "" -- "M"
}
-- Get mixer control contents
local f = io.popen("ossmix -c")
local mixer = f:read("*all")
f:close()
-- Capture mixer control state
local volu = tonumber(string.match(mixer, warg .. "[%s]([%d%.]+)"))/0.25
local mute = string.match(mixer, "vol%.mute[%s]([%a]+)")
-- Handle mixers without data
if volu == nil then
return {0, mixer_state["off"]}
end
-- Handle mixers without mute
if mute == "OFF" and volu == "0"
-- Handle mixers that are muted
or mute == "ON" then
mute = mixer_state["off"]
else
mute = mixer_state["on"]
end
return {volu, mute}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,54 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Boris Bolgradov <>
--
-- This widget type depends on luasocket.
--
-- Widget arguments are host, port, username and
-- password, i.e.:
-- {"mail.myhost.com", 110, "John", "132435"}
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local sock_avail, socket = pcall(function()
return require("socket")
end)
-- }}}
-- POP: provides the count of new messages in a POP3 mailbox
module("vicious.contrib.pop")
-- {{{ POP3 count widget type
local function worker(format, warg)
if not sock_avail or (not warg or #warg ~= 4) then
return {"N/A"}
end
local host, port = warg[1], tonumber(warg[2])
local user, pass = warg[3], warg[4]
local client = socket.tcp()
client:settimeout(3)
client:connect(host, port)
client:receive("*l")
client:send("USER " .. user .. "\r\n")
client:receive("*l")
client:send("PASS " .. pass .. "\r\n")
client:receive("*l")
client:send("STAT" .. "\r\n")
local response = client:receive("*l")
client:close()
if response:find("%+OK") then
response = response:match("%+OK (%d+)")
end
return {response}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,111 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, MrMagne <mr.magne@yahoo.fr>
-- * (c) 2010, Mic92 <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local os = { execute = os.execute }
local table = { insert = table.insert }
local string = {
find = string.find,
match = string.match,
format = string.format,
gmatch = string.gmatch
}
-- }}}
-- Pulse: provides volume levels of requested pulseaudio sinks and methods to change them
module("vicious.contrib.pulse")
-- {{{ Helper function
local function pacmd(args)
local f = io.popen("pacmd "..args)
local line = f:read("*all")
f:close()
return line
end
local function escape(text)
local special_chars = { ["."] = "%.", ["-"] = "%-" }
return text:gsub("[%.%-]", special_chars)
end
local cached_sinks = {}
local function get_sink_name(sink)
if type(sink) == "string" then return sink end
-- avoid nil keys
local key = sink or 1
-- Cache requests
if not cached_sinks[key] then
local line = pacmd("list-sinks")
for s in string.gmatch(line, "name: <(.-)>") do
table.insert(cached_sinks, s)
end
end
return cached_sinks[key]
end
-- }}}
-- {{{ Pulseaudio widget type
local function worker(format, sink)
sink = get_sink_name(sink)
if sink == nil then return {0, "unknown"} end
-- Get sink data
local data = pacmd("dump")
-- If mute return 0 (not "Mute") so we don't break progressbars
if string.find(data,"set%-sink%-mute "..escape(sink).." yes") then
return {0, "off"}
end
local vol = tonumber(string.match(data, "set%-sink%-volume "..escape(sink).." (0x[%x]+)"))
if vol == nil then vol = 0 end
return { vol/0x10000*100, "on"}
end
-- }}}
-- {{{ Volume control helper
function add(percent, sink)
sink = get_sink_name(sink)
if sink == nil then return end
local data = pacmd("dump")
local pattern = "set%-sink%-volume "..escape(sink).." (0x[%x]+)"
local initial_vol = tonumber(string.match(data, pattern))
local vol = initial_vol + percent/100*0x10000
if vol > 0x10000 then vol = 0x10000 end
if vol < 0 then vol = 0 end
local cmd = string.format("pacmd set-sink-volume %s 0x%x >/dev/null", sink, vol)
return os.execute(cmd)
end
function toggle(sink)
sink = get_sink_name(sink)
if sink == nil then return end
local data = pacmd("dump")
local pattern = "set%-sink%-mute "..escape(sink).." (%a%a%a?)"
local mute = string.match(data, pattern)
-- 0 to enable a sink or 1 to mute it.
local state = { yes = 0, no = 1}
local cmd = string.format("pacmd set-sink-mute %s %d", sink, state[mute])
return os.execute(cmd)
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,67 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2009, olcc
--
-- This is now a standalone RSS reader for awesome:
-- * http://github.com/olcc/aware
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local io = { popen = io.popen }
local setmetatable = setmetatable
-- }}}
-- RSS: provides latest world news
module("vicious.contrib.rss")
-- {{{ RSS widget type
local function worker(format, input)
-- input: * feed - feed url
-- * object - entity to look for (typically: 'item')
-- * fields - fields to read (example: 'link', 'title', 'description')
-- output: * count - number of entities found
-- * one table for each field, containing wanted values
local feed = input.feed
local object = input.object
local fields = input.fields
-- Initialise tables
local out = {}
for _, v in pairs(fields) do
out[v] = {}
end
-- Initialise variables
local ob = nil
local i,j,k = 1, 1, 0
local curl = "curl -A 'Mozilla/4.0' -fsm 5 --connect-timeout 3 "
-- Get the feed
local f = io.popen(curl .. '"' .. feed .. '"')
local feed = f:read("*all")
f:close()
while true do
i, j, ob = feed.find(feed, "<" .. object .. ">(.-)</" .. object .. ">", i)
if not ob then break end
for _, v in pairs(fields) do
out[v][k] = ob:match("<" .. v .. ">(.*)</" .. v .. ">")
end
k = k+1
i = j+1
end
-- Update the entity count
out.count = k
return out
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,68 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Greg D. <jabbas@jabbas.pl>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = {
gsub = string.gsub,
match = string.match
}
-- }}}
-- Sensors: provides access to lm_sensors data
module("vicious.contrib.sensors")
-- {{{ Split helper function
local function datasplit(str)
-- Splitting strings into associative array
-- with some magic to get the values right.
str = string.gsub(str, "\n", ":")
local tbl = {}
string.gsub(str, "([^:]*)", function (v)
if string.match(v, ".") then
table.insert(tbl, v)
end
end)
local assoc = {}
for c = 1, #tbl, 2 do
local k = string.gsub(tbl[c], ".*_", "")
local v = tonumber(string.match(tbl[c+1], "[%d]+"))
assoc[k] = v
end
return assoc
end
-- }}}
-- {{{ Sensors widget type
local function worker(format, warg)
-- Get data from all sensors
local f = io.popen("LANG=C sensors -uA")
local lm_sensors = f:read("*all")
f:close()
local sensor_data = string.gsub(
string.match(lm_sensors, warg..":\n(%s%s.-)\n[^ ]"), " ", "")
-- One of: crit, max
local divisor = "crit"
local s_data = datasplit(sensor_data)
if s_data[divisor] and s_data[divisor] > 0 then
s_data.percent = s_data.input / s_data[divisor] * 100
end
return {s_data.input, tonumber(s_data.percent)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,149 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Rémy C. <shikamaru@mandriva.org>
-- * (c) 2009, Benedikt Sauer <filmor@gmail.com>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local rawget = rawget
local require = require
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local getmetatable = getmetatable
local string = {
upper = string.upper,
format = string.format
}
-- }}}
-- Helpers: provides helper functions for vicious widgets
module("vicious.helpers")
-- {{{ Variable definitions
local scroller = {}
-- }}}
-- {{{ Helper functions
-- {{{ Loader of vicious modules
function wrequire(table, key)
local module = rawget(table, key)
return module or require(table._NAME .. "." .. key)
end
-- }}}
-- {{{ Expose path as a Lua table
function pathtotable(dir)
return setmetatable({ _path = dir },
{ __index = function(table, index)
local path = table._path .. '/' .. index
local f = io.open(path)
if f then
local s = f:read("*all")
f:close()
if s then
return s
else
local o = { _path = path }
setmetatable(o, getmetatable(table))
return o
end
end
end
})
end
-- }}}
-- {{{ Format a string with args
function format(format, args)
for var, val in pairs(args) do
format = format:gsub("$" .. (tonumber(var) and var or
var:gsub("[-+?*]", function(i) return "%"..i end)),
val)
end
return format
end
-- }}}
-- {{{ Format units to one decimal point
function uformat(array, key, value, unit)
for u, v in pairs(unit) do
array["{"..key.."_"..u.."}"] = string.format("%.1f", value/v)
end
return array
end
-- }}}
-- {{{ Escape a string
function escape(text)
local xml_entities = {
["\""] = "&quot;",
["&"] = "&amp;",
["'"] = "&apos;",
["<"] = "&lt;",
[">"] = "&gt;"
}
return text and text:gsub("[\"&'<>]", xml_entities)
end
-- }}}
-- {{{ Capitalize a string
function capitalize(text)
return text and text:gsub("([%w])([%w]*)", function(c, s)
return string.upper(c) .. s
end)
end
-- }}}
-- {{{ Truncate a string
function truncate(text, maxlen)
local txtlen = text:len()
if txtlen > maxlen then
text = text:sub(1, maxlen - 3) .. "..."
end
return text
end
-- }}}
-- {{{ Scroll through a string
function scroll(text, maxlen, widget)
if not scroller[widget] then
scroller[widget] = { i = 1, d = true }
end
local txtlen = text:len()
local state = scroller[widget]
if txtlen > maxlen then
if state.d then
text = text:sub(state.i, state.i + maxlen) .. "..."
state.i = state.i + 3
if maxlen + state.i >= txtlen then
state.d = false
end
else
text = "..." .. text:sub(state.i, state.i + maxlen)
state.i = state.i - 3
if state.i <= 1 then
state.d = true
end
end
end
return text
end
-- }}}
-- }}}

View File

@ -0,0 +1,249 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Setup environment
local type = type
local pairs = pairs
local tonumber = tonumber
local capi = { timer = timer }
local os = { time = os.time }
local table = {
insert = table.insert,
remove = table.remove
}
require("vicious.helpers")
require("vicious.widgets")
--require("vicious.contrib")
-- Vicious: widgets for the awesome window manager
module("vicious")
-- Initialize tables
local timers = {}
local registered = {}
local widget_cache = {}
-- }}}
-- {{{ Local functions
-- {{{ Update a widget
local function update(widget, reg, disablecache)
-- Check if there are any equal widgets
if reg == nil then
for w, i in pairs(registered) do
if w == widget then
for _, r in pairs(i) do
update(w, r, disablecache)
end
end
end
return
end
local t = os.time()
local data = {}
-- Check for chached output newer than the last update
if widget_cache[reg.wtype] ~= nil then
local c = widget_cache[reg.wtype]
if (c.time == nil or c.time <= t-reg.timer) or disablecache then
c.time, c.data = t, reg.wtype(reg.format, reg.warg)
end
data = c.data
else
data = reg.wtype and reg.wtype(reg.format, reg.warg)
end
if type(data) == "table" then
if type(reg.format) == "string" then
data = helpers.format(reg.format, data)
elseif type(reg.format) == "function" then
data = reg.format(widget, data)
end
end
if widget.add_value ~= nil then
widget:add_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_value ~= nil then
widget:set_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_markup ~= nil then
widget:set_markup(data)
else
widget.text = data
end
return data
end
-- }}}
-- {{{ Register from reg object
local function regregister(reg)
if not reg.running then
if registered[reg.widget] == nil then
registered[reg.widget] = {}
table.insert(registered[reg.widget], reg)
else
local already = false
for w, i in pairs(registered) do
if w == reg.widget then
for _, v in pairs(i) do
if v == reg then
already = true
break
end
end
if already then
break
end
end
end
if not already then
table.insert(registered[reg.widget], reg)
end
end
-- Start the timer
if reg.timer > 0 then
timers[reg.update] = {
timer = capi.timer({ timeout = reg.timer })
}
local tm = timers[reg.update].timer
if tm.connect_signal then
tm:connect_signal("timeout", reg.update)
else
tm:add_signal("timeout", reg.update)
end
tm:start()
-- Initial update
tm:emit_signal("timeout")
end
reg.running = true
end
end
-- }}}
-- }}}
-- {{{ Global functions
-- {{{ Register a widget
function register(widget, wtype, format, timer, warg)
local reg = {}
local widget = widget
-- Set properties
reg.wtype = wtype
reg.format = format
reg.timer = timer
reg.warg = warg
reg.widget = widget
-- Update function
reg.update = function ()
update(widget, reg)
end
-- Default to 2s timer
if reg.timer == nil then
reg.timer = 2
end
-- Register a reg object
regregister(reg)
-- Return a reg object for reuse
return reg
end
-- }}}
-- {{{ Unregister a widget
function unregister(widget, keep, reg)
if reg == nil then
for w, i in pairs(registered) do
if w == widget then
for _, v in pairs(i) do
reg = unregister(w, keep, v)
end
end
end
return reg
end
if not keep then
for w, i in pairs(registered) do
if w == widget then
for k, v in pairs(i) do
if v == reg then
table.remove(registered[w], k)
end
end
end
end
end
-- Stop the timer
if timers[reg.update].timer.started then
timers[reg.update].timer:stop()
end
reg.running = false
return reg
end
-- }}}
-- {{{ Enable caching of a widget type
function cache(wtype)
if wtype ~= nil then
if widget_cache[wtype] == nil then
widget_cache[wtype] = {}
end
end
end
-- }}}
-- {{{ Force update of widgets
function force(wtable)
if type(wtable) == "table" then
for _, w in pairs(wtable) do
update(w, nil, true)
end
end
end
-- }}}
-- {{{ Suspend all widgets
function suspend()
for w, i in pairs(registered) do
for _, v in pairs(i) do
unregister(w, true, v)
end
end
end
-- }}}
-- {{{ Activate a widget
function activate(widget)
for w, i in pairs(registered) do
if widget == nil or w == widget then
for _, v in pairs(i) do
regregister(v)
end
end
end
end
-- }}}
-- }}}

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local string = { format = string.format }
local helpers = require("vicious.helpers")
local math = {
min = math.min,
floor = math.floor
}
-- }}}
-- Bat: provides state, charge, and remaining time for a requested battery
module("vicious.widgets.bat")
-- {{{ Battery widget type
local function worker(format, warg)
if not warg then return end
local battery = helpers.pathtotable("/sys/class/power_supply/"..warg)
local battery_state = {
["Full\n"] = "",
["Unknown\n"] = "",
["Charged\n"] = "",
["Charging\n"] = "+",
["Discharging\n"] = "-"
}
-- Check if the battery is present
if battery.present ~= "1\n" then
return {battery_state["Unknown\n"], 0, "N/A"}
end
-- Get state information
local state = battery_state[battery.status] or battery_state["Unknown\n"]
-- Get capacity information
if battery.charge_now then
remaining, capacity = battery.charge_now, battery.charge_full
elseif battery.energy_now then
remaining, capacity = battery.energy_now, battery.energy_full
else
return {battery_state["Unknown\n"], 0, "N/A"}
end
-- Calculate percentage (but work around broken BAT/ACPI implementations)
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Get charge information
if battery.current_now then
rate = battery.current_now
elseif battery.power_now then
rate = battery.power_now
else
return {state, percent, "N/A"}
end
-- Calculate remaining (charging or discharging) time
local time = "N/A"
if rate ~= nil then
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "-" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, time}
end
local hoursleft = math.floor(timeleft)
local minutesleft = math.floor((timeleft - hoursleft) * 60 )
time = string.format("%02d:%02d", hoursleft, minutesleft)
end
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,75 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
-- * (c) 2011, Jörg Thalheim <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local ipairs = ipairs
local io = { lines = io.lines }
local setmetatable = setmetatable
local math = { floor = math.floor }
local table = { insert = table.insert }
local string = {
sub = string.sub,
gmatch = string.gmatch
}
-- }}}
-- Cpu: provides CPU usage for all available CPUs/cores
module("vicious.widgets.cpu")
-- Initialize function tables
local cpu_usage = {}
local cpu_total = {}
local cpu_active = {}
-- {{{ CPU widget type
local function worker(format)
local cpu_lines = {}
-- Get CPU stats
for line in io.lines("/proc/stat") do
if string.sub(line, 1, 3) ~= "cpu" then break end
cpu_lines[#cpu_lines+1] = {}
for i in string.gmatch(line, "[%s]+([^%s]+)") do
table.insert(cpu_lines[#cpu_lines], i)
end
end
-- Ensure tables are initialized correctly
for i = #cpu_total + 1, #cpu_lines do
cpu_total[i] = 0
cpu_usage[i] = 0
cpu_active[i] = 0
end
for i, v in ipairs(cpu_lines) do
-- Calculate totals
local total_new = 0
for j = 1, #v do
total_new = total_new + v[j]
end
local active_new = total_new - (v[4] + v[5])
-- Calculate percentage
local diff_total = total_new - cpu_total[i]
local diff_active = active_new - cpu_active[i]
cpu_usage[i] = math.floor((diff_active / diff_total) * 100)
-- Store totals
cpu_total[i] = total_new
cpu_active[i] = active_new
end
return cpu_usage
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,56 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Cpufreq: provides freq, voltage and governor info for a requested CPU
module("vicious.widgets.cpufreq")
-- {{{ CPU frequency widget type
local function worker(format, warg)
if not warg then return end
local cpufreq = helpers.pathtotable("/sys/devices/system/cpu/"..warg.."/cpufreq")
local governor_state = {
["ondemand\n"] = "",
["powersave\n"] = "",
["userspace\n"] = "¤",
["performance\n"] = "",
["conservative\n"] = ""
}
-- Default voltage values
local voltage = { v = "N/A", mv = "N/A" }
-- Get the current frequency
local freq = tonumber(cpufreq.scaling_cur_freq)
-- Calculate MHz and GHz
local freqmhz = freq / 1000
local freqghz = freqmhz / 1000
-- Get the current voltage
if cpufreq.scaling_voltages then
voltage.mv = tonumber(string.match(cpufreq.scaling_voltages, freq.."[%s]([%d]+)"))
-- Calculate voltage from mV
voltage.v = voltage.mv / 1000
end
-- Get the current governor
local governor = cpufreq.scaling_governor
-- Represent the governor as a symbol
governor = governor_state[governor] or governor
return {freqmhz, freqghz, voltage.mv, voltage.v, governor}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,43 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
-- }}}
-- Cpuinf: provides speed and cache information for all available CPUs/cores
module("vicious.widgets.cpuinf")
-- {{{ CPU Information widget type
local function worker(format)
local id = nil
local cpu_info = {} -- Get CPU info
for line in io.lines("/proc/cpuinfo") do
for k, v in string.gmatch(line, "([%a%s]+)[%s]+:[%s]([%d]+).-$") do
if k == "processor" then
id = v
elseif k == "cpu MHz\t" or k == "cpu MHz" then
local speed = tonumber(v)
cpu_info["{cpu"..id.." mhz}"] = speed
cpu_info["{cpu"..id.." ghz}"] = speed / 1000
elseif k == "cache size" then
local cache = tonumber(v)
cpu_info["{cpu"..id.." kb}"] = cache
cpu_info["{cpu"..id.." mb}"] = cache / 1024
end
end
end
return cpu_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,26 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local os = {
date = os.date,
time = os.time
}
-- }}}
-- Date: provides access to os.date with optional time formatting
module("vicious.widgets.date")
-- {{{ Date widget type
local function worker(format, warg)
return os.date(format or nil, warg and os.time()+warg or nil)
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Jörg T. <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
local os = {
time = os.time,
difftime = os.difftime
}
-- }}}
-- Disk I/O: provides I/O statistics for requested storage devices
module("vicious.widgets.dio")
-- Initialize function tables
local disk_usage = {}
local disk_stats = {}
local disk_time = 0
-- Constant definitions
local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 }
-- {{{ Disk I/O widget type
local function worker(format)
local disk_lines = {}
for line in io.lines("/proc/diskstats") do
local device, read, write =
-- Linux kernel documentation: Documentation/iostats.txt
string.match(line, "([^%s]+) %d+ %d+ (%d+) %d+ %d+ %d+ (%d+)")
disk_lines[device] = { read, write }
end
local time = os.time()
local interval = os.difftime(time, disk_time)
if interval == 0 then interval = 1 end
for device, stats in pairs(disk_lines) do
-- Avoid insane values on startup
local last_stats = disk_stats[device] or stats
-- Check for overflows and counter resets (> 2^32)
if stats[1] < last_stats[1] or stats[2] < last_stats[2] then
last_stats[1], last_stats[2] = stats[1], stats[2]
end
-- Diskstats are absolute, substract our last reading
-- * divide by timediff because we don't know the timer value
local read = (stats[1] - last_stats[1]) / interval
local write = (stats[2] - last_stats[2]) / interval
-- Calculate and store I/O
helpers.uformat(disk_usage, device.." read", read, unit)
helpers.uformat(disk_usage, device.." write", write, unit)
helpers.uformat(disk_usage, device.." total", read + write, unit)
end
disk_time = time
disk_stats = disk_lines
return disk_usage
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,51 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- FS: provides file system disk space usage
module("vicious.widgets.fs")
-- Variable definitions
local unit = { ["mb"] = 1024, ["gb"] = 1024^2 }
-- {{{ Filesystem widget type
local function worker(format, warg)
-- Fallback to listing local filesystems
if warg then warg = "" else warg = "-l" end
local fs_info = {} -- Get data from df
local f = io.popen("LC_ALL=C df -kP " .. warg)
for line in f:lines() do -- Match: (size) (used)(avail)(use%) (mount)
local s = string.match(line, "^.-[%s]([%d]+)")
local u,a,p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%")
local m = string.match(line, "%%[%s]([%p%w]+)")
if u and m then -- Handle 1st line and broken regexp
helpers.uformat(fs_info, m .. " size", s, unit)
helpers.uformat(fs_info, m .. " used", u, unit)
helpers.uformat(fs_info, m .. " avail", a, unit)
fs_info["{" .. m .. " used_p}"] = tonumber(p)
fs_info["{" .. m .. " avail_p}"] = 100 - tonumber(p)
end
end
f:close()
return fs_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,82 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
find = string.find,
match = string.match
}
-- }}}
-- Gmail: provides count of new and subject of last e-mail on Gmail
module("vicious.widgets.gmail")
-- {{{ Variable definitions
local rss = {
inbox = {
"https://mail.google.com/mail/feed/atom",
"Gmail %- Inbox"
},
unread = {
"https://mail.google.com/mail/feed/atom/unread",
"Gmail %- Label"
},
--labelname = {
-- "https://mail.google.com/mail/feed/atom/labelname",
-- "Gmail %- Label"
--},
}
-- Default is just Inbox
local feed = rss.inbox
local mail = {
["{count}"] = 0,
["{subject}"] = "N/A"
}
-- }}}
-- {{{ Gmail widget type
local function worker(format, warg)
-- Get info from the Gmail atom feed
local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed[1])
-- Could be huge don't read it all at once, info we are after is at the top
for line in f:lines() do
mail["{count}"] = -- Count comes before messages and matches at least 0
tonumber(string.match(line, "<fullcount>([%d]+)</fullcount>")) or mail["{count}"]
-- Find subject tags
local title = string.match(line, "<title>(.*)</title>")
-- If the subject changed then break out of the loop
if title ~= nil and not string.find(title, feed[2]) then
-- Check if we should scroll, or maybe truncate
if warg then
if type(warg) == "table" then
title = helpers.scroll(title, warg[1], warg[2])
else
title = helpers.truncate(title, warg)
end
end
-- Spam sanitize the subject and store
mail["{subject}"] = helpers.escape(title)
break
end
end
f:close()
return mail
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,37 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
-- }}}
-- Hddtemp: provides hard drive temperatures using the hddtemp daemon
module("vicious.widgets.hddtemp")
-- {{{ HDD Temperature widget type
local function worker(format, warg)
-- Fallback to default hddtemp port
if warg == nil then warg = 7634 end
local hdd_temp = {} -- Get info from the hddtemp daemon
local f = io.popen("curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..warg)
for line in f:lines() do
for d, t in string.gmatch(line, "|([%/%a%d]+)|.-|([%d]+)|[CF]+|") do
hdd_temp["{"..d.."}"] = tonumber(t)
end
end
f:close()
return hdd_temp
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,17 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Setup environment
local setmetatable = setmetatable
local wrequire = require("vicious.helpers").wrequire
-- Vicious: widgets for the awesome window manager
module("vicious.widgets")
-- }}}
-- Load modules at runtime as needed
setmetatable(_M, { __index = wrequire })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local io = { open = io.open }
local setmetatable = setmetatable
local string = { gfind = string.gfind }
local helpers = require("vicious.helpers")
-- }}}
-- Mbox: provides the subject of last e-mail in a mbox file
module("vicious.widgets.mbox")
-- Initialize variables
local subject = "N/A"
-- {{{ Mailbox widget type
local function worker(format, warg)
if not warg then return end
-- mbox could be huge, get a 30kb chunk from EOF
if type(warg) ~= "table" then mbox = warg end
-- * attachment could be much bigger than 30kb
local f = io.open(mbox or warg[1])
f:seek("end", -30720)
local txt = f:read("*all")
f:close()
-- Find all Subject lines
for i in string.gfind(txt, "Subject: ([^\n]*)") do
subject = i
end
-- Check if we should scroll, or maybe truncate
if type(warg) == "table" then
if warg[3] ~= nil then
subject = helpers.scroll(subject, warg[2], warg[3])
else
subject = helpers.truncate(subject, warg[2])
end
end
return {helpers.escape(subject)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,57 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { open = io.open }
local setmetatable = setmetatable
local string = { find = string.find }
-- }}}
-- Mboxc: provides the count of total, old and new messages in mbox files
module("vicious.widgets.mboxc")
-- {{{ Mbox count widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local count = { old = 0, total = 0, new = 0 }
-- Get data from mbox files
for i=1, #warg do
local f = io.open(warg[i])
while true do
-- Read the mbox line by line, if we are going to read
-- some *HUGE* folders then switch to reading chunks
local lines = f:read("*line")
if not lines then break end
-- Find all messages
-- * http://www.jwz.org/doc/content-length.html
local _, from = string.find(lines, "^From[%s]")
if from ~= nil then count.total = count.total + 1 end
-- Read messages have the Status header
local _, status = string.find(lines, "^Status:[%s]RO$")
if status ~= nil then count.old = count.old + 1 end
-- Skip the folder internal data
local _, int = string.find(lines, "^Subject:[%s].*FOLDER[%s]INTERNAL[%s]DATA")
if int ~= nil then count.total = count.total - 1 end
end
f:close()
end
-- Substract total from old to get the new count
count.new = count.total - count.old
return {count.total, count.old, count.new}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,40 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) Maildir Biff Widget, Fredrik Ax
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
-- }}}
-- Mdir: provides the number of new and unread messages in Maildir structures/dirs
module("vicious.widgets.mdir")
-- {{{ Maildir widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local count = { new = 0, cur = 0 }
for i=1, #warg do
-- Recursively find new messages
local f = io.popen("find "..warg[i].." -type f -wholename '*/new/*'")
for line in f:lines() do count.new = count.new + 1 end
f:close()
-- Recursively find "old" messages lacking the Seen flag
local f = io.popen("find "..warg[i].." -type f -regex '.*/cur/.*2,[^S]*$'")
for line in f:lines() do count.cur = count.cur + 1 end
f:close()
end
return {count.new, count.cur}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,49 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { gmatch = string.gmatch }
-- }}}
-- Mem: provides RAM and Swap usage statistics
module("vicious.widgets.mem")
-- {{{ Memory widget type
local function worker(format)
local mem = { buf = {}, swp = {} }
-- Get MEM info
for line in io.lines("/proc/meminfo") do
for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+") do
if k == "MemTotal" then mem.total = math.floor(v/1024)
elseif k == "MemFree" then mem.buf.f = math.floor(v/1024)
elseif k == "Buffers" then mem.buf.b = math.floor(v/1024)
elseif k == "Cached" then mem.buf.c = math.floor(v/1024)
elseif k == "SwapTotal" then mem.swp.t = math.floor(v/1024)
elseif k == "SwapFree" then mem.swp.f = math.floor(v/1024)
end
end
end
-- Calculate memory percentage
mem.free = mem.buf.f + mem.buf.b + mem.buf.c
mem.inuse = mem.total - mem.free
mem.usep = math.floor(mem.inuse / mem.total * 100)
-- Calculate swap percentage
mem.swp.inuse = mem.swp.t - mem.swp.f
mem.swp.usep = math.floor(mem.swp.inuse / mem.swp.t * 100)
return {mem.usep, mem.inuse, mem.total, mem.free,
mem.swp.usep, mem.swp.inuse, mem.swp.t, mem.swp.f}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,63 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Mpd: provides Music Player Daemon information
module("vicious.widgets.mpd")
-- {{{ MPD widget type
local function worker(format, warg)
local mpd_state = {
["{volume}"] = 0,
["{state}"] = "N/A",
["{Artist}"] = "N/A",
["{Title}"] = "N/A",
["{Album}"] = "N/A",
["{Genre}"] = "N/A",
--["{Name}"] = "N/A",
--["{file}"] = "N/A",
}
-- Fallback to MPD defaults
local pass = warg and (warg.password or warg[1]) or "\"\""
local host = warg and (warg.host or warg[2]) or "127.0.0.1"
local port = warg and (warg.port or warg[3]) or "6600"
-- Construct MPD client options
local mpdh = "telnet://"..host..":"..port
local echo = "echo 'password "..pass.."\nstatus\ncurrentsong\nclose'"
-- Get data from MPD server
local f = io.popen(echo.." | curl --connect-timeout 1 -fsm 3 "..mpdh)
for line in f:lines() do
for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
if k == "volume" then mpd_state["{"..k.."}"] = v and tonumber(v)
elseif k == "state" then mpd_state["{"..k.."}"] = helpers.capitalize(v)
elseif k == "Artist" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Title" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Album" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Genre" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "Name" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "file" then mpd_state["{"..k.."}"] = helpers.escape(v)
end
end
end
f:close()
return mpd_state
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,79 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local os = { time = os.time }
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Net: provides state and usage statistics of all network interfaces
module("vicious.widgets.net")
-- Initialize function tables
local nets = {}
-- Variable definitions
local unit = { ["b"] = 1, ["kb"] = 1024,
["mb"] = 1024^2, ["gb"] = 1024^3
}
-- {{{ Net widget type
local function worker(format)
local args = {}
-- Get NET stats
for line in io.lines("/proc/net/dev") do
-- Match wmaster0 as well as rt0 (multiple leading spaces)
local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):")
if name ~= nil then
-- Received bytes, first value after the name
local recv = tonumber(string.match(line, ":[%s]*([%d]+)"))
-- Transmited bytes, 7 fields from end of the line
local send = tonumber(string.match(line,
"([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$"))
helpers.uformat(args, name .. " rx", recv, unit)
helpers.uformat(args, name .. " tx", send, unit)
-- Operational state and carrier detection
local sysnet = helpers.pathtotable("/sys/class/net/" .. name)
args["{"..name.." carrier}"] = tonumber(sysnet.carrier) or 0
if nets[name] == nil then
-- Default values on the first run
nets[name] = {}
helpers.uformat(args, name .. " down", 0, unit)
helpers.uformat(args, name .. " up", 0, unit)
nets[name].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets[name].time > 0 and
os.time() - nets[name].time or 1
nets[name].time = os.time()
local down = (recv - nets[name][1]) / interval
local up = (send - nets[name][2]) / interval
helpers.uformat(args, name .. " down", down, unit)
helpers.uformat(args, name .. " up", up, unit)
end
-- Store totals
nets[name][1] = recv
nets[name][2] = send
end
end
return args
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,61 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) org-awesome, Damien Leone
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { find = string.find }
local os = {
time = os.time,
date = os.date
}
-- }}}
-- Org: provides agenda statistics for Emacs org-mode
module("vicious.widgets.org")
-- {{{ OrgMode widget type
local function worker(format, warg)
if not warg then return end
-- Compute delays
local today = os.time{ year=os.date("%Y"), month=os.date("%m"), day=os.date("%d") }
local soon = today + 24 * 3600 * 3 -- 3 days ahead is close
local future = today + 24 * 3600 * 7 -- 7 days ahead is maximum
-- Initialize counters
local count = { past = 0, today = 0, soon = 0, future = 0 }
-- Get data from agenda files
for i=1, #warg do
for line in io.lines(warg[i]) do
local scheduled = string.find(line, "SCHEDULED:")
local closed = string.find(line, "CLOSED:")
local deadline = string.find(line, "DEADLINE:")
if (scheduled and not closed) or (deadline and not closed) then
local b, e, y, m, d = string.find(line, "(%d%d%d%d)-(%d%d)-(%d%d)")
if b then
local t = os.time{ year = y, month = m, day = d }
if t < today then count.past = count.past + 1
elseif t == today then count.today = count.today + 1
elseif t <= soon then count.soon = count.soon + 1
elseif t <= future then count.future = count.future + 1
end
end
end
end
end
return {count.past, count.today, count.soon, count.future}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local tonumber = tonumber
local io = { popen = io.popen }
local os = { getenv = os.getenv }
local math = { ceil = math.ceil }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
gsub = string.gsub,
match = string.match
}
-- }}}
-- OS: provides operating system information
module("vicious.widgets.os")
-- {{{ Operating system widget type
local function worker(format)
local system = {
["ostype"] = "N/A",
["hostname"] = "N/A",
["osrelease"] = "N/A",
["username"] = "N/A",
["entropy"] = "N/A",
["entropy_p"] = "N/A"
}
-- Linux manual page: uname(2)
local kernel = helpers.pathtotable("/proc/sys/kernel")
for k, v in pairs(system) do
if kernel[k] then
system[k] = string.gsub(kernel[k], "[%s]*$", "")
end
end
-- BSD manual page: uname(1)
if system["ostype"] == "N/A" then
local f = io.popen("uname -snr")
local uname = f:read("*line")
f:close()
system["ostype"], system["hostname"], system["osrelease"] =
string.match(uname, "([%w]+)[%s]([%w%p]+)[%s]([%w%p]+)")
end
-- Linux manual page: random(4)
if kernel.random then
-- Linux 2.6 default entropy pool is 4096-bits
local poolsize = tonumber(kernel.random.poolsize)
-- Get available entropy and calculate percentage
system["entropy"] = tonumber(kernel.random.entropy_avail)
system["entropy_p"] = math.ceil(system["entropy"] * 100 / poolsize)
end
-- Get user from the environment
system["username"] = os.getenv("USER")
return {system["ostype"], system["osrelease"], system["username"],
system["hostname"], system["entropy"], system["entropy_p"]}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,46 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local math = { max = math.max }
local setmetatable = setmetatable
-- }}}
-- Pkg: provides number of pending updates on UNIX systems
module("vicious.widgets.pkg")
-- {{{ Packages widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local updates = 0
local manager = {
["Arch"] = { cmd = "pacman -Qu" },
["Arch S"] = { cmd = "yes | pacman -Sup", sub = 2 },
["Debian"] = { cmd = "apt-show-versions -u -b" },
["Ubuntu"] = { cmd = "aptitude search '~U'" },
["Fedora"] = { cmd = "yum list updates", sub = 3 },
["FreeBSD"] ={ cmd = "pkg_version -I -l '<'" },
["Mandriva"]={ cmd = "urpmq --auto-select" }
}
-- Check if updates are available
local pkg = manager[warg]
local f = io.popen(pkg.cmd)
for line in f:lines() do
updates = updates + 1
end
f:close()
return {pkg.sub and math.max(updates-pkg.sub, 0) or updates}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,57 @@
-----------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Hagen Schink <troja84@googlemail.com>
-----------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = {
len = string.len,
sub = string.sub,
match = string.match,
gmatch = string.gmatch
}
-- }}}
-- Raid: provides state information for a requested RAID array
module("vicious.widgets.raid")
-- Initialize function tables
local mddev = {}
-- {{{ RAID widget type
local function worker(format, warg)
if not warg then return end
mddev[warg] = {
["found"] = false,
["active"] = 0,
["assigned"] = 0
}
-- Linux manual page: md(4)
for line in io.lines("/proc/mdstat") do
if mddev[warg]["found"] then
local updev = string.match(line, "%[[_U]+%]")
for i in string.gmatch(updev, "U") do
mddev[warg]["active"] = mddev[warg]["active"] + 1
end
break
elseif string.sub(line, 1, string.len(warg)) == warg then
mddev[warg]["found"] = true
for i in string.gmatch(line, "%[[%d]%]") do
mddev[warg]["assigned"] = mddev[warg]["assigned"] + 1
end
end
end
return {mddev[warg]["assigned"], mddev[warg]["active"]}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,45 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Thermal: provides temperature levels of ACPI and coretemp thermal zones
module("vicious.widgets.thermal")
-- {{{ Thermal widget type
local function worker(format, warg)
if not warg then return end
local zone = { -- Known temperature data sources
["sys"] = {"/sys/class/thermal/", file = "temp", div = 1000},
["core"] = {"/sys/devices/platform/", file = "temp1_input",div = 1000},
["proc"] = {"/proc/acpi/thermal_zone/",file = "temperature"}
} -- Default to /sys/class/thermal
warg = type(warg) == "table" and warg or { warg, "sys" }
-- Get temperature from thermal zone
local thermal = helpers.pathtotable(zone[warg[2]][1] .. warg[1])
if thermal[zone[warg[2]].file] then
if zone[warg[2]].div then
return {thermal[zone[warg[2]].file] / zone[warg[2]].div}
else -- /proc/acpi "temperature: N C"
return {tonumber(string.match(thermal[zone[warg[2]].file], "[%d]+"))}
end
end
return {0}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,35 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Uptime: provides system uptime and load information
module("vicious.widgets.uptime")
-- {{{ Uptime widget type
local function worker(format)
local proc = helpers.pathtotable("/proc")
-- Get system uptime
local up_t = math.floor(string.match(proc.uptime, "[%d]+"))
local up_d = math.floor(up_t / (3600 * 24))
local up_h = math.floor((up_t % (3600 * 24)) / 3600)
local up_m = math.floor(((up_t % (3600 * 24)) % 3600) / 60)
local l1, l5, l15 = -- Get load averages for past 1, 5 and 15 minutes
string.match(proc.loadavg, "([%d%.]+)[%s]([%d%.]+)[%s]([%d%.]+)")
return {up_d, up_h, up_m, l1, l5, l15}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
-- }}}
-- Volume: provides volume levels and state of requested ALSA mixers
module("vicious.widgets.volume")
-- {{{ Volume widget type
local function worker(format, warg)
if not warg then return end
local mixer_state = {
["on"] = "", -- "",
["off"] = "" -- "M"
}
-- Get mixer control contents
local f = io.popen("amixer get " .. warg)
local mixer = f:read("*all")
f:close()
-- Capture mixer control state: [5%] ... ... [on]
local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
-- Handle mixers without data
if volu == nil then
return {0, mixer_state["off"]}
end
-- Handle mixers without mute
if mute == "" and volu == "0"
-- Handle mixers that are muted
or mute == "off" then
mute = mixer_state["off"]
else
mute = mixer_state["on"]
end
return {tonumber(volu), mute}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local math = { ceil = math.ceil }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Weather: provides weather information for a requested station
module("vicious.widgets.weather")
-- Initialize function tables
local weather = {
["{city}"] = "N/A",
["{wind}"] = "N/A",
["{windmph}"] = "N/A",
["{windkmh}"] = "N/A",
["{sky}"] = "N/A",
["{weather}"] = "N/A",
["{tempf}"] = "N/A",
["{tempc}"] = "N/A",
["{humid}"] = "N/A",
["{press}"] = "N/A"
}
-- {{{ Weather widget type
local function worker(format, warg)
if not warg then return end
-- Get weather forceast by the station ICAO code, from:
-- * US National Oceanic and Atmospheric Administration
local noaa = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"
local f = io.popen("curl --connect-timeout 1 -fsm 3 "..noaa..warg..".TXT")
local ws = f:read("*all")
f:close()
-- Check if there was a timeout or a problem with the station
if ws == nil then return weather end
weather["{city}"] = -- City and/or area
string.match(ws, "^(.+)%,.*%([%u]+%)") or weather["{city}"]
weather["{wind}"] = -- Wind direction and degrees if available
string.match(ws, "Wind:[%s][%a]+[%s][%a]+[%s](.+)[%s]at.+$") or weather["{wind}"]
weather["{windmph}"] = -- Wind speed in MPH if available
string.match(ws, "Wind:[%s].+[%s]at[%s]([%d]+)[%s]MPH") or weather["{windmph}"]
weather["{sky}"] = -- Sky conditions if available
string.match(ws, "Sky[%s]conditions:[%s](.-)[%c]") or weather["{sky}"]
weather["{weather}"] = -- Weather conditions if available
string.match(ws, "Weather:[%s](.-)[%c]") or weather["{weather}"]
weather["{tempf}"] = -- Temperature in fahrenheit
string.match(ws, "Temperature:[%s]([%-]?[%d%.]+).*[%c]") or weather["{tempf}"]
weather["{humid}"] = -- Relative humidity in percent
string.match(ws, "Relative[%s]Humidity:[%s]([%d]+)%%") or weather["{humid}"]
weather["{press}"] = -- Pressure in hPa
string.match(ws, "Pressure[%s].+%((.+)[%s]hPa%)") or weather["{press}"]
-- Wind speed in km/h if MPH was available
if weather["{windmph}"] ~= "N/A" then
weather["{windmph}"] = tonumber(weather["{windmph}"])
weather["{windkmh}"] = math.ceil(weather["{windmph}"] * 1.6)
end -- Temperature in °C if °F was available
if weather["{tempf}"] ~= "N/A" then
weather["{tempf}"] = tonumber(weather["{tempf}"])
weather["{tempc}"] = math.ceil((weather["{tempf}"] - 32) * 5/9)
end -- Capitalize some stats so they don't look so out of place
if weather["{sky}"] ~= "N/A" then
weather["{sky}"] = helpers.capitalize(weather["{sky}"])
end
if weather["{weather}"] ~= "N/A" then
weather["{weather}"] = helpers.capitalize(weather["{weather}"])
end
return weather
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,80 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local math = { ceil = math.ceil }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local io = {
open = io.open,
popen = io.popen
}
local string = {
find = string.find,
match = string.match
}
-- }}}
-- Wifi: provides wireless information for a requested interface
module("vicious.widgets.wifi")
-- {{{ Wireless widget type
local function worker(format, warg)
if not warg then return end
-- Default values
local winfo = {
["{ssid}"] = "N/A",
["{mode}"] = "N/A",
["{chan}"] = 0,
["{rate}"] = 0,
["{link}"] = 0,
["{linp}"] = 0,
["{sign}"] = 0
}
-- Get data from iwconfig where available
local iwconfig = "/sbin/iwconfig"
local f = io.open(iwconfig, "rb")
if not f then
iwconfig = "/usr/sbin/iwconfig"
else
f:close()
end
local f = io.popen(iwconfig .." ".. warg .. " 2>&1")
local iw = f:read("*all")
f:close()
-- iwconfig wasn't found, isn't executable, or non-wireless interface
if iw == nil or string.find(iw, "No such device") then
return winfo
end
-- Output differs from system to system, some stats can be
-- separated by =, and not all drivers report all stats
winfo["{ssid}"] = -- SSID can have almost anything in it
helpers.escape(string.match(iw, 'ESSID[=:]"(.-)"') or winfo["{ssid}"])
winfo["{mode}"] = -- Modes are simple, but also match the "-" in Ad-Hoc
string.match(iw, "Mode[=:]([%w%-]*)") or winfo["{mode}"]
winfo["{chan}"] = -- Channels are plain digits
tonumber(string.match(iw, "Channel[=:]([%d]+)") or winfo["{chan}"])
winfo["{rate}"] = -- Bitrate can start with a space, we don't want to display Mb/s
tonumber(string.match(iw, "Bit Rate[=:]([%s]?[%d%.]*)") or winfo["{rate}"])
winfo["{link}"] = -- Link quality can contain a slash (32/70), match only the first number
tonumber(string.match(iw, "Link Quality[=:]([%d]+)") or winfo["{link}"])
winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation
tonumber(string.match(iw, "Signal level[=:]([%-]?[%d]+)") or winfo["{sign}"])
-- Link quality percentage if quality was available
if winfo["{link}"] ~= 0 then winfo["{linp}"] = math.ceil(winfo["{link}"] / 0.7) end
return winfo
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })