Skip to content

Commit

Permalink
manage cpu widget better
Browse files Browse the repository at this point in the history
  • Loading branch information
aarondill committed Nov 20, 2023
1 parent 22b7081 commit d5b277b
Showing 1 changed file with 51 additions and 37 deletions.
88 changes: 51 additions & 37 deletions widget/cpu/init.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
local gstring = require("gears.string")
local gtimer = require("gears.timer")
local icons = require("theme.icons")
local mat_icon = require("widget.material.icon")
local read_async = require("util.file.read_async")
local wibox = require("wibox")
local dpi = require("beautiful").xresources.apply_dpi
local bind = require("util.bind")

-- All special characters escaped in a string: %%, %^, %$, ...
local patternchars = table.concat({ "[", ("%^$().[]*+-?"):gsub("(.)", "%%%1"), "]" })
Expand All @@ -22,36 +24,52 @@ end
---How often to update the widget in seconds (default: 15).
---@field timeout integer?

---@return integer cpu_idle_time
---@return integer cpu_total
---@class proc_stat_info
---@field user integer?
---@field nice integer?
---@field system integer?
---@field idle integer?
---@field iowait integer?
---@field irq integer?
---@field softirq integer?
---@field steal integer?

---@param content string
---@return proc_stat_info
local function parse_proc_stat(content)
local rem = content:match("^cpu%s+([^\n]*)")
if not rem then return 0, 0 end
local user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice
-- find values, even if not exist
user, rem = rem:match("(%d+)(.*)")
nice, rem = rem:match("(%d+)(.*)")
system, rem = rem:match("(%d+)(.*)")
idle, rem = rem:match("(%d+)(.*)")
iowait, rem = rem:match("(%d+)(.*)")
irq, rem = rem:match("(%d+)(.*)")
softirq, rem = rem:match("(%d+)(.*)")
steal, rem = rem:match("(%d+)(.*)")
guest, rem = rem:match("(%d+)(.*)")
guest_nice, rem = rem:match("(%d+)(.*)")
local ret = {} ---@type proc_stat_info
if not rem then return ret end
local vals = gstring.split(rem, " ")
for i, name in ipairs({ "user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal" }) do
ret[name] = tonumber(vals[i]) -- Generate ret from the list of header names
end
return ret
end

local cpu_total = user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice
-- return user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, cpu_total
return idle, cpu_total
---@param proc_info proc_stat_info
---@return { total: integer, idle: integer }
local function get_cpu_time(proc_info)
local user, nice, system, idle, iowait, irq, softirq, steal =
proc_info.user or 0,
proc_info.nice or 0,
proc_info.system or 0,
proc_info.idle or 0,
proc_info.iowait or 0,
proc_info.irq or 0,
proc_info.softirq or 0,
proc_info.steal or 0
local total_idle = idle + iowait
local total_nonidle = user + nice + system + irq + softirq + steal
local total = total_idle + total_nonidle
return { total = total, idle = total_idle }
end

---Create a new CPU usage widget
---@param args CPUWidgetConfig?
---@return table
function CPU(args)
args = args or {}
local cpu_total_prev = 0
local idle_prev = 0

local format = string.format(
"%s%%.%df%s",
Expand All @@ -64,35 +82,31 @@ function CPU(args)
widget = wibox.widget.textbox,
text = string.format(format, 0),
})
local prev = { total = 0, idle = 0 }

local file_callback = function(content, _)
if not content then return end
local idle, cpu_total = parse_proc_stat(content)
---@param content string
local file_callback = function(content, _err, _path)
--- See: http://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux
if not content or content == "" then return end
local proc_info = parse_proc_stat(content)
local time = get_cpu_time(proc_info)

-- Get the delta between two reads
local diff_cpu = cpu_total - cpu_total_prev
-- Get the idle time delta
local diff_idle = idle - idle_prev
-- Calc time spent working
local cpu_used = diff_cpu - diff_idle
-- Calc percentage
local cpu_usage = 100 * cpu_used / diff_cpu
local cpu_usage = ((time.total - prev.total) - (time.idle - prev.idle)) / (time.total - prev.total) * 100
cpu_usage = cpu_usage ~= cpu_usage and -1 or cpu_usage -- NaN is -1
cpu_usage = cpu_usage == math.huge and -1 or cpu_usage -- Inf is -1
cpu_usage = cpu_usage == -math.huge and -1 or cpu_usage -- -Inf is -1

-- Round to percentage
text_box:set_text(string.format(format, cpu_usage))

cpu_total_prev = cpu_total
idle_prev = idle
prev = time
collectgarbage("collect")
end

gtimer.new({
autostart = true,
call_now = true,
timeout = args.timeout or 15,
callback = function()
read_async("/proc/stat", file_callback)
end,
callback = bind.with_args(read_async, "/proc/stat", file_callback),
})

local cpu_meter = wibox.widget({
Expand Down

0 comments on commit d5b277b

Please sign in to comment.