123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- local awful = require("awful")
- local watch = require("awful.widget.watch")
- local wibox = require("wibox")
- local beautiful = require("beautiful")
- local gears = require("gears")
- local CMD = [[sh -c "grep '^cpu.' /proc/stat; ps -eo 'pid:10,pcpu:5,pmem:5,comm:30,cmd' --sort=-pcpu ]]
- .. [[| grep -v [p]s | grep -v [g]rep | head -11 | tail -n +2"]]
- -- A smaller command, less resource intensive, used when popup is not shown.
- local CMD_slim = [[grep --max-count=1 '^cpu.' /proc/stat]]
- local HOME = os.getenv("HOME")
- local TERMINAL = os.getenv("TERMINAL") or "alacritty"
- local SHELL = os.getenv("SHELL") or "/usr/bin/fish"
- local WIDGET_DIR = HOME.."/.config/awesome/awesome-wm-widgets/cpu-widget"
- local cpu_widget_icon = {}
- local cpu_rows = {
- spacing = 4,
- layout = wibox.layout.fixed.vertical,
- }
- local is_update = true
- local process_rows = {
- layout = wibox.layout.fixed.vertical,
- }
- -- Remove spaces at end and beggining of a string
- local function trim(s)
- return (s:gsub("^%s*(.-)%s*$", "%1"))
- end
- -- Checks if a string starts with an another string
- local function starts_with(str, start)
- return str:sub(1, #start) == start
- end
- local function create_textbox(args)
- return wibox.widget {
- text = args.text,
- align = args.align or 'left',
- markup = args.markup,
- forced_width = args.forced_width or 40,
- widget = wibox.widget.textbox
- }
- end
- local function create_process_header(params)
- local res = wibox.widget{
- create_textbox{markup = '<b>PID</b>'},
- create_textbox{markup = '<b>Name</b>'},
- {
- create_textbox{markup = '<b>%CPU</b>'},
- create_textbox{markup = '<b>%MEM</b>'},
- params.with_action_column and create_textbox{forced_width = 20} or nil,
- layout = wibox.layout.align.horizontal
- },
- layout = wibox.layout.ratio.horizontal
- }
- res:ajust_ratio(2, 0.2, 0.47, 0.33)
- return res
- end
- local function create_kill_process_button()
- return wibox.widget{
- {
- id = "icon",
- image = WIDGET_DIR .. '/window-close-symbolic.svg',
- resize = false,
- opacity = 0.1,
- widget = wibox.widget.imagebox
- },
- widget = wibox.container.background
- }
- end
- local function worker(user_args)
- local args = user_args or {}
- local width = args.width or 50
- local step_width = args.step_width or 2
- local step_spacing = args.step_spacing or 1
- local color = args.color or beautiful.fg_normal
- local background_color = args.background_color or "#00000000"
- local enable_kill_button = args.enable_kill_button or false
- local process_info_max_length = args.process_info_max_length or -1
- local timeout = args.timeout or 1
- local cpu_widget_icon = wibox.widget {
- {
- id = "txt_icon",
- text = " ",
- font = args.font,
- widget = wibox.widget.textbox,
- },
- valign = "center",
- layout = wibox.layout.align.horizontal,
- -- layout = wibox.container.place,
- }
- -- This timer periodically executes the heavy command while the popup is open.
- -- It is stopped when the popup is closed and only the slim command is run then.
- -- This greatly improves performance while the popup is closed at the small cost
- -- of a slightly longer popup opening time.
- local popup_timer = gears.timer {
- timeout = timeout
- }
- local popup = awful.popup{
- ontop = true,
- visible = false,
- shape = gears.shape.rounded_rect,
- border_width = 1,
- border_color = beautiful.bg_normal,
- maximum_width = 300,
- offset = { y = 5 },
- widget = {}
- }
- popup:buttons(
- awful.util.table.join(
- awful.button({}, 3, function()
- popup.visible = not popup.visible
- end)
- )
- )
- -- Do not update process rows when mouse cursor is over the widget
- popup:connect_signal("mouse::enter", function() is_update = false end)
- popup:connect_signal("mouse::leave", function() is_update = true end)
- cpu_widget_icon:buttons(
- awful.util.table.join(
- awful.button({}, 1, function()
- if popup.visible then
- popup.visible = not popup.visible
- -- When the popup is not visible, stop the timer
- popup_timer:stop()
- else
- popup:move_next_to(mouse.current_widget_geometry)
- -- Restart the timer, when the popup becomes visible
- -- Emit the signal to start the timer directly and not wait the timeout first
- popup_timer:start()
- popup_timer:emit_signal("timeout")
- end
- end),
- awful.button({}, 3, function()
- awful.spawn(TERMINAL.." -e "..SHELL.." -c htop")
- end)
- )
- )
- -- -- This part runs constantly, also when the popup is closed.
- -- -- It updates the graph widget in the bar.
- -- local maincpu = {}
- -- watch(CMD_slim, timeout, function(widget, stdout)
- --
- -- local _, user, nice, system, idle, iowait, irq, softirq, steal, _, _ =
- -- stdout:match('(%w+)%s+(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)')
- --
- -- local total = user + nice + system + idle + iowait + irq + softirq + steal
- --
- -- local diff_idle = idle - tonumber(maincpu['idle_prev'] == nil and 0 or maincpu['idle_prev'])
- -- local diff_total = total - tonumber(maincpu['total_prev'] == nil and 0 or maincpu['total_prev'])
- -- local diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10
- --
- -- maincpu['total_prev'] = total
- -- maincpu['idle_prev'] = idle
- --
- -- -- widget:add_value(diff_usage)
- -- -- local t = user + nice + system + irq +softirq
- -- -- local cpu_usage_perc = 100 * t / (t + idle)
- -- -- cpu_widget_icon.set("2.6 GHz", cpu_usage_perc)
- --
- -- cpu_widget_icon.set("2.6 GHz", string.format(" %.0f%%", diff_usage))
- -- end,
- -- cpu_widget_icon
- -- )
- -- local maincpu = {}
- -- watch(CMD_slim, timeout, function(widget, stdout)
- --
- -- local _, user, nice, system, idle, iowait, irq, softirq, steal, _, _ =
- -- stdout:match('(%w+)%s+(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)')
- --
- -- local total = user + nice + system + idle + iowait + irq + softirq + steal
- --
- -- local diff_idle = idle - tonumber(maincpu['idle_prev'] == nil and 0 or maincpu['idle_prev'])
- -- local diff_total = total - tonumber(maincpu['total_prev'] == nil and 0 or maincpu['total_prev'])
- -- local diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10
- --
- -- maincpu['total_prev'] = total
- -- maincpu['idle_prev'] = idle
- --
- -- widget:add_value(diff_usage)
- -- end,
- -- cpugraph_widget
- -- )
- -- This part runs whenever the timer is fired.
- -- It therefore only runs when the popup is open.
- local cpus = {}
- popup_timer:connect_signal('timeout', function()
- awful.spawn.easy_async(CMD, function(stdout, _, _, _)
- local i = 1
- local j = 1
- for line in stdout:gmatch("[^\r\n]+") do
- if starts_with(line, 'cpu') then
- if cpus[i] == nil then cpus[i] = {} end
- local name, user, nice, system, idle, iowait, irq, softirq, steal, _, _ =
- line:match('(%w+)%s+(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)%s(%d+)')
- local total = user + nice + system + idle + iowait + irq + softirq + steal
- local diff_idle = idle - tonumber(cpus[i]['idle_prev'] == nil and 0 or cpus[i]['idle_prev'])
- local diff_total = total - tonumber(cpus[i]['total_prev'] == nil and 0 or cpus[i]['total_prev'])
- local diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10
- cpus[i]['total_prev'] = total
- cpus[i]['idle_prev'] = idle
- local row = wibox.widget
- {
- create_textbox{text = name},
- create_textbox{text = math.floor(diff_usage) .. '%'},
- {
- max_value = 100,
- value = diff_usage,
- forced_height = 20,
- forced_width = 150,
- paddings = 1,
- margins = 4,
- border_width = 1,
- border_color = beautiful.bg_focus,
- background_color = beautiful.bg_normal,
- bar_border_width = 1,
- bar_border_color = beautiful.bg_focus,
- color = "linear:150,0:0,0:0,#D08770:0.3,#BF616A:0.6," .. beautiful.fg_normal,
- widget = wibox.widget.progressbar,
- },
- layout = wibox.layout.ratio.horizontal
- }
- row:ajust_ratio(2, 0.15, 0.15, 0.7)
- cpu_rows[i] = row
- i = i + 1
- else
- if is_update == true then
- local pid = trim(string.sub(line, 1, 10))
- local cpu = trim(string.sub(line, 12, 16))
- local mem = trim(string.sub(line, 18, 22))
- local comm = trim(string.sub(line, 24, 53))
- local cmd = trim(string.sub(line, 54))
- local kill_proccess_button = enable_kill_button and create_kill_process_button() or nil
- local pid_name_rest = wibox.widget{
- create_textbox{text = pid},
- create_textbox{text = comm},
- {
- create_textbox{text = cpu, align = 'center'},
- create_textbox{text = mem, align = 'center'},
- kill_proccess_button,
- layout = wibox.layout.fixed.horizontal
- },
- layout = wibox.layout.ratio.horizontal
- }
- pid_name_rest:ajust_ratio(2, 0.2, 0.47, 0.33)
- local row = wibox.widget {
- {
- pid_name_rest,
- top = 4,
- bottom = 4,
- widget = wibox.container.margin
- },
- widget = wibox.container.background
- }
- row:connect_signal("mouse::enter", function(c) c:set_bg(beautiful.bg_focus) end)
- row:connect_signal("mouse::leave", function(c) c:set_bg(beautiful.bg_normal) end)
- if enable_kill_button then
- row:connect_signal("mouse::enter", function() kill_proccess_button.icon.opacity = 1 end)
- row:connect_signal("mouse::leave", function() kill_proccess_button.icon.opacity = 0.1 end)
- kill_proccess_button:buttons(
- awful.util.table.join( awful.button({}, 1, function()
- row:set_bg('#ff0000')
- awful.spawn.with_shell('kill -9 ' .. pid)
- end) ) )
- end
- awful.tooltip {
- objects = { row },
- mode = 'outside',
- preferred_positions = {'bottom'},
- timer_function = function()
- local text = cmd
- if process_info_max_length > 0 and text:len() > process_info_max_length then
- text = text:sub(0, process_info_max_length - 3) .. '...'
- end
- return text
- :gsub('%s%-', '\n\t-') -- put arguments on a new line
- :gsub(':/', '\n\t\t:/') -- java classpath uses : to separate jars
- end,
- }
- process_rows[j] = row
- j = j + 1
- end
- end
- end
- popup:setup {
- {
- cpu_rows,
- {
- orientation = 'horizontal',
- forced_height = 15,
- color = beautiful.bg_focus,
- widget = wibox.widget.separator
- },
- create_process_header{with_action_column = enable_kill_button},
- process_rows,
- layout = wibox.layout.fixed.vertical,
- },
- margins = 8,
- widget = wibox.container.margin
- }
- end)
- end)
- -- Set fg and bg colors for ram_widget_icon
- local cpu_widget_icon_clr = wibox.widget.background()
- cpu_widget_icon_clr:set_widget(cpu_widget_icon)
- cpu_widget_icon_clr:set_fg("#89ddff")
- return cpu_widget_icon_clr
- end
- return setmetatable(cpu_widget_icon, { __call = function(_, ...) return worker(...) end })
|