diff --git a/pacman-widget/README.md b/pacman-widget/README.md new file mode 100644 index 0000000..f9c4325 --- /dev/null +++ b/pacman-widget/README.md @@ -0,0 +1,37 @@ +# Pacman widget for AwesomeWM + +This widget displays the number of upgradable Pacman packages. Clicking the icon reveals a scrollable list of available upgrades. A full system upgrade can be performed from the widget via Polkit. + +![](screenshots/pacman.gif) + +## Requirements +`lxpolkit` is the default [Polkit agent](https://wiki.archlinux.org/title/Polkit). + +The widget also uses the `checkupdates` script from the `pacman-contrib` package. + + +## Installation + +Clone the repo under **~/.config/awesome/** and add the following to **rc.lua**: + +```lua +local pacman_widget = require('pacman-widget.pacman') +... +s.mytasklist, -- Middle widget + { -- Right widgets + layout = wibox.layout.fixed.horizontal, + ... + -- default + pacman_widget(), + -- custom (shown with defaults) + pacman_widget { + interval = 600, -- Refresh every 10 minutes + popup_bg_color = '#222222', + popup_border_width = 1, + popup_border_color = '#7e7e7e', + popup_height = 10, -- 10 packages shown in scrollable window + popup_width = 300, + polkit_agent_path = '/usr/bin/lxpolkit' + }, +``` + diff --git a/pacman-widget/icons/pacman-full.svg b/pacman-widget/icons/pacman-full.svg new file mode 100644 index 0000000..50bb939 --- /dev/null +++ b/pacman-widget/icons/pacman-full.svg @@ -0,0 +1,86 @@ + + + + + + + + Gnome Symbolic Icon Theme + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + diff --git a/pacman-widget/icons/pacman.svg b/pacman-widget/icons/pacman.svg new file mode 100644 index 0000000..9c1cb2c --- /dev/null +++ b/pacman-widget/icons/pacman.svg @@ -0,0 +1,82 @@ + + + + + + + + Gnome Symbolic Icon Theme + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + + + + + + diff --git a/pacman-widget/icons/upgrade.svg b/pacman-widget/icons/upgrade.svg new file mode 100644 index 0000000..0a556ae --- /dev/null +++ b/pacman-widget/icons/upgrade.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + diff --git a/pacman-widget/pacman.lua b/pacman-widget/pacman.lua new file mode 100644 index 0000000..a547f6a --- /dev/null +++ b/pacman-widget/pacman.lua @@ -0,0 +1,254 @@ +local naughty = require("naughty") +local wibox = require("wibox") +local awful = require("awful") +local beautiful = require("beautiful") +local gears = require("gears") + +local DIR = os.getenv("HOME") .. "/.config/awesome/pacman-widget/" +local ICON_DIR = DIR .. "icons/" + +local pacman_widget = {} +local config = {} + +config.interval = 600 +config.popup_bg_color = "#222222" +config.popup_border_width = 1 +config.popup_border_color = "#7e7e7e" +config.popup_height = 10 +config.popup_width = 300 +config.polkit_agent_path = "/usr/bin/lxpolkit" + +local function worker(user_args) + local args, _config = user_args or {}, {} + for prop, value in pairs(config) do + _config[prop] = args[prop] or beautiful[prop] or value + end + + awful.spawn.once(_config.polkit_agent_path) + + pacman_widget = wibox.widget { + { + { + id = "icon", + resize = false, + widget = wibox.widget.imagebox, + }, + valign = "center", + layout = wibox.container.place, + }, + { + id = "txt", + font = font, + widget = wibox.widget.textbox + }, + spacing = 5, + layout = wibox.layout.fixed.horizontal, + } + function pacman_widget:set(new_value) + pacman_widget:get_children_by_id("txt")[1]:set_text(new_value) + pacman_widget:get_children_by_id("icon")[1]:set_image( + ICON_DIR .. (tonumber(new_value) > 0 and "pacman" or "pacman-full") .. ".svg" + ) + end + + local rows, ptr = wibox.layout.fixed.vertical(), 0 + rows:connect_signal("button::press", function(_,_,_,button) + if button == 4 then + if ptr > 0 then + rows.children[ptr].visible = true + ptr = ptr - 1 + end + elseif button == 5 then + if ptr < #rows.children and ((#rows.children - ptr) > _config.popup_height) then + ptr = ptr + 1 + rows.children[ptr].visible = false + end + end + end) + + local popup = awful.popup { + border_width = _config.popup_border_width, + border_color = _config.popup_border_color, + shape = gears.shape.rounded_rect, + visible = false, + ontop = true, + offset = { y = 5 }, + widget = {} + } + + pacman_widget:buttons( + awful.util.table.join( + awful.button({}, 1, function() + if popup.visible then + popup.visible = false + else + popup.visible = true + popup:move_next_to(mouse.current_widget_geometry) + end + end) + ) + ) + + local upgr_opacity = 0.6 + local upgr_btn = wibox.widget { + { + image = ICON_DIR .. "upgrade.svg", + resize = false, + layout = wibox.widget.imagebox + }, + opacity = upgr_opacity, + layout = wibox.container.background + } + + local old_cursor, old_wibox + local busy, upgrading = false, false + upgr_btn:connect_signal("mouse::enter", function(c) + if not busy then + c:set_opacity(1) + c:emit_signal("widget::redraw_needed") + local wb = mouse.current_wibox + old_cursor, old_wibox = wb.cursor, wb + wb.cursor = "hand2" + end + end) + upgr_btn:connect_signal("mouse::leave", function(c) + if not busy then + c:set_opacity(upgr_opacity) + c:emit_signal("widget::redraw_needed") + if old_wibox then + old_wibox.cursor = old_cursor + old_wibox = nil + end + end + end) + upgr_btn:connect_signal("button::press", function(c) + c:set_opacity(1) + c:emit_signal("widget::redraw_needed") + if old_wibox then + old_wibox.cursor = old_cursor + old_wibox = nil + end + if not busy then + busy, one_shot = true, true + awful.spawn.with_line_callback("bash -c " .. DIR .. "upgrade", { + stdout = function() + if one_shot then + upgrading, one_shot = true, false + timer:emit_signal("timeout") + end + end, + stderr = function(line) + if (line ~= nil and line ~= "") then + notification = string.find(line, "warning") and + naughty.notify({ + title = "Warning!", + text = line, + timeout = 0 + }) + or + naughty.notify({ + preset = naughty.config.presets.critical, + title = "Error!", + text = line, + }) + end + end, + exit = function() + upgrading, busy = false, false + c:set_opacity(upgr_opacity) + c:emit_signal("widget::redraw_needed") + timer:emit_signal("timeout") + end, + }) + end + end) + + _, timer = awful.widget.watch([[bash -c "checkupdates 2>/dev/null"]], + _config.interval, + function(widget, stdout) + local upgrades_tbl = {} + for value in stdout:gmatch("([^\n]+)") do + upgrades_tbl[#upgrades_tbl+1] = value + end + widget:set(#upgrades_tbl) + + local popup_header_height, popup_row_height = 30, 20 + local header = wibox.widget { + { + nil, + { + markup = "" .. (upgrading and "Upgrading " .. #upgrades_tbl .. " Packages" or + (#upgrades_tbl == 0 and "No" or #upgrades_tbl) .. " Available Upgrades") .. "", + layout = wibox.widget.textbox, + }, + #upgrades_tbl > 0 and { + upgr_btn, + valign = "center", + layout = wibox.container.place, + }, + expand = "none", + layout = wibox.layout.align.horizontal, + }, + forced_height = popup_header_height, + left = 20, + right = 20, + layout = wibox.container.margin + } + + for k, v in ipairs(upgrades_tbl) do + for i = 1, #rows.children do + if v == rows.children[i]:get_txt() then goto continue end + end + local row = wibox.widget{ + { + id = "idx", + text = tostring(k), + widget = wibox.widget.textbox + }, + { + id = "txt", + text = v, + forced_height = popup_row_height, + paddings = 1, + widget = wibox.widget.textbox + }, + layout = wibox.layout.ratio.horizontal, + } + function row:get_txt() return row:get_children_by_id("txt")[1].text end + function row:set_idx(idx) row:get_children_by_id("idx")[1]:set_text(idx) end + row:ajust_ratio(2, 0.1, 0.9, 0) + rows:insert(k, row) + ::continue:: + end + + local height = popup_header_height + math.min(#upgrades_tbl, _config.popup_height) * popup_row_height + popup:setup { + { + { + { + { + header, + rows, + forced_height = height, + layout = wibox.layout.fixed.vertical + }, + content_fill_horizontal = true, + layout = wibox.container.place + }, + margins = 10, + layout = wibox.container.margin + }, + bg = _config.popup_bg_color, + layout = wibox.widget.background + }, + forced_width = _config.popup_width, + layout = wibox.layout.fixed.horizontal + } + end, + pacman_widget + ) + return pacman_widget +end + +return setmetatable(pacman_widget, { __call = function(_, ...) return worker(...) end }) + diff --git a/pacman-widget/screenshots/pacman.gif b/pacman-widget/screenshots/pacman.gif new file mode 100644 index 0000000..1c26bcc Binary files /dev/null and b/pacman-widget/screenshots/pacman.gif differ diff --git a/pacman-widget/upgrade b/pacman-widget/upgrade new file mode 100755 index 0000000..af2f9f7 --- /dev/null +++ b/pacman-widget/upgrade @@ -0,0 +1,2 @@ +#!/bin/bash +pkexec --disable-internal-agent pacman -Syu --noconfirm